非正規の ACL をプログラムで修正するにはどうすればよいでしょうか? 質問する

非正規の 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);

呼びかけアクセスルールの追加投げる無効な操作例外次のスタック トレースが表示されます。

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 エクスプローラーを使用してディレクトリのセキュリティ権限を表示すると、次のテキストを含むメッセージ ボックスが表示されます。

の権限の順序が間違っているため、一部のエントリが無効になる可能性があります。続行して権限を正しく並べ替えるには [OK] を、権限をリセットするには [キャンセル] を押してください。

この時点で [OK] をクリックすると、問題は解決します。これは何が起きているのでしょうか? システムはどのようにしてこの状態になるのでしょうか。また、プログラムでこれを検出/修正する方法はありますか (つまり、ユーザーがエクスプローラーを使用して手動で修正しなくてもよい)?

アップデート

ACLについてもう少し調べてみたところ、正規形とは何か、そしてなぜそれが必要なのかファイルが通常どのようにしてこの状態になるのかはまだわかりませんが、イカクルスツールを使用すると、権限リストを保存し、それを順序どおりに変更し、復元することで、非正規の ACL を持つディレクトリを作成できます。今必要なのは、ユーザーの介入なしにこれを修正する方法だけです。

ベストアンサー1

この解決策は、MSDN のブログ投稿で見つかりました。えっ? - アクセス制御リストが正規ではありません基本的には、同じ権限を持つ新しい 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<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);
}

おすすめ記事