为何Win32 API获取的文件DACL条目与文件属性信息不匹配?
Windows文件权限解析问题排查与解决
1. 获取文件安全信息的代码
ULONG result = GetSecurityInfo(Hfile , SE_FILE_OBJECT , OWNER_SECURITY_INFORMATION |GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION , &sidowner , &sidgroup , &pdacl , NULL , &psd);
2. 提取访问控制条目(ACE)的代码
BOOL b = GetAce(pdacl, i, (LPVOID*)&ace); if (((ACCESS_ALLOWED_ACE*)ace)->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) { sid = (PSID)&((ACCESS_ALLOWED_ACE*)ace)->SidStart; LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse); wcout << "domianName/AccoutName : " << doname << "/" << oname << endl; mask = ((ACCESS_ALLOWED_ACE*)ace)->Mask; cout << "Allowed" << endl; }
3. API输出结果
domianName/AccoutName : BUILTIN/Administrators Allowed DELETE FILE_GENERIC_READ FILE_GENERIC_WRITE FILE_GENERIC_EXECUTE READ_CONTROL WRITE_DAC WRITE_OWNER SYNCHRONIZE domianName/AccoutName : BUILTIN/Administrators Allowed DELETE FILE_GENERIC_READ FILE_GENERIC_WRITE FILE_GENERIC_EXECUTE READ_CONTROL WRITE_DAC WRITE_OWNER SYNCHRONIZE domianName/AccoutName : BUILTIN/Administrators Allowed DELETE FILE_GENERIC_READ FILE_GENERIC_WRITE FILE_GENERIC_EXECUTE READ_CONTROL SYNCHRONIZE domianName/AccoutName : BUILTIN/Users Allowed FILE_GENERIC_READ FILE_GENERIC_WRITE FILE_GENERIC_EXECUTE READ_CONTROL SYNCHRONIZE
4. CACLS命令输出结果
C:\Users\Administrator>cacls e:/hello.txt e:\hello.txt BUILTIN\Administrators:F NT AUTHORITY\SYSTEM:F NT AUTHORITY\Authenticated Users:C BUILTIN\Users:R
存在的问题
- Administrators出现多条重复的ACE条目
- 未找到NT AUTHORITY\SYSTEM和NT AUTHORITY\Authenticated Users的ACE
- 文件属性中Users仅拥有读取权限,但API输出显示其拥有写入权限
问题原因与解决方案
1. 重复ACE条目
重复条目来自继承权限与显式权限的叠加。Windows文件系统中,文件会继承父目录的ACE,同时可添加显式ACE。需通过ACE的AceFlags字段判断是否为继承条目(INHERITED_ACE标志),过滤重复项:
// 在处理ACE时添加判断 if ((allowedAce->Header.AceFlags & INHERITED_ACE) != 0) { // 跳过继承条目或标记为继承 continue; }
2. 缺失SYSTEM和Authenticated Users的ACE
代码仅处理了ACCESS_ALLOWED_ACE_TYPE类型的ACE,需遍历所有ACE类型,同时确保循环覆盖pdacl->AceCount所有索引:
for (DWORD i = 0; i < pdacl->AceCount; i++) { BOOL b = GetAce(pdacl, i, (LPVOID*)&ace); if (!b) continue; switch(ace->Header.AceType) { case ACCESS_ALLOWED_ACE_TYPE: // 处理允许ACE break; case ACCESS_DENIED_ACE_TYPE: // 处理拒绝ACE break; // 可扩展处理其他ACE类型,如审核类ACE } }
3. Users权限显示不符
API返回的Mask是原始权限位,FILE_GENERIC_WRITE是通用权限宏,需用MapGenericMask映射为具体文件权限,同时需考虑拒绝ACE的优先级(拒绝ACE会覆盖允许ACE):
// 映射通用权限到具体权限 FILE_GENERIC_MAPPING fileMapping = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; DWORD specificMask = mask; MapGenericMask(&specificMask, &fileMapping); // 检查实际有效权限,比如写入权限 if (specificMask & FILE_WRITE_DATA) { // 实际拥有写入权限 } else { // 无写入权限 }
另外,LookupAccountSid需正确初始化缓冲区大小,避免因缓冲区不足导致的解析错误:
WCHAR oname[256], doname[256]; DWORD namelen = _countof(oname); DWORD domainnamelen = _countof(doname); SID_NAME_USE peUse; // 第一次调用获取缓冲区大小(若需要) if (!LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse)) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // 分配足够大的缓冲区,此处简化处理 namelen = 256; domainnamelen = 256; LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse); } }
内容的提问来源于stack exchange,提问作者Brighton Joel
相关产品推荐
相关产品推荐

