2

我对某事感到好奇。我正在用 C++ 编写一段代码,它需要扫描特定文件夹的子文件夹的 ACL 权限,并找到与第一个文件夹的 ACL 权限不同的那些。为此,我使用FindFirstFileFindNextFile递归检查文件夹中的所有文件夹,然后通过在找到的每个子文件夹上调用GetNamedSecurityInfo来检查 ACL 权限。

此方法有效,只是它运行速度很慢,尤其是在扫描网络共享时。我知道名为accesschk的工具可以做同样的事情,但是当我在同一个文件夹(带开关)上递归运行它时,-dsqvli它返回的结果比我上面描述的过程更快。

所以我想知道如何加快这个 ACL 权限查找过程?

我的第一个想法是在 ACE 上使用继承,我只是不确定如何实现它......

编辑 2:感谢@arx 的建议。这个 ACL/ACE 东西的文档记录很差。他在下面发布的代码对我有用。请注意,我用于检查 ACL 继承的原始代码没有产生可靠的结果,原因是他在下面的帖子中概述了 @arx 的原因。

4

1 回答 1

5

评论中的一些进一步信息:

应用程序确定正在检查其访问权限的用户的 SID。

调用GetNamedSecurityInfo每个目录后,应用程序GetEffectiveRightsFromAcl使用用户的 SID 进行调用。花费大部分时间的是后一个调用。

GetEffectiveRightsFromAcl根据用户的 SID 和用户所属的任何组的 SID 检查 ACL。它可能很慢,因为确定用户组需要往返域控制器。

有两种可能的修复方法和死胡同:

模拟 GetEffectiveRightsFromAcl

在循环之外,确定用户的 SID 和用户组的 SID。(TODO:检查嵌套组是否自动处理,或者是否必须递归解析。)

要确定 ACL 的有效权限:

  1. 使用 ACCESS_MASK(实际上是 DWORD)来表示权限掩码。将其初始化为零。
  2. 以相反的顺序处理 ACL 中的 ACE。这可确保较早的 ACE 优先。
  3. 如果 ACE 引用任何先前确定的 SID,则对于允许访问的 ACE 或带有您的权限掩码的 ACE 掩码,对于拒绝访问的 ACE,从您的权限掩码中屏蔽掉 ACE 的掩码。

处理完所有 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;
}
于 2013-03-07T22:32:11.390 回答