简单的例子 这部分的重点是谈一个文件对象的安全描述器的创建和应用。这里选择了文件对象作例子的原因是由于它是常见的而且易于理解,你也可以通过文件管理器中的安全编辑器很容易地查看代码的运行结果。不过,如果你的硬盘并不是使用NT的文件系统来格式化的话,这些代码将是没有用处的,而且安全编辑器也是不可用的。你可以采用以下三种方式来处理:
1。重新使用NTFS来格式化硬盘,并且重新安装NT,或者使用convert命令来将你的硬盘转换为NT的文件系统;
2。装入一个新的硬盘,并且使用NT的文件系统来格式化
3。在你的硬盘中分出一小部分(大概10M),并且使用NTFS来格式化,这意味着你将必须重新格式化和重装原有的分区
或者,你可以先等一下,我们还会将同样的代码应用到一个注册键中。无论你使用的是哪种文件系统,你将可以在注册表编辑器中看到安全编辑器。
以下的代码包含了一个创建新文件的程序。为新文件创建的安全描述器设置为只有“guest”的用户才可以使用这个文件,而且该用户只有读的权限。这个程序没有任何的错误检测,因此你很容易看出其中的要点。当你运行代码的时候,它将会在c:\创建一个testfile的文件。在文件管理器中选择该文件,然后选择安全菜单中权限选项。你将会看到列表中只有一个项目:只有“guest”可以读取该文件。在你运行这些代码时,你可以任意改变文件名或者用户的名字。
#include #include
SECURITY_ATTRIBUTES sa; SECURITY_DESCRIPTOR sd; BYTE aclBuffer[1024]; PACL pacl=(PACL)&aclBuffer; BYTE sidBuffer[100]; PSID psid=(PSID) &sidBuffer; DWORD sidBufferSize = 100; char domainBuffer[80]; DWORD domainBufferSize = 80; SID_NAME_USE snu; HANDLE file;
void main(void) { InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); InitializeAcl(pacl, 1024, ACL_REVISION); LookupAccountName(0, "guest", psid, &sidBufferSize, domainBuffer, &domainBufferSize, &snu); AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_READ, psid); SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE);
sa.nLength= sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = &sd;
file = CreateFile("c:\\testfile", GENERIC_READ | GENERIC_WRITE, 0, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0); CloseHandle(file); }
|
首先看看程序的末尾,你将会发现一个对CreateFile的调用,该调用创建了一个名字为c:\testfile的文件。不过,这个调用拥有一个安全的参数,它位于参数列表的第4位。你可能在不少的NT代码中看到这个参数通常为0。0值让操作系统在创建对象的时候,使用一个默认的安全描述器。0值还会禁止继承。上面的代码使用一个SECURITY_ATTRIBUTES来代替,其中包含有一个有效的安全描述器。
安全描述器首先在第一行通过调用InitializeSecurityDescriptor函数创建。(对于本文用到的函数,可见SDK中的Win32帮助文件或者Visual C++的帮助文件得到更多的信息)。这步使用绝对(absolute)的格式创建一个安全描述器(还有第二种称为自相关的格式)。新的安全描述器在初始化时除了修改级别的信息外,没有其它的信息:没有拥有者的标识符,没有组的定义,没有SACL,也没有DACL。
下一行调用InitializeAcl来创建ACL,它将成为该安全描述器的DACL。当InitializeAcl函数返回时,pacl指向一个空的ACL。这就是说,ADL中没有ACE。如果你注释掉下面的两行,这个空的ACL就会被放到安全描述器中,并且这个安全描述器会应用到文件,这时就没有人可以访问到这个文件。这是因为discretionary ACL中没有任何的ACE。如果你不创建DACL,并且在DACL安全步骤中传入一个NULL,这样所有人都可以访问到该文件。
接着的两行创建一个ACE,并且将它加入到ACL中。对于指定的帐号名,LookupAccountName函数返回一个SID。LookupAccountName函数将在系统或者本地系统中查找特定的帐号。如果在本地找不到,它将会在域
控制器或者信任的域控制器中找。该函数返回帐号的SID,如果SID是由域控制器得到的,它还会返回一个域名,以及一个枚举值,该值用来指示帐号的类型:
SidTypeUser SidTypeGroup SidTypeDomain SidTypeAlias SidTypeWellKnownGroup SidTypeDeletedAccount SidTypeInvalid SidTypeUnknown
|
SID是一个安全标识符,它唯一标识系统中的一个用户或者一个组。
由LookupAccountName函数返回的SID被用在一个对AddAccessAllowedAce函数的调用中,该函数用来创建一个访问允许(与访问拒绝相对)ACE,并且将它加入到当前为空的ACL中。
AddAccessAllowedAce 函数创建该SID的ACE和指定的访问掩模,并且将它加入到指定的ACL中。GENERIC_READ访问掩模为文件加入读权限。
现在ACL中包含了一个ACE,并且指定了“guest”用户可以读取该文件。这个ACL需要使用SetSecurityDescriptorDacl函数来放入到安全描述器的Discretionary ACL中。
现在安全描述器中包含了一个有效的DACL,其中有一个ACE。程序将这个安全描述器放到一个安全属性结构体中,并且将它传送给CreateFile函数。
如果你将前面的三行代码注释掉,并且在调用SetSecurityDescriptorDacl时,将DACL的pacl用NULL代替,这时任何人都可以访问该文件。没有DACL意味着任何人可以访问该文件。
编译并运行以上的代码。在你运行前,你要确保程序中指定的文件名在系统中并不存在。通过文件管理器的安全编辑器查看该新文件时,你将会发现它的权限与代码中设置得一样。
你可以很容易地修改这些代码,以加深对安全描述器的了解。例如,你可以尝试给某个组写的权限,或者给DACL加入几个ACE,或者使用AddAccessDeniedAce创建一个访问拒绝ACE。要确保访问拒绝ACE放在访问允许ACE的前面。使用SetSecurityDescriptorOwner函数来修改安全描述器中的拥有者。