建议您执行这个服务并从多重使用者环境下启动客户端程序,或许可以使用封装在Windows内的RunAs.exe公用程序执行。这可让您实验对象所有权及存取控制的部份。
客户端程序非常小,几乎已经把所有的功能皆委托给服务了。所有安全性的程序代码(除了ACL编辑外)皆在服务端实作。
服务使用 模拟(impersonation) 以取得每个连接客户端的权杖(关于模拟的内容在第十一章有更详细的讨论),然后服务会为来自客户端之未来的安全性要求,以及在机器上建立的使用权储存权杖。服务使用了CreatePrivate ObjectSecurity、DestroyPrivateObjectSecurity及AccessCheck,以及许多与安全性相关的函数来实作。
除了安全性功能以外,服务使用了第二章所讨论的I/O完成连接埠,有效地与客户端通讯,并为可调整的通讯实作了一个模组。
稽核与SACL
最后,我们准备开始讨论稽核和SACL的内容。与DACL不同的是,对象的SACL不影响可存取对象的人。
当对象被要求存取时,SACL会产生一个被加入事件日志的事件。特别的是,您可以增加ACEs到对象的SACL中,两种情况下会增加事件到事件日志中:
- 当任何一组存取权利的存取检查成功时。
- 当任何一组存取权利的存取检查失败时。
举例来说,您可以为NTFS分割区上包含个别SYSTEM_AUDIT_ACE的文件建立一个SACL。这表示若发生因为拒绝存取而使每次某个信任成员写入文件失败的情形时,应该由系统将此事件加到事件日志中。
Windows 2000的预设稽核选项是失效的,所以如果您想要开始编写稽核软件,应该加入稽核的能力。以下即是赋予稽核至系统的步骤:
- 使用 /a参数mmc /a,执行Microsoft管理主控台(MMC)。
- 在主控台功能表上,选择新增/移除嵌入式管理单元,然后点选新增按钮。
- 在新增独立嵌入式管理单元对话方块中,选择群组原则,然后点选新增按钮。
- 在选取群组对象的对话方块中,应选取本机电脑,然后点选完成按钮。
- 点选关闭按钮,然后再点选确定钮。此时本机电脑原则对象应该被显示在MMC中。
- 以下展示本机电脑的原则对象:本机电脑原则\电脑设定\Windows设定\安全性设定\本机原则\稽核原则。
- 从右边窗格的稽核对象存取上点滑鼠右键,然后选择安全性钮。
- 在本机安全性原则设定对话方块中,如图10-10所示,核取成功及失败核取方块,然后点选确定钮。

| 图10-10 授予成功及失败稽核给对象存取 |
在采用这些步骤之后,系统会开始增加稽核事件到安全性日志下的事件日志。
说明
网域原则可以覆盖您的本机原则,并阻止稽核被设定。您必须有网域管理的权利才能调整网域原则。
相不相信,您已经完成实作稽核最难的工作。您知道如何从我们处理DACLs的工作中建立ACEs及修改ACLs。目前为止,您已经学会应用于建立ACEs及SACLs的每件事。
以下是SACL使用的ACE结构:
typedef struct _SYSTEM_AUDIT_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
}SYSTEM_AUDIT_ACE;这个结构看起来应该很熟悉,因为它与我们已经讨论过的ACCESS_ALLOWED_ACE及ACCESS_DENIED_ACE结构完全一样。ACE_HEADER结构的AceFlags成员通常会包含继承资讯,稽核ACEs也确实为真。而您也该含入SUCCESSFUL_ACCESS_ACE_FLAG及FAILED_ACCESS_ACE_FLAG稽核标记的其中一或两个,并分别指出成功及失败的存取稽核。
为了修改对象的SACLs,可以使用您已经熟悉的函数,例如InitializeAcl及AddAce。为仍旧使用GetSecurityInfo及SetSecurityInfo处理的系统对象取得及设定SACL使用几乎相同的方式,您可以将它们使用在DACL上。建立一个SACL比建立DACL稍微简单,由于SACL中的ACEs顺序在对象上没有影响,因此您可以用任何方便的顺序增加ACEs。
说明
处理DACLs时要注意一个差异点,那就是信任成员不能设定对象的SACL,除非它拥有SE_AUDIT_NAME权限。
设计存取控制程序应考虑的因素
您现在已经了解什么样的存取控制是合适的,以及如何在您自己的软件中实作存取控制,然而您还要学习更多,以成为一个成功的安全性程序设计师-Windows存取控制的弹性使安全性开发人员因为太多规则而绑住了自己。本节会讨论一些在做安全性程序开发时应考虑的议题,在此对您的劝告是尽早计划您服务的存取控制,提前做计划会替您省去大量的麻烦。
内存管理
和在百分之九十的应用层程序设计不同的是,安全性程序设计需要大量的缓冲器管理机制,包括分配及移动内存内的结构。尽管这是许多开发人员在学校专注的部分,但它并不是Windows开发中常见的工作。
在处理安全性时,您应该提供简化内存管理的方法,因为有很多函数皆要求分配及重分配暂时缓冲器。以下是一些秘诀:
- 考虑使用不需要释放内存的函数,例如_alloca。
- 考虑编写一个类别,例如CautoBuf类别,此类别在附录B中讨论(您也会在附录B中找到范例应用程序),这个类别会自动稽核它的缓冲器,并在它离开范围时释放它自己。
- 避免使用static缓冲器;它们是懒惰程序设计师的技巧,最后将会破坏您的应用程序。
- 考虑手边的工作,并设计一个尽可能要求少数缓冲器分配的方法。尽可能在开始演算时放置大量的缓冲器分配机制。
- 在您使用后利用结构化的例外处理清理它。
重复利用的程序代码
编写越多的程序代码,就会有越多的错误。就存取控制程序来说,这绝对是真的。试着以服务的不同观点来设计彼此一致的安全性,以使您依据某些样式及重复使用函数,这种函数越多越好。重复使用的机制会大大地减轻侦错程序代码的工作。
最后,我无法充分的表达出用一组小的C++ 类别实作您的安全性程序代码是多么有利,这么做会使您换取重复使用的程序代码及封装(Encapsulation),并且可以大大地简化存取控制这类的工作。
保持简单
简单的安全性往往是最好的安全性。假如一个对象拥有许多ACEs,您的软件及使用者要监视确实有存取对象的存取时可能会有困难。假如对象只有一些简单的ACEs,则软件和使用者会很清楚谁做了存取的动作。一个被系统拥有且只有一个空的DACL对象(不是NULL DACL)和从Windows中取得的对象一样简单及安全。增加一个个别的ACE可让信任成员存取及对象仍旧是安全的,并且可以很清楚谁拥有对象的存取权。
试着只使用允许存取的ACEs来实作大量的存取控制。请记得除非明确地允许存取权,否则即表示它被暗示性地拒绝。若您使用允许存取ACEs及拒绝存取ACEs时,即表示允许群组及个人的存取权以及拒绝个人及较小群组的存取权。这样一来,您将会发现自己不必为群组中的个别使用者覆盖群组的拒绝存取ACE。所以尽量使它保持简单以避免存取控制产生漏洞。
使用预设安全性及继承的安全性
假如有可能的话,请使用预设安全性及继承的安全性。假如您的服务可以避免在个别对象上操作ACLs,这样会很好!许多服务可以只建立个别的DACL并将它设定为服务的预设DACL(第十二章有更详细的讨论)。
由服务建立的对象可以使用预设安全性,以避免为每个建立的对象产生一个DACL的需求。这个方法可以大大地简化必须建立许多安全对象的服务。
说明
继承安全性的对象不使用来自预设DACL的ACEs。您会发现如果要使用子对象的预设安全性时,必须保护父目录或登录机码的安全描述项。
继承的安全性是另一个取得免费DACL的方法。您可以设定文件结构(或登录树)中个别父对象上的DACL,在节点下建立的每个对象皆可以继承全部DACL的ACEs。这是一个非常有效的技巧,它可以使您的程序代码免于建立大量的个别DACLs。
说明
不可继承ACEs的这些对象本身即可以拥有可继承的ACEs。所以您可以建立一个受保护的根目录或登录机码,以避免使用继承的ACEs,而对象的ACEs可以为下面的每个对象定义继承的安全性。这是一个在文件及登录机码上实作继承安全性之常见且有效的策略。
