簡體   English   中英

使用C ++遞歸掃描目錄上的ACL

[英]Recursive scan for ACLs on directories with C++

我很好奇。 我正在用C ++寫一段代碼,該代碼需要掃描特定文件夾的子文件夾上的ACL權限,並查找與第一個文件夾的ACL權限不同的代碼。 為此,我使用FindFirstFileFindNextFile遞歸檢查文件夾中的所有文件夾,然后通過在找到的每個子文件夾上調用GetNamedSecurityInfo來檢查ACL權限。

該方法行之有效,除了它的運行速度非常慢之外,尤其是在掃描網絡共享時。 我知道稱為accesschk的工具可以做同樣的事情,但是當我在同一文件夾上遞歸運行它時(使用-dsqvli開關),它返回結果的速度比我上面描述的過程更快。

因此,我想知道如何加快此ACL權限的查找過程?

我的第一個想法是在ACE上使用繼承,但我不確定如何實現它。

編輯2:感謝@arx的建議。 這些ACL / ACE資料記錄得很差。 他在下面發布的代碼對我有用。 請注意,由於下面在他的帖子中概述的原因,我用於檢查ACL繼承的原始代碼未產生可靠的結果。

評論提供了一些進一步的信息:

該應用程序確定正在檢查其訪問權限的用戶的SID。

在每個目錄上調用GetNamedSecurityInfo ,應用程序將使用用戶的SID調用GetEffectiveRightsFromAcl 大部分時間都是使用后一種方式。

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目錄上嘗試了此操作。 掃描整個目錄結構需要對GetEffectiveRightsFromAcl進行6次調用。 其余的2708個目錄已從緩存中解析,因此速度更快。

以下實現了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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM