我有一个应用程序在应用了一些 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/