Comment réparer programmatiquement une ACL non canonique?

j'ai le code suivant:

DirectoryInfo directory = new DirectoryInfo(@"C:Program FilesCompanyProduct");
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);

L'appel à AddAccessRule lance InvalidOperationException, avec trace de la pile:

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)

cela ne se produit que sur certains systèmes (J'ai vu Windows XP et Windows 7). Dans les situations où l'erreur se produit, l'affichage des permissions de sécurité pour le répertoire à L'aide de L'Explorateur Windows provoque généralement une boîte de message à afficher avec le texte suivant:

les permissions sont mal ordonnées, ce qui peut cause de certaines entrées inefficace. Appuyez sur OK pour continuer et trier les autorisations correctement, ou sur Annuler pour réinitialiser les autorisations.

cliquer OK à ce point résout le problème. Ce qui se passe ici? Comment fonctionne un système dans cet état, et est-il possible de les détecter/corriger par programme (c'est à dire sans avoir à l'utilisateur manuellement, utilisez l'Explorateur de fixer c')?

mise à Jour

j'ai fait un peu plus de recherche sur ACL, Quelle est la forme canonique, et pourquoi c'est nécessaire. Je ne suis toujours pas sûr comment un fichier entrerait normalement dans cet état, mais j'ai trouvé que le Icacls l'outil peut être utilisé pour créer un répertoire avec un ACL non-canonique en sauvegardant la liste des permissions, en l'altérant pour être hors-service, et en la rétablissant. Maintenant j'ai juste besoin d'un moyen de le réparer sans exiger l'utilisateur interaction.

19
demandé sur Dave Andersen 2011-11-14 23:16:23

1 réponses

j'ai trouvé la solution dans un billet de blog MSDN: Dire wwhhhaaaat? - La liste de contrôle d'accès n'est pas canonique. Fondamentalement, vous devez construire un nouveau DACL avec les mêmes permissions, mais dans l'ordre canonique correct:

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);
}
37
répondu Kevin Kibler 2016-08-18 22:13:31