可靠方法判断NTFS权限是否被继承
我有一个比较冷门的问题。
我想知道的是:如何判断一个文件或文件夹的权限(严格来说,是DACL中的一个特定ACE)是否是继承来的。
我尝试的解决方法是:使用Python的winapi绑定(具体来说是win32security模块)。下面是一个简化的版本,它只需要一个文件路径作为参数,然后逐个打印出ACE,并指明哪些标志被设置了。
#!/usr/bin/env python
from win32security import *
import sys
def decode_flags(flags):
_flags = {
SE_DACL_PROTECTED:"SE_DACL_PROTECTED",
SE_DACL_AUTO_INHERITED:"SE_DACL_AUTO_INHERITED",
OBJECT_INHERIT_ACE:"OBJECT_INHERIT_ACE",
CONTAINER_INHERIT_ACE:"CONTAINER_INHERIT_ACE",
INHERIT_ONLY_ACE:"INHERIT_ONLY_ACE",
NO_INHERITANCE:"NO_INHERITANCE",
NO_PROPAGATE_INHERIT_ACE:"NO_PROPAGATE_INHERIT_ACE",
INHERITED_ACE:"INHERITED_ACE"
}
for key in _flags.keys():
if (flags & key):
print '\t','\t',_flags[key],"is set!"
def main(argv):
target = argv[0]
print target
security_descriptor = GetFileSecurity(target,DACL_SECURITY_INFORMATION)
dacl = security_descriptor.GetSecurityDescriptorDacl()
for ace_index in range(dacl.GetAceCount()):
(ace_type,ace_flags),access_mask,sid = dacl.GetAce(ace_index)
name,domain,account_type = LookupAccountSid(None,sid)
print '\t',domain+'\\'+name,hex(ace_flags)
decode_flags(ace_flags)
if __name__ == '__main__':
main(sys.argv[1:])
这个过程其实很简单——获取一个安全描述符,从中获取DACL,然后遍历DACL中的ACE。这里最重要的部分是INHERITED_ACE访问标志。当ACE是继承来的时候,这个标志应该被设置,而不是显式地设置。
当你创建一个文件或文件夹时,它的ACL会根据父对象(文件夹)的ACE来填充,这些ACE是设置为可以传播给子对象的。然而,除非你对访问列表做了任何更改,否则INHERITED_ACE标志是不会被设置的!不过,继承的权限是存在的,并且确实有效。
如果你稍微做一点更改(比如,往访问列表中添加一个条目,应用更改后再删除它),这个标志就会神奇地出现(不过行为并没有改变,之前有效,现在依然有效)!我想要的是找出INHERITED_ACE标志这种行为的来源,或者找到另一种可靠的方法来判断ACE是否是继承来的。
如何重现这个现象:
- 创建一个对象(文件或文件夹)
- 在Windows资源管理器中检查权限,看到它们是从父对象传播过来的(比如,使用文件属性对话框的安全选项卡)。
- 使用我之前用的脚本检查标志(INHERITED_ACE在任何ACE上都不会被设置)。
- 更改对象的权限(应用更改),甚至可以再改回来。
- 检查标志(INHERITED_ACE 会出现)
- ..摇头不已(我知道我当时就是这样)
抱歉发了这么长的帖子,希望这至少能让人明白一点。
3 个回答
我觉得发帖的人遇到的情况和这里描述的很像:
注意,容器上的控制标志可以通过在图形界面中取消勾选再重新勾选继承选项来改变。
另外,仅仅使用微软的工具向DACL(访问控制列表)添加一个ACE(访问控制条目)也会改变控制标志。
还要注意,由于新闻组帖子中提到的许多细微错误,图形界面、cacls和icacls在处理继承时是不能完全信赖的。
看起来,控制继承的“旧”方法是结合容器上的控制标志和与继承相关的ACE标志来使用。
而“新”方法则不再使用容器上的控制标志,而是使用重复的ACE;一个用来控制对象的访问,另一个用来控制子对象继承的内容。
但是,似乎现有的微软工具(比如Vista)还不能使用这种“新”方法,所以当你用这些工具做简单修改时,它会退回到使用容器上的控制标志的“旧”方法。
如果你在Vista上创建一个新分区,然后创建一个新文件夹,再查看标志和ACE,它会看起来像这样:
ControlFlags : 0x8004
Owner : BUILTIN\Administrators
Group : WS1\None
S-1-5-32-544 : BUILTIN\Administrators : 0x0 : 0x0 : 0x1F01FF
S-1-5-32-544 : BUILTIN\Administrators : 0x0 : 0xB : 0x10000000
S-1-5-18 : NT AUTHORITY\SYSTEM : 0x0 : 0x0 : 0x1F01FF
S-1-5-18 : NT AUTHORITY\SYSTEM : 0x0 : 0xB : 0x10000000
S-1-5-11 : NT AUTHORITY\Authenticated Users : 0x0 : 0x0 : 0x1301BF
S-1-5-11 : NT AUTHORITY\Authenticated Users : 0x0 : 0xB : 0xE0010000
S-1-5-32-545 : BUILTIN\Users : 0x0 : 0x0 : 0x1200A9
S-1-5-32-545 : BUILTIN\Users : 0x0 : 0xB : 0xA0000000
注意控制标志和重复的ACE。
你可以使用 .Net 框架
System.Security.AccessControl
这包括了访问控制列表(ACL)、默认访问控制列表(DACL)和系统访问控制列表(SACL)。
在我的Windows XP家庭版上,这段代码似乎根本不管用 :-)
我得到了这个错误信息:
错误追踪(最近的调用在最前面):
文件 "C:\1.py",第37行,在 main(sys.argv[1:])
文件 "C:\1.py",第29行,在 main 对于 ace_index 在 range(dacl.GetAceCount()):AttributeError: 'NoneType' 对象没有 'GetAceCount' 属性
你能不能试着“推动”一下DACL,让它被填充呢? 我的意思是,如果你知道在稍微修改后它会正常工作……那就通过程序做个小改动,添加一个占位ACE然后再删除它。可以吗?
更新。 我在我的工作机器上用C#程序做了个实验(用的是Windows XP专业版),我得告诉你,.net获取这些安全信息的方法确实有效。所以,当我创建一个新文件时,我的C#程序能检测到ACE是被继承的,而你的Python代码却不能。
这是我运行的示例输出:
C:>csharp_tricks.exe 2.txt
完全控制 --> 是否继承: 是
完全控制 --> 是否继承: 是
读取和执行,同步 --> 是否继承: 是
C:>1.py 2.txt
2.txt
BUILTIN\Administrators 0x0
NT AUTHORITY\SYSTEM 0x0
BUILTIN\Users 0x0
我的C#类:
public class InheritedAce
{
public static string GetDACLReport(string path)
{
StringBuilder result = new StringBuilder();
FileSecurity fs = new FileSecurity(path, AccessControlSections.Access);
foreach (var rule in fs.GetAccessRules(true, true, typeof(SecurityIdentifier)).OfType<FileSystemAccessRule>())
{
result.AppendFormat("{0} --> IsInherited: {1}", rule.FileSystemRights, rule.IsInherited);
result.AppendLine();
}
return result.ToString();
}
}
所以,看起来这是Python的pywin32安全库中的一个bug。也许他们没有执行所有必要的系统调用……