/// <summary> /// Create SecurityDescriptor in absolute format. MS-DTYP section 2.5.4.3 /// </summary> /// <param name="parentDescriptor"> /// Security descriptor for the parent (container) object of the new object. /// If the object has no parent, this parameter is null. /// </param> /// <param name="creatorDescriptor"> /// Security descriptor for the new object provided by the creator of the object. /// Caller can pass null. /// </param> /// <param name="isContainerObject"> /// TRUE when the object is a container; otherwise, FALSE. /// </param> /// <param name="objectTypes"> /// An array of pointers to GUID structures that identify the object types or /// classes of the object associated with NewDescriptor (the return value). /// For Active Directory objects, this array contains pointers to the class /// GUIDs of the object's structural class and all attached auxiliary classes. /// If the object for which this descriptor is being created does not have a GUID, /// this field MUST be set to null. /// </param> /// <param name="autoInheritFlags"> /// A set of bit flags that control how access control entries (ACEs) are /// inherited from ParentDescriptor. /// </param> /// <param name="token"> /// Token supplied by the caller for default security information for the new object. /// </param> /// <param name="genericMapping"> /// Mapping of generic permissions to resource manager-specific permissions /// supplied by the caller. /// </param> /// <returns> /// Output security descriptor for the object computed by the algorithm. /// </returns> /// <exception cref="ArgumentException"> /// Thrown when the OwnerSid or GroupSid field of parentDescriptor is null, but /// autoInheritFlags is set to DEFAULT_OWNER_FROM_PARENT or DEFAULT_GROUP_FROM_PARENT. /// Thrown when the Dacl field of parentDescriptor doesn't exist, but the DACLPresent flag is set. /// Thrown when the Sacl field of parentDescriptor doesn't exist, but the SP flag is set. /// </exception> public static _SECURITY_DESCRIPTOR CreateSecurityDescriptor( _SECURITY_DESCRIPTOR? parentDescriptor, _SECURITY_DESCRIPTOR? creatorDescriptor, bool isContainerObject, Guid[] objectTypes, SecurityDescriptorAutoInheritFlags autoInheritFlags, Token token, GenericMapping genericMapping) { const byte DEFAULT_REVISION = 1; _SECURITY_DESCRIPTOR newDescriptor = new _SECURITY_DESCRIPTOR(); newDescriptor.Revision = DEFAULT_REVISION; //Compute the Owner field (v20100908) //IF CreatorDescriptor.Owner is NULL THEN //IF AutoInheritFlags contains DEFAULT_OWNER_FROM_PARENT THEN //Set NewDescriptor.Owner to ParentDescriptor.Owner //ELSE //Set NewDescriptor.Owner to Token.SIDs[Token.OwnerIndex] //ENDIF //ELSE //Set NewDescriptor.Owner to CreatorDescriptor.Owner //ENDIF if (creatorDescriptor != null) { newDescriptor.OwnerSid = creatorDescriptor.Value.OwnerSid; } else if ((autoInheritFlags & SecurityDescriptorAutoInheritFlags.DEFAULT_OWNER_FROM_PARENT) == SecurityDescriptorAutoInheritFlags.DEFAULT_OWNER_FROM_PARENT) { if (parentDescriptor != null) { newDescriptor.OwnerSid = parentDescriptor.Value.OwnerSid; } else { throw new ArgumentException( "The parentDescriptor doesn't exist, but DEFAULT_OWNER_FROM_PARENT flag is set.", "parentDescriptor"); } } else { newDescriptor.OwnerSid = token.Sids[token.OwnerIndex]; } //Compute the Group field //IF CreatorDescriptor.Group is NULL THEN //IF AutoInheritFlags contains DEFAULT_GROUP_FROM_PARENT THEN //Set NewDescriptor.Group to ParentDescriptor.Group //ELSE //Set NewDescriptor.Group to Token.SIDs[Token.PrimaryGroup] //ENDIF //ELSE //Set NewDescriptor.Group to CreatorDescriptor.Group //ENDIF if (creatorDescriptor != null) { newDescriptor.GroupSid = creatorDescriptor.Value.GroupSid; } else if ((autoInheritFlags & SecurityDescriptorAutoInheritFlags.DEFAULT_GROUP_FROM_PARENT) == SecurityDescriptorAutoInheritFlags.DEFAULT_GROUP_FROM_PARENT) { if (parentDescriptor != null) { newDescriptor.GroupSid = parentDescriptor.Value.GroupSid; } else { throw new ArgumentException( "The parentDescriptor doesn't exist, but DEFAULT_GROUP_FROM_PARENT flag is set.", "parentDescriptor"); } } else { newDescriptor.GroupSid = token.Sids[token.PrimaryGroup]; } _ACL? parentDacl = null; _ACL? parentSacl = null; SECURITY_DESCRIPTOR_Control parentControl = SECURITY_DESCRIPTOR_Control.None; _ACL? creatorDacl = null; _ACL? creatorSacl = null; SECURITY_DESCRIPTOR_Control creatorControl = SECURITY_DESCRIPTOR_Control.None; #region Check Sacl or Dacl filed against SP or DACLPresent flag in the control field. if (parentDescriptor != null) { parentControl = parentDescriptor.Value.Control; if ((parentDescriptor.Value.Control & SECURITY_DESCRIPTOR_Control.DACLPresent) == SECURITY_DESCRIPTOR_Control.DACLPresent) { if (parentDescriptor.Value.Dacl == null) { throw new ArgumentException( "The Dacl field of parentDescriptor doesn't exist, but the DACLPresent flag is set.", "parentDescriptor"); } else { parentDacl = parentDescriptor.Value.Dacl; } } if ((parentDescriptor.Value.Control & SECURITY_DESCRIPTOR_Control.SACLPresent) == SECURITY_DESCRIPTOR_Control.SACLPresent) { if (parentDescriptor.Value.Sacl == null) { throw new ArgumentException( "The Sacl field of parentDescriptor doesn't exist, but the SP flag is set.", "parentDescriptor"); } else { parentSacl = parentDescriptor.Value.Sacl; } } } if (creatorDescriptor != null) { creatorControl = creatorDescriptor.Value.Control; if ((creatorDescriptor.Value.Control & SECURITY_DESCRIPTOR_Control.DACLPresent) == SECURITY_DESCRIPTOR_Control.DACLPresent) { if (creatorDescriptor.Value.Dacl == null) { throw new ArgumentException( "The Dacl field of creatorDescriptor doesn't exist, but the DACLPresent flag is set.", "creatorDescriptor"); } else { creatorDacl = creatorDescriptor.Value.Dacl; } } if ((creatorDescriptor.Value.Control & SECURITY_DESCRIPTOR_Control.SACLPresent) == SECURITY_DESCRIPTOR_Control.SACLPresent) { if (creatorDescriptor.Value.Sacl == null) { throw new ArgumentException( "The Sacl field of creatorDescriptor doesn't exist, but the SP flag is set.", "creatorDescriptor"); } else { creatorSacl = creatorDescriptor.Value.Sacl; } } } #endregion //Compute the DACL //CALL ComputeACL WITH //COMPUTE_DACL, ParentDescriptor.DACL, ParentDescriptor.Control, //CreatorDescriptor.DACL,CreatorDescriptor.Control //IsContainerObject, ObjectTypes, GenericMapping, //NewDescriptor.Owner, NewDescriptor.Group, Token //RETURNING NewDACL, NewControl //Set NewDescriptor.DACL to NewDACL //Set NewDescriptor.Control to NewControl SECURITY_DESCRIPTOR_Control newControl = SECURITY_DESCRIPTOR_Control.None; _ACL? newDacl = ComputeACL( AclComputeType.COMPUTE_DACL, parentDacl, parentControl, creatorDacl, creatorControl, isContainerObject, objectTypes, autoInheritFlags, genericMapping, newDescriptor.OwnerSid.Value, newDescriptor.GroupSid.Value, token, out newControl); newDescriptor.Dacl = newDacl; newDescriptor.Control = newControl; //Compute the SACL //CALL ComputeACL WITH //COMPUTE_SACL, ParentDescriptor.SACL, ParentDescriptor.Control, //CreatorDescriptor.SACL,CreatorDescriptor.Control //IsContainerObject, ObjectTypes, GenericMapping, //NewDescriptor.Owner, NewDescriptor.Group, Token //RETURNING NewSACL, NewControl //Set NewDescriptor.SACLto NewSACL //Set NewDescriptor.Control to (NewDescriptor.Control OR NewControl) //RETURN NewDescriptor _ACL? newSacl = ComputeACL( AclComputeType.COMPUTE_SACL, parentSacl, parentControl, creatorSacl, creatorControl, isContainerObject, objectTypes, autoInheritFlags, genericMapping, newDescriptor.OwnerSid.Value, newDescriptor.GroupSid.Value, token, out newControl); newDescriptor.Sacl = newSacl; newDescriptor.Control |= newControl; //New SecurityDescriptor is in absolute format. newDescriptor.Control = newDescriptor.Control & (~SECURITY_DESCRIPTOR_Control.SelfRelative); return newDescriptor; }
/// <summary> /// PostProcessACL. MS-DTYP section 2.5.4.9 /// </summary> /// <param name="acl">ACL on which to substitute SIDs.</param> /// <param name="copyFilter">The filter for post-processing the ACL.</param> /// <param name="owner">Owner to use in substituting the CreatorOwner SID.</param> /// <param name="group">Group to use in substituting the CreatorGroup SID.</param> /// <param name="genericMapping"> /// Mapping of generic permissions to resource manager-specific /// permissions supplied by the caller. /// </param> /// <returns> /// The computed ACL with the SID substitutions performed. /// </returns> public static _ACL PostProcessACL( _ACL acl, CopyFilter copyFilter, _SID owner, _SID group, GenericMapping genericMapping) { #region Pseudocode v20110329 //// Substitute CreatorOwner and CreatorGroup SIDs and do GenericMapping in ACL //Initialize NewACL to Empty ACL //FOR each ACE in ACL DO // // Determine if this ACE passes the filter to be copied to the new ACL // SET CopyThisAce = FALSE // CASE CopyFilter OF // CopyAllAces: // BEGIN // SET CopyThisAce = TRUE // END // CopyInheritedAces: // BEGIN // IF (ACE.AceFlags contains INHERITED_ACE) THEN // SET CopyThisAce = TRUE // ENDIF // END // CopyExplicitAces: // BEGIN // IF (ACE.AceFlags does not contain INHERITED_ACE) THEN // SET CopyThisAce = TRUE // ENDIF // END // ENDCASE // Set NewACE to ACE // IF (CopyThisAce) THEN // CASE ACE.Sid OF // CREATOR_OWNER: // NewACE.Sid = Owner // CREATOR_GROUP: // NewACE.Sid = Group // ENDCASE // IF (ACE.Mask contains GENERIC_READ) THEN // Add GenericMapping.GenericRead to NewACE.Mask // ENDIF // IF (ACE.Mask contains GENERIC_WRITE) THEN // Add GenericMapping.GenericWrite to NewACE.Mask // ENDIF // IF (ACE.Mask contains GENERIC_EXECUTE) THEN // Add GenericMapping.GenericExecute to NewACE.Mask // ENDIF // Append NewACE to NewACL // ENDIF //END FOR //RETURN NewACL //// END PostProcessACL #endregion _ACL newAcl = new _ACL(); newAcl.AclRevision = acl.AclRevision; newAcl.Aces = new object[] { }; List<object> newAces = new List<object>(); if (acl.Aces != null) { foreach (object ace in acl.Aces) { object newAce = ObjectUtility.DeepClone(ace); _ACE_HEADER header = (_ACE_HEADER)ObjectUtility.GetFieldValue(newAce, "Header"); bool copyThisAce = false; switch (copyFilter) { case CopyFilter.CopyAllAces: copyThisAce = true; break; case CopyFilter.CopyInheritedAces: if ((header.AceFlags & ACE_FLAGS.INHERITED_ACE) == ACE_FLAGS.INHERITED_ACE) { copyThisAce = true; } break; case CopyFilter.CopyExplicitAces: if ((header.AceFlags & ACE_FLAGS.INHERITED_ACE) == ACE_FLAGS.None) { copyThisAce = true; } break; } if (copyThisAce) { #region Substitute CreatorOwner and CreatorGroup SIDs in ACL _SID sid = (_SID)ObjectUtility.GetFieldValue(newAce, "Sid"); if (ObjectUtility.DeepCompare(sid, GetWellKnownSid(WellKnownSid.CREATOR_OWNER, null))) { ObjectUtility.SetFieldValue(newAce, "Sid", owner); header.AceSize = (ushort)(header.AceSize - (sid.SubAuthorityCount - owner.SubAuthorityCount) * sizeof(uint)); ObjectUtility.SetFieldValue(newAce, "Header", header); } else if (ObjectUtility.DeepCompare(sid, GetWellKnownSid(WellKnownSid.CREATOR_GROUP, null))) { ObjectUtility.SetFieldValue(newAce, "Sid", group); header.AceSize = (ushort)(header.AceSize - (sid.SubAuthorityCount - group.SubAuthorityCount) * sizeof(uint)); ObjectUtility.SetFieldValue(newAce, "Header", header); } #endregion #region Do GenericMapping in ACL uint mask = (uint)ObjectUtility.GetFieldValue(newAce, "Mask"); if ((mask & ACCESS_MASK_GENERIC_READ) == ACCESS_MASK_GENERIC_READ) { mask = mask | genericMapping.GenericRead; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } if ((mask & ACCESS_MASK_GENERIC_WRITE) == ACCESS_MASK_GENERIC_WRITE) { mask = mask | genericMapping.GenericWrite; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } if ((mask & ACCESS_MASK_GENERIC_EXECUTE) == ACCESS_MASK_GENERIC_EXECUTE) { mask = mask | genericMapping.GenericExecute; ObjectUtility.SetFieldValue(newAce, "Mask", mask); } #endregion //Append NewACE to NewACL newAces.Add(newAce); } } } newAcl.Aces = newAces.ToArray(); newAcl.AceCount = (ushort)newAcl.Aces.Length; newAcl.AclSize = DtypUtility.CalculateAclSize(newAcl); return newAcl; }
/// <summary> /// ComputeACL. MS-DTYP section 2.5.4.4 /// </summary> /// <param name="computeType">Enumeration of COMPUTE_DACL and COMPUTE_SACL.</param> /// <param name="parentAcl">ACL from the parent security descriptor.</param> /// <param name="parentControl">Control flags from the parent security descriptor.</param> /// <param name="creatorAcl">ACL supplied in the security descriptor by the creator.</param> /// <param name="creatorControl">Control flags supplied in the security descriptor by the creator.</param> /// <param name="isContainerObject">TRUE if the object is a container; otherwise, FALSE.</param> /// <param name="objectTypes">Array of GUIDs for the object type being created.</param> /// <param name="autoInheritFlags"> /// A set of bit flags that control how access control entries (ACEs) are /// inherited from ParentDescriptor. /// </param> /// <param name="genericMapping"> /// Mapping of generic permissions to resource manager-specific permissions supplied by the caller. /// </param> /// <param name="owner">Owner to use in substituting the CreatorOwner SID.</param> /// <param name="group">Group to use in substituting the CreatorGroup SID.</param> /// <param name="token">Token for default values.</param> /// <param name="computedControl">ComputedControl</param> /// <returns>Computed ACL</returns> public static _ACL? ComputeACL( AclComputeType computeType, _ACL? parentAcl, SECURITY_DESCRIPTOR_Control parentControl, _ACL? creatorAcl, SECURITY_DESCRIPTOR_Control creatorControl, bool isContainerObject, Guid[] objectTypes, SecurityDescriptorAutoInheritFlags autoInheritFlags, GenericMapping genericMapping, _SID owner, _SID group, Token token, out SECURITY_DESCRIPTOR_Control computedControl) { #region Pseudocode v20110329 //// The details of the algorithm to merge the parent ACL and the supplied ACL. //// The Control flags computed are slightly different based on whether it is the //// ACL in the DACL or the SACL field of the descriptor. //// The caller specifies whether it is a DACL or a SACL using the parameter, //// ComputeType. //Set ComputedACL to NULL //Set ComputedControl to NULL //CALL ContainsInheritableACEs WITH ParentACL RETURNING ParentHasInheritableACEs //IF ParentHasInheritableACEs = TRUE THEN // // The Parent ACL has inheritable ACEs. The Parent ACL should be used if no Creator // // ACL is supplied, or if the Creator ACL was supplied AND it is a default ACL based // // on object type information // IF(CreatorACL is not present) OR // ((CreatorACL is present) AND // (AutoInheritFlags contains DEFAULT_DESCRIPTOR_FOR_OBJECT)) // THEN // // Use only the inherited ACEs from the parent. First compute the ACL from the // // parent ACL, then clean it up by resolving the generic mappings etc. // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING NextACL // CALL PostProcessACL WITH // ACL set to NextACL, // CopyFilter set to CopyInheritedAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING FinalACL // Set ComputedACL to FinalACL // RETURN // ENDIF //IF ((CreatorACL is present) AND // (AutoInheritFlags does not contain DEFAULT_DESCRIPTOR_FOR_OBJECT)) //THEN // // Since a creator ACL is present, and we’re not defaulting the // // descriptor, determine which ACEs are inherited and compute the new ACL // CALL PreProcessACLFromCreator WITH // ACL set to CreatorACL // RETURNING PreACL // CALL ComputeInheritedACLFromCreator WITH // ACL set to PreACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING TmpACL // // Special handling for DACL types of ACLs // IF (ComputeType = DACL_COMPUTE) THEN // // DACL-specific operations // IF (CreatorControl does not have DACL_PROTECTED flag set) AND // (AutoInheritFlags contains DACL_AUTO_INHERIT) // THEN // // We’re not working from a protected DACL, and we’re supposed to // // allow automatic inheritance. Compute the inherited ACEs from // // Parent ACL this time, and append that to the ACL that we’re building // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING InheritedParentACL // Append InheritedParentACL.ACEs to TmpACL.ACE // Set DACL_AUTO_INHERITED flag in ComputedControl // ENDIF // ENDIF // DACL-Specific behavior // IF (ComputeType = SACL_COMPUTE) THEN // // Similar to the above, perform SACL-specific operations // IF (CreatorControl does not have SACL_PROTECTED flag set) AND // (AutoInheritFlags contains SACL_AUTO_INHERIT flag) // THEN // // We’re not working from a protected SACL, and we’re supposed to // // allow automatic inheritance. Compute the inherited ACEs from // // Parent ACL this time, and append that to the ACL that we’re building // CALL ComputeInheritedACLFromParent WITH // ACL set to ParentACL, // IsContainerObject set to IsContainerObject, // ObjectTypes set to ObjectTypes // RETURNING InheritedParentACL // Append InheritedParentACL.ACEs to TmpACL.ACE // Set SACL_AUTO_INHERITED flag in ComputedControl // ENDIF // ENDIF // SACL-Specific behavior // CALL PostProcessACL WITH // ACL set to TmpACL, // CopyFilter set to CopyInheritedAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING ProcessedACL // Set ComputedACL to ProcessedACL // RETURN //ENDIF // CreatorACL is present //ELSE // ParentACL does not contain inheritable ACEs // IF CreatorACL = NULL THEN // // No ACL supplied for the object // IF (ComputeType = DACL_COMPUTE) THEN // Set TmpACL to Token.DefaultDACL // ELSE // // No default for SACL; left as NULL // ENDIF // ELSE // // Explicit ACL was supplied for the object - either default or not. // // In either case, use it for the object, since there are no inherited ACEs. // CALL PreProcessACLFromCreator WITH CreatorACL // RETURNING TmpACL // ENDIF // CALL PostProcessACL WITH // ACL set to TmpACL, // CopyFilter set to CopyAllAces, // Owner set to Owner, // Group set to Group, // GenericMapping set to GenericMapping // RETURNING ProcessedACL // Set ComputedACL to ProcessedACL //ENDIF //// END ComputeACL #endregion _ACL? computedACL = null; computedControl = SECURITY_DESCRIPTOR_Control.None; _ACL tmpAcl = new _ACL(); _ACL preAcl = new _ACL(); if (parentAcl != null && ContainsInheritableACEs(parentAcl.Value)) { // ParentACL contains inheritable ACEs if ((creatorAcl == null) || ((creatorAcl != null) && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.DEFAULT_DESCRIPTOR_FOR_OBJECT) == SecurityDescriptorAutoInheritFlags.DEFAULT_DESCRIPTOR_FOR_OBJECT)) { // Use only the inherited ACEs from the parent _ACL nextAcl = ComputeInheritedACLfromParent(parentAcl.Value, isContainerObject, objectTypes); _ACL finalAcl = PostProcessACL(nextAcl, CopyFilter.CopyInheritedAces, owner, group, genericMapping); computedACL = finalAcl; } else { preAcl = PreProcessACLfromCreator(creatorAcl.Value); tmpAcl = ComputeInheritedACLfromCreator(preAcl, isContainerObject, objectTypes); _ACL? inheritedAcl = null; if ((computeType == AclComputeType.COMPUTE_DACL) && (creatorControl & SECURITY_DESCRIPTOR_Control.DACLProtected) == SECURITY_DESCRIPTOR_Control.None && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.DACL_AUTO_INHERIT) == SecurityDescriptorAutoInheritFlags.DACL_AUTO_INHERIT) { // Compute the inherited ACEs from the parent inheritedAcl = ComputeInheritedACLfromParent( parentAcl.Value, isContainerObject, objectTypes); computedControl |= SECURITY_DESCRIPTOR_Control.DACLAutoInherited; } else if ((computeType == AclComputeType.COMPUTE_SACL) && (creatorControl & SECURITY_DESCRIPTOR_Control.SACLProtected) == SECURITY_DESCRIPTOR_Control.None && (autoInheritFlags & SecurityDescriptorAutoInheritFlags.SACL_AUTO_INHERIT) == SecurityDescriptorAutoInheritFlags.SACL_AUTO_INHERIT) { // Compute the inherited ACEs from the parent inheritedAcl = ComputeInheritedACLfromParent( parentAcl.Value, isContainerObject, objectTypes); computedControl |= SECURITY_DESCRIPTOR_Control.SACLAutoInherited; } if (inheritedAcl != null) { List<object> tmpAces = new List<object>(); tmpAces.AddRange(tmpAcl.Aces); tmpAces.AddRange(inheritedAcl.Value.Aces); tmpAcl.Aces = tmpAces.ToArray(); tmpAcl.AceCount = (ushort)tmpAcl.Aces.Length; tmpAcl.AclSize = DtypUtility.CalculateAclSize(tmpAcl); } computedACL = PostProcessACL(tmpAcl, CopyFilter.CopyInheritedAces, owner, group, genericMapping); } } else { // ParentACL does not contain inheritable ACEs if (creatorAcl == null) { if (computeType == AclComputeType.COMPUTE_DACL) { tmpAcl = token.DefaultDACL; } else { //No default for SACL;left as NULL return null; } } else { // Explicit ACL was supplied for the object - either default or not. // In either case, use it for the object, since there are no inherited ACEs. tmpAcl = PreProcessACLfromCreator(creatorAcl.Value); } computedACL = PostProcessACL(tmpAcl, CopyFilter.CopyAllAces, owner, group, genericMapping); } if (computedACL != null) { computedControl |= ((computeType == AclComputeType.COMPUTE_DACL) ? SECURITY_DESCRIPTOR_Control.DACLPresent : SECURITY_DESCRIPTOR_Control.SACLPresent); } return computedACL; }