您如何以编程方式修复非规范ACL?

我有以下代码:

DirectoryInfo directory = new DirectoryInfo(@"C:\Program Files\Company\Product"); if (!directory.Exists) { directory.Create(); } DirectorySecurity directorySecurity = directory.GetAccessControl(); SecurityIdentifier securityIdentifier = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); directorySecurity.AddAccessRule( new FileSystemAccessRule( securityIdentifier, FileSystemRights.Write, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow)); directory.SetAccessControl(directorySecurity); 

AddAccessRule的调用会引发带有以下堆栈跟踪的InvalidOperationException

 System.InvalidOperationException: This access control list is not in canonical form and therefore cannot be modified. at System.Security.AccessControl.CommonAcl.ThrowIfNotCanonical() at System.Security.AccessControl.CommonAcl.AddQualifiedAce(SecurityIdentifier sid, AceQualifier qualifier, Int32 accessMask, AceFlags flags, ObjectAceFlags objectFlags, Guid objectType, Guid inheritedObjectType) at System.Security.AccessControl.DiscretionaryAcl.AddAccess(AccessControlType accessType, SecurityIdentifier sid, Int32 accessMask, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags) at System.Security.AccessControl.CommonObjectSecurity.ModifyAccess(AccessControlModification modification, AccessRule rule, Boolean& modified) at System.Security.AccessControl.CommonObjectSecurity.AddAccessRule(AccessRule rule) at System.Security.AccessControl.FileSystemSecurity.AddAccessRule(FileSystemAccessRule rule) 

这只发生在某些系统上(我见过Windows XP和Windows 7)。 在发生错误的情况下,使用Windows资源管理器查看目录的安全权限通常会导致使用以下文本显示消息框:

权限不正确,这可能会导致某些条目无效。 按“确定”继续并正确排序权限,或按“取消”重置权限。

此时单击“确定”可以解决问题。 这里发生了什么? 系统如何进入这种状态,有没有办法以编程方式检测/修复它(即没有用户手动使用资源管理器来解决这个问题)?

更新

我做了一些关于ACL的研究,规范forms是什么, 为什么它是必要的 。 我仍然不确定文件通常会如何进入此状态,但我发现Icacls工具可以通过保存权限列表来创建具有非规范ACL的目录,将其更改为不属于订购,并恢复它。 现在我只需要一种方法来修复它而无需用户交互。

我在MSDN博客文章中找到了解决方案: 说wwhhhaaaat? – 访问控制列表不是规范的 。 基本上,您需要使用相同的权限构造一个新的DACL,但是按照正确的规范顺序:

 static void Main(string[] args) { // directory with known ACL problem (created using Icacls) DirectoryInfo directoryInfo = new DirectoryInfo("acltest"); var directorySecurity = directoryInfo.GetAccessControl(AccessControlSections.Access); CanonicalizeDacl(directorySecurity); directoryInfo.SetAccessControl(directorySecurity); } static void CanonicalizeDacl(NativeObjectSecurity objectSecurity) { if (objectSecurity == null) { throw new ArgumentNullException("objectSecurity"); } if (objectSecurity.AreAccessRulesCanonical) { return; } // 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 RawSecurityDescriptor descriptor = new RawSecurityDescriptor(objectSecurity.GetSecurityDescriptorSddlForm(AccessControlSections.Access)); List implicitDenyDacl = new List(); List implicitDenyObjectDacl = new List(); List inheritedDacl = new List(); List implicitAllowDacl = new List(); List implicitAllowObjectDacl = new List(); foreach (CommonAce ace in descriptor.DiscretionaryAcl) { if ((ace.AceFlags & AceFlags.Inherited) == AceFlags.Inherited) { inheritedDacl.Add(ace); } else { switch (ace.AceType) { case AceType.AccessAllowed: implicitAllowDacl.Add(ace); break; case AceType.AccessDenied: implicitDenyDacl.Add(ace); break; case AceType.AccessAllowedObject: implicitAllowObjectDacl.Add(ace); break; case AceType.AccessDeniedObject: implicitDenyObjectDacl.Add(ace); break; } } } Int32 aceIndex = 0; RawAcl newDacl = new RawAcl(descriptor.DiscretionaryAcl.Revision, descriptor.DiscretionaryAcl.Count); implicitDenyDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x)); implicitDenyObjectDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x)); implicitAllowDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x)); implicitAllowObjectDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x)); inheritedDacl.ForEach(x => newDacl.InsertAce(aceIndex++, x)); if (aceIndex != descriptor.DiscretionaryAcl.Count) { System.Diagnostics.Debug.Fail("The DACL cannot be canonicalized since it would potentially result in a loss of information"); return; } descriptor.DiscretionaryAcl = newDacl; objectSecurity.SetSecurityDescriptorSddlForm(descriptor.GetSddlForm(AccessControlSections.Access), AccessControlSections.Access); }