Windows2000 服务器端应用程序开发设计指南-存取控制(4)
转自:http://mickorguo.blog.163.com/blog/static/62194802007412282866/
ACL_SIZE_INFORMATION结构最常与GetAclInformation一起使用,其定义如下:
typedef struct _ACL_SIZE_INFORMATION {
DWORD AceCount;
DWORD AclBytesInUse;
DWORD AclBytesFree;
}ACL_SIZE_INFORMATION;
以下的程序代码片段撷取了C:\Test\Text.txt文件之DACL中的ACES数量。
PSECURITY_DESCRIPTOR pSD;
PACL pDACL;
ULONG lErr = GetNamedSecurityInfo(TEXT("C:\\Test\\Test.txt"),
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
&pDACL, NULL, &pSD);
if (lErr != ERROR_SUCCESS){
// 错误实例
}
ACL_SIZE_INFORMATION aclSize = {0};
if(pDACL != NULL){
if(!GetAclInformation(pDACL, &aclSize, sizeof(aclSize),
AclSizeInformation)){
// 错误实例
}
}
ULONG nAceCount = aclSize.AceCount;
说明
假如C:\Test\Test.txt文件存在FAT磁盘上,或是它没有DACL,系统会在pDACL变数中传回NULL。这就是为什么在呼叫GetAclInformation之前测试NULL是很重要的原因。传递NULL指标值到GetAclInformation中,会导致违规存取的情形。
拥有DACL中的ACEs数量后,可以使用这个资讯去撷取个别的ACEs。GetAce的呼叫方式如下。
BOOL GetAce(
PACL pACL,
DWORD dwACEIndex,
PVOID *pACE);
这个函数简单地取得指标,指向ACL、您想要取得ACE以0为基础的索引、以及储存DACL中指向ACE的指标的指标变数位址。
请注意pace参数是PVOID类型。这是因为一些不同的ACE类型属于DACL,而每个类型都由一种不同的结构表示。GetAce被用来撷取每个类型。然而,ACLs可以包含任何数量的ACEs及任何ACE类型的组合,所以在您用GetAce撷取ACE前,不可能知道ACEs的类型。
尽管撷取ACE总数及ACE本身是简单的,研读DACL的真正工作变成去认识不同类型的ACEs。
认识ACEs
有些不同的ACE类型可以被分割成两个广泛的类型:标准ACEs及对象ACEs。您可以在每一组中找到允许、拒绝存取和稽核存取的ACE类型。每个ACE类型必定会分享共同的属性-即其结构第一个成员为ACE_HEADER类型,定义如下:
typedef struct _ACE_HEADER {
BYTE AceType;
BYTE AceFlags;
USHORT AceSize;
}ACE_HEADER;
ACE标头对于读取DACL来说是重要的,因为当您从GetAce撷取指向ACE的指标时,在读取ACE的标头前,并不知道ACE的类型或是组成ACE的结构类型。
如您所猜测的,ACE标头的AceType成员指出了标头代表的ACE类型。表10-10列出了AceType可能的值。这六种ACE类型可在Windows 2000中使用。
| 表10-10 ACE类型 |
| ACE类型 | 叙述 |
|---|---|
| ACCESS_ALLOWED_ACE_TYPE | 在DACL中使用,指出一组明确地授予给信任成员的存取权利,使用ACCESS_ ALLOWED_ACE结构。 |
| ACCESS_DENIED_ACE_TYPE | 在DACL中使用,指出一组明确地拒绝给予信任成员的存取权利,使用ACCESS_ DENIED_ACE结构。 |
| SYSTEM_AUDIT_ACE_TYPE | 在SACL(请参阅〈 稽核及SACL 〉一节)中使用,指出要求一组信任成员时,可能会产生稽核事件的存取权利,使用SYSTEM_AUDIT_ACE结构。 |
| ACCESS_ALLOWED_OBJECT_ACE_TYPE | 在目录服务对象的DACL中使用,指出一组明确授予代表Active Directory中某个对象之信任成员的存取权利,使用ACCESS_ALLOWED_OBJECT_ACE结构。 |
| ACCESS_DENIED_OBJECT_ACE_TYPE | 在目录服务对象的DACL中使用。指出一组明确拒绝给予代表Active Directory中某个对象之信任成员的存取权利,使用ACCESS_DENIED_OBJECT_ACE结构。 |
| SYSTEM_AUDIT_OBJECT_ACE_TYPE | 在目录服务对象的SACL(请参阅〈 稽核及SACL 〉一节)中使用,指出要求一组信任成员时,可能产生稽核事件的存取权利,使用SYSTEM_AUDIT_OBJECT_ ACE结构。 |
ACE_HEADE结构的AceFlags成员包含关于ACE的继承及稽核资讯。AceFlag成员将是表10-11中某些值的组合。图10-2 。
ACE结构的最后一个成员是SidStart,它指出ACE拒绝、允许或稽核存取这个对象的信任成员帐户的SID起点。这个成员需要稍作讨论。
请记得SID(在第九章讨论过)是个可变长度的二进制结构,它指出系统的信任成员帐户。因为每个ACE包含SID,ACE结构也是一个可变长度的结构。SidStart成员是一个包含在ACE中的SID起点的预留位置。SidStart值(及类型)是不恰当且不应该被存取的,因为它实际上是SID结构的第一对位元组。
您可能会发现以下的巨集指令对于从ACE结构取得SID是很有用的:
#define PSIDFromPACE(pACE)((PSID)(&((pACE)->SidStart)))
这个特殊的巨集指令取得指向任何ACE结构的指标-即对象或标准ACE-并且传回指向ACE的SID结构的指标。
标准ACE类型被分成叁种不同的结构,当您为ACL建立ACEs时,主要被用来作为逻辑的预留位置。然而,因为每个ACE结构是完全相同的,从DACL读取ACEs时,常见的情形是只编写使用其中一个ACE类型的程序代码,而不使用ACE_HEADER结构的AceType成员来维护ACE类型。以下的函数使用了此种技巧,并印出DACL中每个ACE的资讯:
void DumpACL( PACL pACL ){
__try{
if (pACL == NULL){
_tprintf(TEXT("NULL DACL\n"));
__leave;
}
ACL_SIZE_INFORMATION aclSize = {0};
if (!GetAclInformation(pACL, &aclSize, sizeof(aclSize),
AclSizeInformation))
__leave;
_tprintf(TEXT("ACL ACE count: %d\n"), aclSize.AceCount);
struct{
BYTE lACEType;
PTSTR pszTypeName;
}aceTypes[6] == {
{ACCESS_ALLOWED_ACE_TYPE, TEXT("ACCESS_ALLOWED_ACE_TYPE")},
{ACCESS_DENIED_ACE_TYPE, TEXT("ACCESS_DENIED_ACE_TYPE")},
{SYSTEM_AUDIT_ACE_TYPE,TEXT("SYSTEM_AUDIT_ACE_TYPE")},
{ACCESS_ALLOWED_OBJECT_ACE_TYPE,
TEXT("ACCESS_ALLOWED_OBJECT_ACE_TYPE")},
{ACCESS_DENIED_OBJECT_ACE_TYPE,
TEXT("ACCESS_DENIED_OBJECT_ACE_TYPE")},
{SYSTEM_AUDIT_OBJECT_ACE_TYPE,
TEXT("SYSTEM_AUDIT_OBJECT_ACE_TYPE")}};
struct{
ULONG lACEFlag;
PTSTR pszFlagName;
}aceFlags [7 ] =={
{INHERITED_ACE, TEXT("INHERITED_ACE")},
{CONTAINER_INHERIT_ACE, TEXT("CONTAINER_INHERIT_ACE")},
{OBJECT_INHERIT_ACE, TEXT("OBJECT_INHERIT_ACE")},
{INHERIT_ONLY_ACE, TEXT("INHERIT_ONLY_ACE")},
{NO_PROPAGATE_INHERIT_ACE, TEXT("NO_PROPAGATE_INHERIT_ACE")},
{FAILED_ACCESS_ACE_FLAG, TEXT("FAILED_ACCESS_ACE_FLAG")},
{SUCCESSFUL_ACCESS_ACE_FLAG,
TEXT("SUCCESSFUL_ACCESS_ACE_FLAG")}};
for (ULONG lIndex = 0; lIndex < aclSize.AceCount; lIndex++){
ACCESS_ALLOWED_ACE* pACE;
if (!GetAce(pACL, lIndex, (PVOID*)&pACE))
__leave;
_tprintf(TEXT("\nACE #%d\n"), lIndex);
ULONG lIndex2 = 6;
PTSTR pszString = TEXT("Unknown ACE Type");
while (lIndex2--){
if(pACE->Header.AceType == aceTypes [lIndex2 ].lACEType){
pszString = aceTypes[lIndex2].pszTypeName;
}
}
_tprintf(TEXT(" ACE Type =\n \t%s\n"), pszString);
_tprintf(TEXT(" ACE Flags = \n"));
lIndex2 = 7;
while (lIndex2--){
if ((pACE->Header.AceFlags &aceFlags [lIndex2 ].lACEFlag)
!=0)
_tprintf(TEXT(" \t%s\n"),
aceFlags[lIndex2].pszFlagName);
}
_tprintf(TEXT(" ACE Mask ((32->0)=\n \t "));
lIndex2 = (ULONG)1<<31;
while (lIndex2){
_tprintf(((pACE->Mask &lIndex2) != 0)?TEXT("1"):TEXT("0"));
lIndex2>>=1;
}
TCHAR szName[1024];
TCHAR szDom[1024];
PSID pSID = PSIDFromPACE(pACE);
SID_NAME_USE sidUse;
ULONG lLen1 = 1024, lLen2 = 1024;
if (!LookupAccountSid(NULL, pSID,
szName, &lLen1, szDom, &lLen2, &sidUse))
lstrcpy(szName, TEXT("Unknown"));
PTSTR pszSID;
if (!ConvertSidToStringSid(pSID, &pszSID))
__leave;
_tprintf(TEXT("\n ACE SID = \n \t%s (%s)\n"), pszSID, szName);
LocalFree(pszSID);
}
}__finally{}
}
ACEs的所有ACE类型倾印了ACCESS_ALLOWED_ACE结构,这个结构对任何标准ACE类型(尽管这个函数对对象ACEs将失去作用)都能作用,因为所有标准ACE结构都相同。
以下的程序片段可以与DumpACL函数一起被用来显示NTFS共享目录中的ACEs:
PSECURITY_DESCRIPTOR pSD;
PACL pDACL;
ULONG lErr = GetNamedSecurityInfo(TEXT("C:\\Test\\Test"),
SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
&pDACL, NULL, &pSD);
if (lErr == ERROR_SUCCESS){
DumpACL(pDACL);
}
说明
DumpACL范例函数说明了如何读取DACL中的ACE资讯。您会在自己编写的程序代码中发现像这样有用的程序代码。您也会发现以DumpACL函数作为除错工具是很有帮助的,尤其是当您开始编写程序代码去修改安全对象的DACL时。在您作了修改的前后,使用它来倾印对象的DACL内容,以查看您的程序代码是否产生了所要求的结果。
这种技巧胜过使用浏览器的安全属性页检视安全性,因为系统的使用者介面不会逐字显示对象的ACE资讯。事实上,使用者介面不会显示没有事先在DACL中适当地安排ACEs顺序的DACL。
因为ACE结构的性质可让您发现它对定义一个表示每个ACE类型的Union很有帮助。于是就一个指向Union类型的指标来说,您可以处理从GetAce传回的ACEs。
以下的程序代码显示一个使用此类技巧的范例:
typedef union _ACE_UNION{
ACE_HEADER aceHeader;
ACCESS_ALLOWED_ACE aceAllowed;
ACCESS_DENIED_ACE aceDenied;
SYSTEM_AUDIT_ACE aceAudit;
}*PACE_UNION;
PACE_UNION pACE ;
GetAce(pDACL, 0, (PVOID*)&pACE);
switch (pACE->aceHeader.AceType){
.
.
.
对象ACEs 除非您要编写保护及修改Active Directory对象安全性的程序代码,否则您不可能会在自己的应用程序中使用对象ACEs。然而,Active Directory是Windows 2000的一个重要元件,了解它保护对象安全的方法并没有害处。我们不会花太多时间在这个主题上,此处的说明只是为了避免混淆您对标准ACEs的认识。
就像标准ACES一样,对象ACEs有叁种结构:拒绝、允许及稽核信任成员存取权利。以下是对象ACEs的结构:
typedef struct _ACCESS_ALLOWED_OBJECT_ACE {
ACE_HEADER Header;
ACCESS_ MASK Mask;
DWORD Flags;
GUID ObjectType;
GUID InheritedObjectType;
DWORD SidStart;
} ACCESS_ALLOWED_OBJECT_ACE, *PACCESS_ALLOWED_OBJECT_ACE;
typedef struct _ACCESS_DENIED_OBJECT_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD Flags;
GUID ObjectType;
GUID InheritedObjectType;
DWORD SidStart;
} ACCESS_DENIED_OBJECT_ACE, *PACCESS_DENIED_OBJECT_ACE;
typedef struct _SYSTEM_AUDIT_OBJECT_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD Flags;
GUID ObjectType;
GUID InheritedObjectType;
DWORD SidStart;
} SYSTEM_AUDIT_OBJECT_ACE, *PSYSTEM_AUDIT_OBJECT_ACE;
就像标准ACEs一样,除了附加的Flags、ObjectType及InheritedObjectType成员外,每个结构类型及结构的成员都相同。
注意不要把对象ACE结构的Flags成员与ACE_HEADER结构的AceFlags成员混淆了。Flags成员可以为0或是表10-12中任何值的组合。
说明
和Win32 SDK中类似的结构不同,表10-12中的标记不指出结构中是否有使用ObjectType及InheritedObjectType成员,而是指出这些成员是否真的存在于结构中!
没有固定成员清单的结构被称为 定形的(amorphous) 。每个对象ACE结构都是无定形的,在Win32 SDK中是非常罕见的结构。这显然是对象ACEs最难处理的一面。

浙公网安备 33010602011771号