Export (0) Print
Expand All

2.5.3.2 Access Check Algorithm Pseudocode

In overview, the Access Check algorithm takes an access request and a security descriptor. It iterates through the DACL of the security descriptor, processing each ACE. If the ACE contains a SID that is also in the Token authorization context, then the ACE is processed, otherwise it is skipped. If an ACE grants access to that SID, then those access rights from the Access Request Mask are considered satisfied, and removed from the mask. If the ACE denies access to that SID, and the access rights in the ACE are present in the request mask, the whole request is denied. At the end of the algorithm, if there are any access rights still pending in the Access Request Mask, then the request is considered denied.

There are two noteworthy configurations of the security descriptor in light of the access check algorithm: an empty DACL, and a NULL (or absent) DACL. No DACL in the security descriptor implies that there is no policy in place to govern access to the object; any access check will succeed. An empty DACL, where the DACL is marked as being present but contains no ACEs, means that no principal should gain access to the object, except through the implied access of the owner.

If the access request is MAXIMUM_ALLOWED, the algorithm operates in a different mode. It iterates through every ACE in the DACL of the security descriptor, remembering which access rights were granted or denied for each ACE. After all ACEs have been examined, the complete set of grantable access rights is computed and returned via the GrantedAccess parameter (described later in this section).

Note that the use of MAXIMUM_ALLOWED is not recommended; instead, callers should request the specific minimum level of access required to accomplish their requirements.

The detailed processing of the list is as follows.

On entrance:

  • SecurityDescriptor: SECURITY_DESCRIPTOR structure that is assigned to the object.

  • Token: Authorization context as described above.

  • Access Request mask: Set of permissions requested on the object.

  • Object Tree: An array of OBJECT_TYPE_LIST structures representing a hierarchy of objects for which to check access. Each node represents an object with three values: A GUID that represents the object itself; a value called Remaining, which can be zero, and which specifies the user rights requests for that node that have not yet been satisfied; and a value called Level, which indicates the level of the object type in the hierarchy.

  • PrincipalSelfSubst SID: A SID that logically replaces the SID in any ACE that contains the well-known PRINCIPAL_SELF SID. It can be null.

  • GrantedAccess: An optional ACCESS_MASK output parameter used when the Access Request Mask parameter equals MAXIMUM_ALLOWED. Upon return this parameter contains the set of permissions granted to Token by the SecurityDescriptor.

STATUS_CODE
EvaluateTokenAgainstDescriptor(
    TOKEN Token,
    SECURITY_DESCRIPTOR SecurityDescriptor,
    ACCESS_MASK Access_Request_mask,
    OBJECT_TYPE_LIST Object Tree,
    Sid PrincipalSelfSubstitute,
    [out] ACCESS_MASK GrantedAccess)

    Dim OBJECT_TYPE_LIST LocalTree
    Dim ULONG Result

    Set DACL to SecurityDescriptor Dacl field
    Set SACL to SecurityDescriptor Sacl field
    Set RemainingAccess to Access Request mask
    Set AllowedAccesses to 0
    Set DeniedAccesses to 0
    Set MaxAllowedMode to FALSE

    IF RemainingAccess contains ACCESS_SYSTEM_SECURITY access bit THEN
        IF Token.Privileges contains SeSecurityPrivilege THEN
            Remove ACCESS_SYSTEM_SECURITY access bit from RemainingAccess
        END IF
    END IF

    IF RemainingAccess contains WRITE_OWNER access bit THEN
        IF  Token.Privileges contains SeTakeOwnershipPrivilege THEN
          Remove WRITE_OWNER access bit from RemainingAccess
       END IF
    END IF

    -- the owner of an object is always granted READ_CONTROL and WRITE_DAC.
    CALL SidInToken(Token, SecurityDescriptor.Owner, PrincipalSelfSubst)
    IF SidInToken returns True THEN
        IF DACL does not contain ACEs from object owner THEN
            Remove READ_CONTROL and WRITE_DAC from RemainingAccess
        END IF
    END IF

    -- Support for MAXIMUM_ALLOWED 
    IF RemainingAccess contains MAXIMUM_ALLOWED access bit THEN
        Set MaxAllowedMode to TRUE
    END IF

    IF Object Tree is not NULL THEN
        Set LocalTree to Object Tree

    -- node is of type OBJECT_TYPE_LIST
        FOR each node in LocalTree DO
            Set node.Remaining to RemainingAccess
        END FOR

        END IF

    FOR each ACE in DACL DO
        IF ACE.AceFlags does not contain INHERIT_ONLY_ACE THEN
       
            CASE ACE.Type OF

                CASE Allow Access:

                    CALL SidInToken( Token, ACE.Sid, and PrincipalSelfSubst )
                    IF SidInToken returns True THEN
                        IF MaxAllowedMode equals TRUE THEN
                          Set AllowedAccesses to AllowedAccesses or ACE.AccessMask
                        ELSE
                            Remove ACE.AccessMask from RemainingAccess
                            FOR each node in LocalTree DO
                                Remove ACE.AccessMask from node.Remaining
                            END FOR
                        END IF
                    END IF
       
                CASE Deny Access:

                    CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst )
                    IF SidInToken returns True THEN
                        IF MaxAllowedMode equals TRUE THEN
                            Set DeniedAccesses to DeniedAccesses or ACE.AccessMask
                        ELSE
                            IF any bit of RemainingAccess is in ACE.AccessMask THEN
                                Return access_denied
                            END IF
                        END IF
                    END IF
       
                CASE Object Allow Access:

                    CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst )
                    IF SidInToken returns True THEN
                        IF ACE.Object is contained in LocalTree THEN
                            Locate node n in LocalTree such that 
                              n.GUID is the same as ACE.Object
                            Remove ACE.AccessMask from n.Remaining
                            FOR each node ns such that ns is a descendent of n DO
                                Remove ACE.AccessMask from ns.Remaining
                            END FOR
                            FOR each node np such that np is an ancestor of n DO
                              Set np.Remaining to np.Remaining or np-1.Remaining

                          --  the 'or' above is a logical bitwise OR operator.  For
                          --  Some uses (like Active Directory), a hierarchical list
                          --  of types can be passed in; if the requestor is granted 
                          --  access to a specific node, this will grant access to 
                          --  all its children. The preceding lines implement this by 
                          --  removing, from each child, the permissions just found for
                          --  the parent. The change is propagated upwards in
                          --  the tree: once a permission request has been satisfied
                          --  we can tell the next-higher node that we do not need
                          --  to inherit it from the higher node (we already have it 
                          --  in the current node). And since we must not blindly 
                          --  replace the parent's RemainingAccess, we BIT_OR the  
                          --  parent's RemainingAccess with the current node's. This 
                          --  way, if the parent needs, say, READ_CONTROL, and the
                          --  current node was just granted that, the parent's 
                          --  RemainingAccess still contains this bit since satisfying 
                          --  the request at a lower level does nothing to affect 
                          --  the higher level node. Active Directory has its own  
                          --  checking rules--see [MS-ADTS] section 3.1.1.4.3. 
                          
                            END FOR
                        END IF
                    END IF
       
                CASE Object Deny Access:

                    CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst )
                    IF SidInToken returns True THEN
                        Locate node n in LocalTree such that 
                          n.GUID is the same as ACE.Object
                        IF n exists THEN
                            If any bit of n.Remaining is in ACE.AccessMask THEN
                                Return access_denied
                            END IF
                        END IF
                    END IF

                CASE Allow Access Callback Ace:

                    EvaluateAceCondition(Token,
                                         Sacl,
                                         ApplicationData,
                                         ApplicationDataSize) returning Result 

                    IF Result is 1 THEN
                        IF (SidInToken(Token, ACE.Sid, PrincipalSelfSubst)) THEN
                            SET n = root node of object tree
                            FOR each node np such that np is an ancestor of n DO
                              Set np.Remaining to np.Remaining or np-1.Remaining

                          --  the 'or' above is a logical bitwise OR operator.  For
                          --  Some uses (like Active Directory), a hierarchical list
                          --  of types can be passed in; if the requestor is granted 
                          --  access to a specific node, this will grant access to 
                          --  all children. The preceding lines implement this by 
                          --  removing, from each child, the permissions just found for 
                          --  the parent. The change is propagated upwards in
                          --  the tree: once a permission request has been satisfied
                          --  we can tell the next-higher node that we do not need
                          --  to inherit it from the higher node (we already have it 
                          --  in the current node). And since we must not blindly 
                          --  replace the parent's RemainingAccess, we BIT_OR the  
                          --  parent's RemainingAccess with the current node's. This 
                          --  way, if the parent needs, say, READ_CONTROL, and the
                          --  current node was just granted that, the parent's 
                          --  RemainingAccess still contains this bit since satisfying 
                          --  the request at a lower level does nothing to affect 
                          --  the higher level node.

                            END FOR
                        END IF
                    END IF
            END CASE
        END IF
    END FOR

    Set GrantedAccess to AllowedAccesses and (not DeniedAccesses)

    IF MaxAllowedMode equals TRUE THEN
        -- The not operator below is a bit-wise operator
        Set GrantedAccess to AllowedAccesses and (not DeniedAccesses)
        Return success
    END IF

    IF 

    IF RemainingAccess to 0 THEN
        Return success
    Else
        Return access_denied
    END IF

END-SUBROUTINE

STATUS_CODE
AccessCheck(
    TOKEN Token,
    SECURITY_DESCRIPTOR SecurityDescriptor,
    ACCESS_MASK Access Request mask,
    OBJECT_TYPE_LIST Object Tree,
    [out] ACCESS_MASK GrantedAccess)

    Dim CentralAccessPolicy CentralizedAccessPolicy
    Dim SECURITY_DESCRIPTOR CaprSecurityDescriptor
    Dim SECURITY_DESCRIPTOR StagedCaprSecurityDescriptor
    Dim ACCESS_MASK DesiredAccess
    Dim ACCESS_MASK CentralAccessPolicyEffectiveAccess
    Dim ACCESS_MASK CentralAccessPolicyEntryEffectiveAccess
    Dim ACCESS_MASK CentralAccessPolicyStagedAccess
    Dim ACCESS_MASK CentralAccessPolicyEntryStagedAccess
    Dim ULONG Result
    Dim STATUS_CODE Status
    
    Set DACL to SecurityDescriptor Dacl field
    Set SACL to SecurityDescriptor Sacl field
    Set RemainingAccess to Access Request mask
    Set AllowedAccesses to 0
    Set DeniedAccesses to 0
    Set DesiredAccess to Access Request mask

    CALL EvaluateTokenAgainstDescriptor(Token,
                                        SecurityDescriptor,
                                        DesiredAccess,
                                        Object Tree,
                                        GrantedAccess) returning Status

    IF Status is access_denied THEN
        return Status
    END IF

    CALL GetCentralizedAccessPolicy(SACL) returning CentralizedAccessPolicy

    IF CentralizedAccessPolicy is not NULL THEN

        Set CentralAccessPolicyEffectiveAccess to DesiredAccess
        Set CentralAccessPolicyStagedAccess to DesiredAccess

        FOR each CentralAccessPolicyRule in CentralAccessPolicy.RulesList

           EvaluateAceCondition(Token,
                                SACL,
                                AppliesTo,
                                AppliesToSize) returning Result

            IF Result is not 1 THEN
                GOTO NextRule
            END IF

            Copy SecurityDescriptor to CaprSecurityDescriptor
            Set CaprSecurityDescriptor.DACL to
    CentralAccessPolicyRule.EffectiveCentralAccessPolicy.AccessCondition.DACL

            EvaluateTokenAgainstDescriptor
                                   (Token,
                                    CaprSecurityDescriptor,
                                    DesiredAccess,
                                    NULL,
                                    CentralAccessPolicyEntryEffectiveAccess)

            -- The and operator below is a bit-wise operator
            Set CentralAccessPolicyEffectiveAccess to 
         CentralAccessPolicyEffectiveAccess and CentralAccessPolicyEntryEffectiveAccess

           -- StagingLocalPolicyEnabled = True if MS-GPAC ADM variable
           -- "System Advanced Audit Policy" (MS-GPAC section 3.2.1.1) contains the GUID
           -- for "Central Access Policy Staging" as specified in MS-GPAC section 2.2.1.2

            IF IfStagingLocalPolicyEnabled THEN 

                Copy SecurityDescriptor to StagedCaprSecurityDescriptor
                Set StagedCaprSecurityDescriptor.DACL to 
         CentralAccessPolicyRule.StagedCentralAccessPolicy.AccessControl.DACL

                EvaluateTokenAgainstDescriptor
                                        (Token,
                                        StagedCaprSecurityDescriptor,
                                        DesiredAccess,
                                        NULL,
                                        CentralAccessPolicyEntryStagedAccess)

                -- The and operator below is a bit-wise operator

                Set CentralAccessPolicyStagedAccess to CentralAccessPolicyStagedAccess 
                                            and CentralAccessPolicyEntryStagedAccess

            ELSE IF CentralAccessPolicyEffectiveAccess is 0 THEN
                Set GrantedAccess to 0
                return access_denied
            END IF
NextRule:
        END FOR

        IF CentralAccessPolicyEffectiveAccess is not equal to 
                                        CentralAccessPolicyStagedAccess THEN
            -- Log the difference between the Effective and Staged Access
        END IF

        -- The “not” and “and” operator below is a bit-wise operator

        Set AllowedAccess to AllowedAccess and CentralAccessPolicyEffectiveAccess
        Set RemainingAccess to DesiredAccess and not CentralAccessPolicyEffectiveAccess

        FOR each node in Object Tree DO
            Set node.Remaining to RemainingAccess
        END FOR

    ELSE
       Return success
    END IF

    IF MaxAllowedMode equals TRUE THEN
        -- The not operator below is a bit-wise operator
        Set GrantedAccess to AllowedAccesses and (not DeniedAccesses)
        Return success
    END IF

    IF RemainingAccess is 0 THEN
        Return success
    Else
        Return access_denied
    END IF

END-SUBROUTINE     
 
Show:
© 2014 Microsoft