/// <summary>
 /// The sub function of the AccessCheck algorithm
 /// </summary>
 /// <param name="objectType">The current ace objectType to be dealt with.</param>
 /// <param name="localTree">
 /// The current tree representation of the hierarchy of objects for which to check access.
 /// </param>
 private static void AncestorNodesAccessCheck(
     Guid objectType,
     ref AccessCheckObjectTree localTree)
 {
     Guid[] ancestor = localTree.GetAncestorNodes(objectType);
     if (ancestor.Length > 0)
     {
         Guid currentNode = objectType;
         for (int i = 0; i < ancestor.Length; i++)
         {
             Guid[] sibling = localTree.GetSiblingNodes(currentNode);
             if (sibling.Length > 0)
             {
                 uint currentRemaining = 0;
                 foreach (Guid node in sibling)
                 {
                     currentRemaining |= localTree.GetTreeNodeData(node);
                 }
                 if (currentRemaining != localTree.GetTreeNodeData(currentNode))
                 {
                     break;
                 }
             }
             localTree.SetTreeNodeData(ancestor[i], localTree.GetTreeNodeData(currentNode));
             currentNode = ancestor[i];
         }
     }
 }
        /// <summary>
        /// Access Check Algorithm. MS-DTYP section 2.5.4.1
        /// </summary>
        /// <param name="securityDescriptor">
        /// SECURITY_DESCRIPTOR structure that is assigned to the object.
        /// </param>
        /// <param name="token">
        /// Token is an authorization context containing all SIDs 
        /// that represent the security principal
        /// </param>
        /// <param name="accessRequestMask">
        /// Set of permissions requested on the object.
        /// </param>
        /// <param name="objectTree">
        /// A tree representation of the hierarchy of objects for which 
        /// to check access. Each node represents an object with two values. 
        /// A GUID that represents the object itself and a value called 
        /// Remaining, which indicates the user rights request for that 
        /// node that have not yet been satisfied. It can be null.
        /// </param>
        /// <param name="principalSelfSubstitudeSid">
        /// A SID that logically replaces the SID in any ACE that contains 
        /// the well-known PRINCIPAL_SELF SID. It can be null.
        /// </param>
        /// <returns>
        /// Returns TRUE if access is allowed. Otherwise, it returns FALSE if access is denied. 
        /// </returns>
        /// <exception cref="ArgumentException">
        /// Thrown when the Dacl field of securityDescriptor doesn't exist, but the DACLPresent flag is set.
        /// Thrown when the Revision field of securityDescriptor is invalid
        /// </exception>
        public static bool AccessCheck(
            _SECURITY_DESCRIPTOR securityDescriptor,
            Token token,
            uint accessRequestMask,
            AccessCheckObjectTree objectTree,
            _SID? principalSelfSubstitudeSid)
        {
            //MS-ADTS Section 5.1.3.3.1 Null vs. Empty DACLs
            //The presence of a NULL DACL in the nTSecurityDescriptor attribute of an object grants full
            //access to the object to any principal that requests it; normal access checks are not performed
            //with respect to the object.

            if (securityDescriptor.Revision != 0x1)
            {
                throw new ArgumentException("The Revision field is invalid.", "securityDescriptor");
            }

            if ((securityDescriptor.Control & SECURITY_DESCRIPTOR_Control.DACLPresent)
                == SECURITY_DESCRIPTOR_Control.None)
            {
                return true;
            }
            else if (securityDescriptor.Dacl == null)
            {
                throw new ArgumentException(
                    "The Dacl field of securityDescriptor doesn't exist, but the DACLPresent flag is set.", "securityDescriptor");
            }

            //Pscudocode
            //
            //Set DACL to SecurityDescriptor Dacl field
            //Set RemainingAccess to Access Request mask
            //
            //IF RemainingAccess contains ACCESS_SYSTEM_SECURITY access flag 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
            //Remove READ_CONTROL and WRITE_DAC from RemainingAccess
            //END IF

            _ACL? dacl = securityDescriptor.Dacl;
            uint remainingAccess = accessRequestMask;
            AccessCheckObjectTree localTree = new AccessCheckObjectTree();
            Guid[] allNodes = new Guid[] { };

            if ((remainingAccess & ACCESS_MASK_ACCESS_SYSTEM_SECURITY) != 0 && token.Privileges != null)
            {
                foreach (_LUID privilege in token.Privileges)
                {
                    if (ObjectUtility.DeepCompare(privilege, GetPrivilegeLuid(PrivilegeName.SE_SECURITY_NAME)))
                    {
                        remainingAccess &= ~ACCESS_MASK_ACCESS_SYSTEM_SECURITY;
                    }
                }
            }
            if ((remainingAccess & ACCESS_MASK_WRITE_OWNER) != 0 && token.Privileges != null)
            {
                foreach (_LUID privilege in token.Privileges)
                {
                    if (ObjectUtility.DeepCompare(privilege, GetPrivilegeLuid(PrivilegeName.SE_TAKE_OWNERSHIP_NAME)))
                    {
                        remainingAccess &= ~ACCESS_MASK_WRITE_OWNER;
                    }
                }
            }
            if (securityDescriptor.OwnerSid != null
                && SidInToken(token, securityDescriptor.OwnerSid.Value, principalSelfSubstitudeSid))
            {
                remainingAccess &= ~(uint)(ACCESS_MASK_READ_CONTROL | ACCESS_MASK_WRITE_DAC);
            }

            //IF Object Tree is not NULL THEN
            //Set LocalTree to Object Tree
            //FOR each node in LocalTree DO
            //Set node.Remaining to RemainingAccess
            //END FOR
            //END IF

            if (objectTree != null && objectTree.Root != null)
            {
                localTree = (AccessCheckObjectTree)ObjectUtility.DeepClone(objectTree);
                List<Guid> tmpList = new List<Guid>();
                tmpList.Add(localTree.Root.Value);
                tmpList.AddRange(localTree.GetDescendentNodes(localTree.Root.Value));
                allNodes = tmpList.ToArray();
                foreach (Guid node in allNodes)
                {
                    localTree.SetTreeNodeData(node, remainingAccess);
                }
            }

            //FOR each ACE in DACL DO
            //IF ACE.flag 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
            //    Remove ACE.AccessMask from RemainingAccess
            //    FOR each node in LocalTree DO
            //    Remove ACE.AccessMask from node.Remaining
            //END FOR
            //END IF
            //CASE Deny Access:
            //CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst )
            //IF SidInToken returns True THEN
            //    IF any bit of RemainingAccess is in ACE.AccessMask THEN
            //    Return access_denied
            //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 = np.Remaining or np-1.Remaining
            //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
            //END CASE
            //END IF
            //END FOR

            if (dacl.Value.Aces != null)
            {
                foreach (object ace in dacl.Value.Aces)
                {
                    _ACE_HEADER header = (_ACE_HEADER)ObjectUtility.GetFieldValue(ace, "Header");
                    _SID sid = (_SID)ObjectUtility.GetFieldValue(ace, "Sid");

                    if ((header.AceFlags & ACE_FLAGS.INHERIT_ONLY_ACE) == ACE_FLAGS.INHERIT_ONLY_ACE
                        || !SidInToken(token, sid, principalSelfSubstitudeSid))
                    {
                        continue;
                    }

                    bool ret = DtypUtility.AceAccessCheck(ace, ref remainingAccess, ref localTree, allNodes);
                    if (ret == false)
                    {
                        return false;
                    }
                }
            }

            //IF RemainingAccess = 0 THEN
            //Return success
            //Else
            //Return access_denied
            //END IF

            bool status;
            if (objectTree != null && objectTree.Root != null)
            {
                status = (localTree.GetTreeNodeData(localTree.Root.Value) == 0);
            }
            else
            {
                status = (remainingAccess == 0);
            }
            return status;
        }
        /// <summary>
        /// The sub function of the AccessCheck algorithm
        /// </summary>
        /// <param name="ace">The current ace to be dealt with.</param>
        /// <param name="remainingAccess">The current requested permissions. </param>
        /// <param name="localTree">
        /// The current tree representation of the hierarchy of objects for which to check access.
        /// </param>
        /// <param name="allNodes">All the nodes in the current tree.</param>
        /// <returns>Return FALSE if access is denied. Otherwise, it returns TRUE.</returns>
        private static bool AceAccessCheck(
            object ace,
            ref uint remainingAccess,
            ref AccessCheckObjectTree localTree,
            Guid[] allNodes)
        {
            if (localTree != null && localTree.Root != null)
            {
                List<Guid> tmpList = new List<Guid>();
                tmpList.Add(localTree.Root.Value);
                tmpList.AddRange(localTree.GetDescendentNodes(localTree.Root.Value));
                allNodes = tmpList.ToArray();
            }

            _ACE_HEADER header = (_ACE_HEADER)ObjectUtility.GetFieldValue(ace, "Header");
            uint mask = (uint)ObjectUtility.GetFieldValue(ace, "Mask");
            ACCESS_OBJECT_ACE_Flags flags = ACCESS_OBJECT_ACE_Flags.None;
            ACE_TYPE aceType = header.AceType;

            if (header.AceType == ACE_TYPE.ACCESS_ALLOWED_OBJECT_ACE_TYPE
                || header.AceType == ACE_TYPE.ACCESS_DENIED_OBJECT_ACE_TYPE)
            {
                flags = (ACCESS_OBJECT_ACE_Flags)ObjectUtility.GetFieldValue(ace, "Flags");
                if ((flags & ACCESS_OBJECT_ACE_Flags.ACE_OBJECT_TYPE_PRESENT)
                                 != ACCESS_OBJECT_ACE_Flags.ACE_OBJECT_TYPE_PRESENT)
                {
                    aceType = (header.AceType == ACE_TYPE.ACCESS_ALLOWED_OBJECT_ACE_TYPE)
                        ? ACE_TYPE.ACCESS_ALLOWED_ACE_TYPE
                        : ACE_TYPE.ACCESS_DENIED_ACE_TYPE;
                }
            }

            switch (aceType)
            {
                //CASE ACE.Type OF
                //CASE Allow Access:
                //CALL SidInToken( Token, ACE.Sid, and PrincipalSelfSubst )
                //IF SidInToken returns True THEN
                //    Remove ACE.AccessMask from RemainingAccess
                //    FOR each node in LocalTree DO
                //    Remove ACE.AccessMask from node.Remaining
                //    END FOR
                //END IF
                //CASE Deny Access:
                //CALL SidInToken( Token, ACE.Sid, PrincipalSelfSubst )
                //IF SidInToken returns True THEN
                //    IF any bit of RemainingAccess is in ACE.AccessMask THEN
                //    Return access_denied
                //END IF
                //END IF

                case ACE_TYPE.ACCESS_ALLOWED_ACE_TYPE:
                    remainingAccess &= ~mask;
                    foreach (Guid node in allNodes)
                    {
                        uint newRemaining = localTree.GetTreeNodeData(node) & ~mask;
                        localTree.SetTreeNodeData(node, newRemaining);
                    }
                    break;

                case ACE_TYPE.ACCESS_DENIED_ACE_TYPE:
                    if ((remainingAccess & mask) != 0)
                    {
                        return false;
                    }
                    break;

                //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 = np.Remaining or np-1.Remaining   //Filed TDI 55801
                //END FOR
                //END IF
                //END IF

                case ACE_TYPE.ACCESS_ALLOWED_OBJECT_ACE_TYPE:
                    Guid objectType = (Guid)ObjectUtility.GetFieldValue(ace, "ObjectType");
                    if (localTree.ContainsTreeNode(objectType))
                    {
                        uint newRemaining = localTree.GetTreeNodeData(objectType) & ~mask;
                        localTree.SetTreeNodeData(objectType, newRemaining);

                        Guid[] descendent = localTree.GetDescendentNodes(objectType);
                        if (descendent.Length > 0)
                        {
                            foreach (Guid nodeGuid in descendent)
                            {
                                newRemaining = localTree.GetTreeNodeData(nodeGuid) & ~mask;
                                localTree.SetTreeNodeData(nodeGuid, newRemaining);
                            }
                        }

                        DtypUtility.AncestorNodesAccessCheck(objectType, ref localTree);
                    }
                    break;

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

                case ACE_TYPE.ACCESS_DENIED_OBJECT_ACE_TYPE:
                    Guid objectType2 = (Guid)ObjectUtility.GetFieldValue(ace, "ObjectType");
                    foreach (Guid node in allNodes)
                    {
                        if (node == objectType2 && (localTree.GetTreeNodeData(node) & mask) != 0)
                        {
                            return false;
                        }
                    }
                    break;
            }
            return true;
        }