using System.Linq; namespace System.Security.AccessControl { /// Extensions for classes in the System.Security.AccessControl namespace. public static class AccessControlExtension { /// Canonicalizes the specified Access Control List. /// The Access Control List. public static void Canonicalize(this RawAcl acl) { if (acl == null) throw new ArgumentNullException(nameof(acl)); // Extract aces to list var aces = new Collections.Generic.List(acl.Cast()); // Sort aces based on canonical order aces.Sort((a, b) => Collections.Generic.Comparer.Default.Compare(GetComparisonValue(a), GetComparisonValue(b))); // Add sorted aces back to ACL while (acl.Count > 0) acl.RemoveAce(0); var aceIndex = 0; aces.ForEach(ace => acl.InsertAce(aceIndex++, ace)); } /// Sort ACEs according to canonical form for this . /// The object security whose DiscretionaryAcl will be made canonical. public static void CanonicalizeAccessRules(this ObjectSecurity objectSecurity) { if (objectSecurity == null) throw new ArgumentNullException(nameof(objectSecurity)); if (objectSecurity.AreAccessRulesCanonical) return; // Get raw SD from objectSecurity and canonicalize DACL var sd = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorBinaryForm(), 0); sd.DiscretionaryAcl.Canonicalize(); // Convert SD back into objectSecurity objectSecurity.SetSecurityDescriptorBinaryForm(sd.GetBinaryForm()); } /// Returns an array of byte values that represents the information contained in this object. /// The object. /// The byte array into which the contents of the is marshaled. public static byte[] GetBinaryForm(this GenericSecurityDescriptor sd) { if (sd == null) throw new ArgumentNullException(nameof(sd)); var bin = new byte[sd.BinaryLength]; sd.GetBinaryForm(bin, 0); return bin; } // A canonical ACL must have ACES sorted according to the following order: // 1. Access-denied on the object // 2. Access-denied on a child or property // 3. Access-allowed on the object // 4. Access-allowed on a child or property // 5. All inherited ACEs private static byte GetComparisonValue(GenericAce ace) { if ((ace.AceFlags & AceFlags.Inherited) != 0) return 5; switch (ace.AceType) { case AceType.AccessDenied: case AceType.AccessDeniedCallback: case AceType.SystemAudit: case AceType.SystemAlarm: case AceType.SystemAuditCallback: case AceType.SystemAlarmCallback: return 0; case AceType.AccessDeniedObject: case AceType.AccessDeniedCallbackObject: case AceType.SystemAuditObject: case AceType.SystemAlarmObject: case AceType.SystemAuditCallbackObject: case AceType.SystemAlarmCallbackObject: return 1; case AceType.AccessAllowed: case AceType.AccessAllowedCallback: return 2; case AceType.AccessAllowedObject: case AceType.AccessAllowedCallbackObject: return 3; default: return 4; } } } }