c# - 静默应用 ACL 失败(有时)

标签 c# file-permissions acl distributed-computing

我有一个应用程序在应用了一些 ACL 的多台服务器上运行。

问题是当多个服务器在同一文件夹结构(即三层)上应用时,通常只有第一层和第三层应用了 ACL,但也不异常(exception)。

我创建了一个具有并行任务的测试(以模拟不同的服务器):

[TestMethod]
public void ApplyACL()
{
    var baseDir = Path.Combine(Path.GetTempPath(), "ACL-PROBLEM");

    if (Directory.Exists(baseDir))
    {
        Directory.Delete(baseDir, true);
    }

    var paths = new[]
    {
        Path.Combine(baseDir, "LEVEL-1"),
        Path.Combine(baseDir, "LEVEL-1", "LEVEL-2"),
        Path.Combine(baseDir, "LEVEL-1", "LEVEL-2", "LEVEL-3")
    };

    //create folders and files, so the ACL takes some time to apply
    foreach (var dir in paths)
    {
        Directory.CreateDirectory(dir);

        for (int i = 0; i < 1000; i++)
        {
            var id = string.Format("{0:000}", i);
            File.WriteAllText(Path.Combine(dir, id + ".txt"), id);
        }
    }

    var sids = new[]
    {
        "S-1-5-21-448539723-725345543-1417001333-1111111",
        "S-1-5-21-448539723-725345543-1417001333-2222222",
        "S-1-5-21-448539723-725345543-1417001333-3333333"
    };

    var taskList = new List<Task>();
    for (int i = 0; i < paths.Length; i++)
    {
        taskList.Add(CreateTask(i + 1, paths[i], sids[i]));        
    }

    Parallel.ForEach(taskList, t => t.Start());

    Task.WaitAll(taskList.ToArray());

    var output = new StringBuilder();
    var failed = false;
    for (int i = 0; i < paths.Length; i++)
    {
        var ok = Directory.GetAccessControl(paths[i])
                          .GetAccessRules(true, false, typeof(SecurityIdentifier))
                          .OfType<FileSystemAccessRule>()
                          .Any(f => f.IdentityReference.Value == sids[i]);

        if (!ok)
        {
            failed = true;
        }
        output.AppendLine(paths[i].Remove(0, baseDir.Length + 1) + " --> " + (ok ? "OK" : "ERROR"));
    }

    Debug.WriteLine(output);

    if (failed)
    {
        Assert.Fail();
    }
}

private static Task CreateTask(int i, string path, string sid)
{
    return new Task(() =>
    {
        var start = DateTime.Now;
        Debug.WriteLine("Task {0} start:  {1:HH:mm:ss.fffffff}", i, start);
        var fileSystemAccessRule = new FileSystemAccessRule(new SecurityIdentifier(sid), 
                                                            FileSystemRights.Modify | FileSystemRights.Synchronize,
                                                            InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
                                                            PropagationFlags.None,
                                                            AccessControlType.Allow);

        var directorySecurity = Directory.GetAccessControl(path);
        directorySecurity.ResetAccessRule(fileSystemAccessRule);
        Directory.SetAccessControl(path, directorySecurity);

        Debug.WriteLine("Task {0} finish: {1:HH:mm:ss.fffffff} ({2} ms)", i, DateTime.Now, (DateTime.Now - start).TotalMilliseconds);
    });
}

我遇到了同样的问题:通常(但不总是)只有一级和三级应用了 ACL。

为什么会这样,我该如何解决?

最佳答案

Directory.SetAccessControl 在内部调用 Win32 API 函数 SetSecurityInfo: https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588.aspx

上述文档的重要部分:

如果您正在设置对象的自由访问控制列表 (DACL) 或系统访问控制列表 (SACL) 中的任何元素,系统会自动将任何可继承的访问控制条目 (ACE) 传播到现有的子对象, 根据 ACE 继承规则。

子对象的枚举(CodeFuller 已经对此进行了描述)是在低级函数 SetSecurityInfo 本身中完成的。更详细地说,此函数调用系统 DLL NTMARTA.DLL,它完成所有脏工作。 这其中的背景是继承,是一种“伪继承”,出于性能的考虑。每个对象不仅包含“自己的”ACE,还包含继承的 ACE(那些在资源管理器中显示为灰色的)。所有这些继承都是在 ACL 设置期间完成的,而不是在运行时 ACL 解析/检查期间完成的。

Microsoft 的这一先前决定也引发了以下问题(Windows 管理员应该知道这一点):

如果将目录树移动到文件系统中设置了不同 ACL 的另一个位置,则移动的 try 对象的 ACL 不会更改。 所以说,继承的权限是错误的,它们不再匹配父级的 ACL。 此继承不是由 InheritanceFlags 定义的,而是由 SetAccessRuleProtection

添加 CodeFuller 的答案:

>>枚举完成后,将内部目录安全记录分配给目录。

这个枚举不是单纯的子对象的读取,每个子对象的ACL都会被SET

所以这个问题是 Windows ACL 处理的内部工作机制所固有的: SetSecurityInfo 检查父目录中应继承的所有 ACE,然后进行递归并将这些可继承的 ACE 应用于所有子对象。

我知道这件事是因为我写了一个工具来设置完整文件系统(有数百万个文件)的 ACL,它使用我们所谓的“托管文件夹”。我们可以拥有具有自动计算列表权限的非常复杂的 ALC。 对于文件和文件夹的 ACL 设置,我使用 SetKernelObjectSecurity。这个 API 通常不应该用于文件系统,因为它不处理继承的东西。所以你必须自己做。但是,如果您知道自己在做什么并且正确地做了,那么这是在任何情况下在文件树上设置 ACL 的唯一可靠方法。 事实上,可能存在 SetSecurityInfo 无法正确设置这些对象的情况(子对象中损坏的/无效的 ACL 条目)。

现在看 Anderson Pimentel 的代码:

从上面应该很清楚,并行设置只有在继承被阻止的情况下才能起作用 在每个目录级别。
但是,仅调用

是行不通的
dirSecurity.SetAccessRuleProtection(true, true);

在任务中,因为这个调用可能会迟到。

如果在开始任务之前调用上面的语句,我的代码就可以工作了。

坏消息是这个用 C# 完成的调用也进行了完整的递归。

因此,除了使用 PInvoke 直接调用低级安全函数外,在 C# 中似乎没有真正令人信服的解决方案。

但那是另一回事了。

以及不同服务器设置 ACL 的初始问题:

如果我们知道背后的意图以及您希望生成的 ALC 是什么,我们也许可以找到一种方法。

让我知道。

关于c# - 静默应用 ACL 失败(有时),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47413328/

相关文章:

c# - MongoDB 中序列化和反序列化结构的解决方法

c# - LINQ to XML 克隆

c# - Amazon s3 API上传文件时如何设置权限

php - Laravel:file_put_contents()权限被拒绝-正确的存储/框架/缓存权限?

swift - 如何在 Parse 中设置 ACL?

c# - 为什么 DirectoryInfo 实例在删除后不会(重新)创建文件夹?

php - windows下linux主机如何设置文件权限?

php - Symfony2 : how to get all entities of one type which are marked with "EDIT" ACL permission?

python - 谷歌日历API ACL

c# - 合并数据集中近似相等的点