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