我对某事感到好奇。我正在用C ++编写一段代码,需要扫描特定文件夹子文件夹的ACL权限,并找到与第一个文件夹的ACL权限不同的代码。为此,我使用FindFirstFile和FindNextFile递归检查文件夹中的所有文件夹,然后通过在找到的每个子文件夹上调用GetNamedSecurityInfo来检查ACL权限。
此方法有效,但工作速度非常慢,尤其是在扫描网络共享时。我知道名为accesschk的工具可以做同样的事情,但是当我在同一个文件夹(使用-dsqvli
开关)上递归运行时,它返回的结果比我上面描述的程序更快。< / p>
所以我想知道如何加快此ACL权限查找过程?
我的第一个想法是在ACE上使用继承,我只是不确定如何实现它......
编辑2:感谢@arx的建议。这个ACL / ACE的内容很难记录。他在下面发布的代码为我工作。请注意,我检查ACL继承的原始代码没有产生可靠的结果,因为他的帖子中列出了@arx的原因。
答案 0 :(得分:4)
评论中的一些进一步信息:
应用程序确定正在检查其访问权限的用户的SID。
在每个目录上调用GetNamedSecurityInfo
后,应用程序使用用户的SID调用GetEffectiveRightsFromAcl
。大部分时间都是后者的召唤。
GetEffectiveRightsFromAcl
根据用户的SID和用户所属的任何组的SID检查ACL。它可能很慢,因为确定用户的组需要往返于域控制器。
有两种可能的修复和死胡同:
模拟GetEffectiveRightsFromAcl
在循环外部,确定用户的SID和用户组的SID。 (TODO:检查是否自动处理嵌套组,或者是否必须递归解析它们。)
确定ACL的有效权利:
一旦您处理了所有ACE,您的版权掩码就会得到答案。
跳过继承的ACL
在许多目录层次结构中,大多数或所有文件和目录都将从其父级继承其权限。但是,这没有用。父级的继承ACL可能不活动,因此子级的有效权限与父级的有效权限不匹配。因此,即使继承了ACL,仍然必须进行检查。
缓存GetEffectiveRightsFromAcl的结果
只需创建从ACL到有效权限掩码的映射。为此,您需要一种比较ACL的方法。您不能仅使用memcmp比较整个ACL,因为ACL.AclSize包含额外填充的大小。相反,比较ACE的数量,如果它们相同,则使用memcmp比较各个ACE。
我在Program Files
目录上尝试了这个。扫描整个目录结构需要6次调用GetEffectiveRightsFromAcl
。其余的2,708个目录是从缓存中解析出来的,所以速度要快得多。
以下实现了GetEffectiveRightsFromAcl
的缓存版本。请注意,缺少错误处理,它永远不会释放它放在地图中的PACL。
// Compare two access-control lists.
// Return <0 if acl1<acl2, 0 if acl1==acl2 and >0 if acl1>acl2.
// The ordering is arbitrary but consistent.
int aclcmp(PACL acl1, PACL acl2)
{
// First compare by number of ACEs
int c = acl1->AceCount - acl2->AceCount;
if (c)
return c;
// We have the same number of ACEs, so compare each ACE
int aceCount = acl1->AceCount;
for (int aceIndex = 0; aceIndex != aceCount; ++aceIndex)
{
// Get the ACEs
PACE_HEADER ace1;
PACE_HEADER ace2;
GetAce(acl1, aceIndex, (LPVOID*)&ace1);
GetAce(acl2, aceIndex, (LPVOID*)&ace2);
// Compare the ACE sizes
c = ace1->AceSize - ace2->AceSize;
if (c)
return c;
// Compare the ACE content
c = memcmp(ace1, ace2, ace1->AceSize);
if (c)
return c;
}
return 0;
}
// Less-than operator for pointers to ACLs
class ComparePAcl
{
public:
bool operator()(const PACL& acl1, const PACL& acl2) const
{
return aclcmp(acl1, acl2) < 0;
}
};
// Map from pointers-to-ACLs to access masks
typedef std::map<PACL, ACCESS_MASK, ComparePAcl> AclToAccessMask;
AclToAccessMask aclToAccessMask;
// Just to check how the cache performs
DWORD foundCount = 0;
DWORD notFoundCount = 0;
// Same as GetEffectiveRightsFromAcl but caches results.
// Note that this must be called with the same trustee to get meaningful results.
DWORD CachedGetEffectiveRightsFromAcl(PACL pacl, PTRUSTEE pTrustee, PACCESS_MASK pAccessRights)
{
AclToAccessMask::const_iterator it = aclToAccessMask.find(pacl);
if (it != aclToAccessMask.end())
{
// The ACL is in the cache
++foundCount;
*pAccessRights = it->second;
}
else
{
// The ACL is not in the cache
++notFoundCount;
DWORD rc = GetEffectiveRightsFromAcl(pacl, pTrustee, pAccessRights);
if (rc != ERROR_SUCCESS)
return rc;
// TODO: Clean up copies of ACLs afterwards
PACL aclcopy = (PACL)malloc(pacl->AclSize);
memcpy(aclcopy, pacl, pacl->AclSize);
aclToAccessMask.insert(AclToAccessMask::value_type(aclcopy, *pAccessRights));
}
return ERROR_SUCCESS;
}