c# exception acl

c# - ¿Cómo se arregla programáticamente una ACL no canónica?



exception (1)

Tengo el siguiente código:

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);

La llamada a AddAccessRule arroja una InvalidOperationException con el siguiente seguimiento de pila:

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)

Esto solo ocurre en algunos sistemas (he visto Windows XP y Windows 7). En las situaciones en las que se produce el error, al ver los permisos de seguridad para el directorio que utiliza Windows Explorer generalmente se muestra un cuadro de mensaje con el siguiente texto:

Los permisos activados están incorrectamente ordenados, lo que puede hacer que algunas entradas no sean efectivas. Presione OK para continuar y ordenar los permisos correctamente, o Cancelar para restablecer los permisos.

Hacer clic en Aceptar en este punto soluciona el problema. ¿Que está pasando aqui? ¿Cómo llega un sistema a este estado, y hay alguna forma de detectarlo / corregirlo programáticamente (es decir, sin que el usuario use manualmente Explorer para solucionarlo)?

Actualizar

Investigué un poco más sobre ACL, qué forma canónica es y por qué es necesario . Todavía no estoy seguro de cómo un archivo normalmente entraría en este estado, pero descubrí que la herramienta Icacls se puede usar para crear un directorio con una ACL no canónica guardando la lista de permisos, alterándola para que quede fuera de orden y restaurarlo Ahora solo necesito una forma de solucionarlo sin requerir la interacción del usuario.


Encontré la solución para esto en una publicación de blog de MSDN: Say wwhhhaaaat? - La lista de control de acceso no es canónica . Básicamente, necesita construir una nueva DACL con los mismos permisos, pero en el orden canónico correcto:

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<CommonAce> implicitDenyDacl = new List<CommonAce>(); List<CommonAce> implicitDenyObjectDacl = new List<CommonAce>(); List<CommonAce> inheritedDacl = new List<CommonAce>(); List<CommonAce> implicitAllowDacl = new List<CommonAce>(); List<CommonAce> implicitAllowObjectDacl = new List<CommonAce>(); 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); }