文件和目录的访问控制(3) 访问规则
访问规则有两种类型:“允许”(allow)和“拒绝”(deny)。可以通过检查规则的AccessControlType属性来确定相应规则的类型。按照约定,拒绝规则总是优先于允许规则。因而,如果向某个对象中添加下列两个规则:“授予每个人读、写访问权限”和“拒绝Xuanhun写访问权限”,则Xuanhun将被拒绝进行写访问。
想要枚举文件或者目录的访问规则时,可以使用如代码清单7-11所示的方式。
7-11 枚举文件访问规则
class Program
{
static void Main(string[] args)
{
string path = @"e:\AclTest\acltest.txt";
FileSecurity security = File.GetAccessControl(path);
foreach (FileSystemAccessRule rule in
security.GetAccessRules(true, true, typeof(NTAccount)))
{
Console.WriteLine("{0} {1}---- {2}",
rule.AccessControlType == AccessControlType.Allow ?
"授权" : "拒绝",rule.IdentityReference.ToString(),
rule.FileSystemRights
);
}
Console.Read();
}
}
以上代码使用File类的GetAccessControl方法获取FileSecurity对象,然后通过FileSecurity类的GetAccessRules方法获取当前文件的访问规则。对于目录的操作与此基本类似,相应地,使用的是DirectorySecurity对象。
GetAccessRules方法有三个参数:第一个参数表示是否要包括为对象显式设置的访问规则;第二个参数表示是否要包括继承的访问规则;第三个参数表示访问规则的类型。代码清单7-11的运行结果如图7-9所示。
7-9 枚举文件访问规则的运行结果
为方便起见,访问规则被公开为集合。该集合是只读的,因此对它的规则进行的所有修改都必须通过FileSecurity对象的专用方法(例如AddAccessRule、SetAccessRule和RemoveAccessRule)执行。集合内部的规则对象也是不可改变的。要了解为什么拒绝规则优先于允许规则,必须知道访问检查算法是如何工作的。当执行访问权限检查时,将按照规则在访问控制列表内部出现的顺序对它们进行评估。在代码清单7-11中,当检查用户Xuanhun的访问权限时,会首先评估拒绝Xuanhun读取访问的规则,然后再评估授予BUILTIN\Everyone读取和执行访问权限的规则。一旦做出允许或拒绝的决策,评估就将停止。这就是拒绝规则“生效”的原因。如果它们被放置在允许规则之后,则它们不会总是执行它们的预期功能。
和可以添加新的访问规则一样,还可以移除现有的访问规则。但是注意,在从用户那里撤回某项权限和完全拒绝该权限之间存在差异。例如,假设Xuanhun是“全职雇员”组的成员,并且被设置为:“全职雇员可以读取文件”和“Xuanhun具有读写访问权限”。根据这一方案,撤消Xuanhun的读取权利会产生下列规则:“全职雇员可以读取文件”和“Xuanhun具有写入访问权限”。这恐怕不是您所期望的结果,因为撤消Xuanhun的读取访问权限没有产生任何效果:Xuanhun仍然可以作为全职雇员获得读取访问权限。如果你的目标是确保不会将访问权限授予Xuanhun,达到该目标的唯一方式是添加一个拒绝规则。此外,如果该对象根本不包含任何访问规则,那么每个人都将被拒绝对该对象的所有访问权限。
继承
我们只考虑了不具有子对象的简单的叶子对象。一旦从叶子对象(例如文件、信号量和互斥锁)转向容器对象(例如目录、注册表项和Active Directory容器),事情就变得复杂了。额外的复杂性源自以下事实:容器的访问规则可能被配置为不仅应用于对象本身,而且还应用于它的子对象、子容器或这两者。这就涉及继承和传播设置的领域。
每个访问规则不是显式的就是继承的(用IsInherited属性来确定),显式规则是那些已经通过在对象上执行的显式操作添加到该对象的规则;相反,继承规则来自于父容器。在使用对象时,只能操纵它的显式规则。
在向容器中添加新的显式规则时,可以指定两组标志:继承标志和传播标志。继承标志有两个:容器继承(Container Inherit,CI)和对象继承(Object Inherit,OI)。指定容器继承的规则将应用于当前容器对象的子对象,对象继承规则应用于叶子子对象。当传播标志被设置为None时,这些关系是可传递的:它们将跨越当前容器下层次结构的整个子树,并且应用于该容器的子对象、孙子对象等。
规则的顺序是很重要的,因为它确定了优先顺序,并最终影响到对象的访问方式。尽管无法更改默认顺序,但明白一组规则将被授予哪个类型的访问权限是很重要的。最重要的是,所有继承规则总是跟在显式规则后面。这样,显式规则总是优先于继承规则,父规则优先于祖父规则,等等。
父对象和子对象
如果希望对象避开由其父对象给予它的安全语义,会发生什么情况呢?实际上,确实存在可以声明“我的父对象的安全设置将不再适用于我”的机制。此时,甚至可以指定是否希望在该情况下使继承规则保持原样,但是在父对象的设置发生更改时拒绝“侦听”,另外,可以彻底清除所有继承规则。这是通过访问控制保护实现的,如代码清单7-12所示:
7-12 访问控制保护
using(FileStream file = new FileStream(
@"M:\temp\sample.txt", FileMode.Open, FileAccess.ReadWrite))
{
FileSecurity security = file.GetAccessControl();
security.SetAccessRuleProtection(
true,
false );
file.SetAccessControl(security);}
以上代码中需要说明的是SetAccessRuleProtection方法,该方法设置或移除与此ObjectSecurity 对象关联的访问规则的保护。受保护的审核规则不会通过继承被父对象修改。它的第一个参数标识访问规则是否被继承,如果为true则不被继承,否则继承。第二个参数标识是否保留继承的访问规则,如果保留为true,去除则为false。
注意 尽管可以使用该技术避免从父对象那里收到继承设置,但没有办法收到父对象不打算给予你的继承设置,传播只会发生在其ACL没有受到保护的对象上。可以做的唯一事情就是在父对象改变主意之前获得继承设置的快照,因为一旦访问规则受到保护,它们就将保持这个状态,并且父对象无法重写它们。
所有者
“所有者”(owner)的概念对于对象安全性是很特殊的。所有者被赋予了特殊的权力,即使与对象相关联的规则禁止用户访问该对象,但如果该用户是所有者,则他仍然可以重写现有规则,并重新获得对该对象的控制。完成该操作的过程与访问规则的常规操作过程没有什么不同。安全对象还允许更改所有者,但是操作系统将禁止其他人执行该操作。通常,为了更改所有者,必须具有对象的TakeOwnership权限或者具有特殊的“取得所有权”(Take Ownership)特权更改所有者如下所示(假设你具有这样做的权利):
FileSecurity security = file.GetAccessControl();
security.SetOwner(new NTAccount(@"Administrators\Xuanhun"));
file.SetAccessControl(security);
以上代码中使用SetOwner方法来制定当前文件的所有者为NTAccount类型用户Xuanhun。
此外,还可以查看对象的当前所有者是谁,或者请求将所有者作为安全标识符或Windows NT账户对象返回,如以下代码所示:
SecurityIdentifier sid = (SecurityIdentifier)security.GetOwner(typeof(SecurityIdentifier));
Console.WriteLine(sid.ToString());
NTAccount nta = (NTAccount)security.GetOwner(typeof(NTAccount));
Console.WriteLine(nta.ToString());
以上代码使用两种形式来查看所有者:一种是SecurityIdentifier对象,另一种是NTAccount对象。SecurityIdentifier类表示一个安全标识符 (SID) 并为 SID 提供封送处理和比较操作。NTAccount类表示一个用户或组账户。