为什么LookupAccountSid使用C在Win32 API中提供奇怪的安全描述符数据?

时间:2018-10-04 22:59:52

标签: c winapi

我正在尝试使用本机LookupAccountSid WinAPI函数为Windows中的每个SACL实例(用于安全审核)获取正确的SID(主名称)。

请参阅下面的屏幕快照,其中是我要测试的一个示例文件:

enter image description here

但是我的代码产生了如下所示的错误信息:

enter image description here

下面是应该发生的事情。

sidName = Everyone, size = 9
sidName = Everyone, size = 9
sidName = trevm, size = 6

我认为我已经仔细遵循了MSDN文档,但是没有帮助吗?

https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw

我的代码可以使用GetSecurityDescriptorSacl毫无疑问地获得安全描述符,但是LookupAccountSid有问题。我出了什么问题?下面是我的C代码。有人知道为什么吗?

int getSACLinfo(HANDLE rToken, SE_OBJECT_TYPE objectType, ACEVALUE*& aceRef, int* count)
{
    int arrSize = 0;
    int retVal = ERROR_SUCCESS;
    PACL sacl = NULL;
    SYSTEM_AUDIT_ACE* ace = NULL;
    PSECURITY_DESCRIPTOR pSS = NULL;
    PSID sid;
    PSID sidOwner;
    BOOL saclPresent;
    BOOL saclDefaulted;
    wchar_t sidName[512];
    wchar_t domainName[512];

    ULONG result = GetSecurityInfo(rToken, objectType,
        OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
        &sidOwner, NULL, NULL, &sacl, &pSS);

    if (result == ERROR_SUCCESS)
    {
        /* Get SACL security descriptor */
        GetSecurityDescriptorSacl(pSS, &saclPresent, &sacl, &saclDefaulted);

        if (saclPresent && (sacl != NULL))
        {
            for (int i = 0; i < sacl->AceCount; i++)
            {
                GetAce(sacl, i, (PVOID*)&ace);

                /* Get SID from Ace */
                sid = (PSID)&ace->SidStart;

                if (IsValidSid(sid) && ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE)
                {
                    arrSize++;
                }
            }

            // Allocate memory for the data structures to be exported
            aceRef = (ACEVALUE*)malloc(arrSize * sizeof(ACEVALUE));

            if (arrSize > 0 && aceRef != NULL)
            {
                size_t len;
                wchar_t* buf;
                DWORD namelen;
                DWORD domainnamelen;
                SID_NAME_USE peUse;

                for (int i = 0; i < sacl->AceCount; i++)
                {
                    GetAce(sacl, i, (PVOID*)&ace);

                    sid = (PSID)&ace->SidStart;

                    if (IsValidSid(sid) && ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE)
                    {
                        LookupAccountSid(NULL, sid, sidName, &namelen, domainName, &domainnamelen, &peUse);

                        wprintf(L"sidName = %s, size = %d\n", sidName, namelen);

                        len = wcslen(sidName) + 1;
                        buf = (wchar_t*)malloc(len * sizeof(wchar_t));

                        if (buf != NULL)
                        {
                            wcscpy_s(buf, len, sidName);
                            aceRef[i].Name = buf;
                        }

                        aceRef[i].AceMask = ace->Mask;
                        aceRef[i].AceType = ace->Header.AceType;
                        aceRef[i].AceFlags = ace->Header.AceFlags;
                    }
                }
            }
        }
    }

    else
    {
        retVal = GetLastError();
    }

    *count = arrSize;

    if (pSS != NULL)
    {
        LocalFree((HLOCAL)pSS);
        wprintf(L"FREED!\n");
    }

    return retVal;
}

1 个答案:

答案 0 :(得分:0)

更新:下面是纠正我的错误的更新代码。经过进一步研究,我发现问题是由于我没有在原始代码中为两个变量: namelen domainnamelen 指定缓冲区大小(感谢@RbMm和@jwdonahue指出)。

DWORD getSACLinfo(HANDLE rToken, SE_OBJECT_TYPE objectType, ACEVALUE*& aceRef, int* count)
{
    size_t len;
    int arrSize = 0;
    wchar_t* buf;
    wchar_t* sidName = NULL;
    wchar_t* domainName = NULL;
    PSID sid;
    PSID sidOwner;
    PACL sacl = NULL;
    DWORD retValue;
    DWORD dwRevision;
    DWORD namelen;
    DWORD domainnamelen;
    SYSTEM_AUDIT_ACE* ace = NULL;
    PSECURITY_DESCRIPTOR pSS = NULL;
    SECURITY_DESCRIPTOR_CONTROL sdControl;

    /* Get SACL security descriptor */
    retValue = GetSecurityInfo(rToken, objectType,
        OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
        &sidOwner, NULL, NULL, &sacl, &pSS);

    if (retValue == ERROR_SUCCESS)
    {
        if (GetSecurityDescriptorControl(pSS, &sdControl, &dwRevision) == FALSE)
        {
            retValue = ERROR_INVALID_FUNCTION;
        }

        else if (sacl != NULL && (sdControl & SE_SACL_PRESENT) == SE_SACL_PRESENT)
        {
            for (int i = 0; i < sacl->AceCount; i++)
            {
                GetAce(sacl, i, (PVOID*)&ace);

                /* Get SID from Ace */
                sid = (PSID)&ace->SidStart;

                if (IsValidSid(sid) && ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE)
                {
                    arrSize++;
                }
            }

            // Allocate memory for the data structures to be exported
            aceRef = (ACEVALUE*)malloc(arrSize * sizeof(ACEVALUE));

            if (arrSize > 0 && aceRef != NULL)
            {
                for (int i = 0; i < sacl->AceCount; i++)
                {
                    GetAce(sacl, i, (PVOID*)&ace);

                    sid = (PSID)&ace->SidStart;

                    if (IsValidSid(sid) && ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE)
                    {
                        // ADDED TO FIX THE PROBLEM
                        getNameBySID(sid, sidName, &namelen, domainName, &domainnamelen);

                        wprintf(L"size = %d, Owner = %s\n", namelen, sidName);

                        len = wcslen(sidName) + 1;
                        buf = (wchar_t*)malloc(len * sizeof(wchar_t));

                        if (buf != NULL)
                        {
                            wcscpy_s(buf, len, sidName);
                            aceRef[i].Name = buf;
                        }

                        aceRef[i].AceMask = ace->Mask;
                        aceRef[i].AceType = ace->Header.AceType;
                        aceRef[i].AceFlags = ace->Header.AceFlags;
                    }
                }
            }
        }
    }

    else
    {
        retValue = GetLastError();
    }

    *count = arrSize;

    // Memory deallocations
    free(sidName);
    free(domainName);
    LocalFree((HLOCAL)pSS);

    wprintf(L"FREED!\n");

    return retValue;
}

要修复,我添加了一个新函数getNameBySID,以便为缓冲区 sidName domainName 提供正确的内存空间。该逻辑仅涉及一个do-while循环以继续迭代,直到满足LookupAccountSid的要求为止,此时这两个缓冲区的缓冲区大小正确。如果先前的缓冲区大小太小而无法填充缓冲区,它将释放先前分配的缓冲区,然后以正确的大小重新分配并重试。

DWORD getNameBySID(PSID sid, wchar_t *&oNameBuf, DWORD *owNameSize, wchar_t *&dNameBuf, DWORD *dnNameSize)
{
    const DWORD DEFAULT_SIZE = 256;
    const wchar_t NONAME[] = L"Unknown";

    bool status;
    SID_NAME_USE peUse;
    DWORD retValue;
    DWORD ownerBufSize = *owNameSize;
    DWORD domainBufSize = *dnNameSize;

    // Create buffers that may be large enough
    if (oNameBuf == NULL || dNameBuf == NULL)
    {
        ownerBufSize = domainBufSize = DEFAULT_SIZE;

        oNameBuf = (wchar_t*)malloc(ownerBufSize * sizeof(wchar_t));
        dNameBuf = (wchar_t*)malloc(domainBufSize * sizeof(wchar_t));

        if (oNameBuf == NULL)
        {
            return ERROR_INSUFFICIENT_BUFFER;
        }

        if (dNameBuf == NULL)
        {
            free(oNameBuf);
            return ERROR_INSUFFICIENT_BUFFER;
        }

        *owNameSize = ownerBufSize;
        *dnNameSize = domainBufSize;

        memset(oNameBuf, 0, ownerBufSize * sizeof(wchar_t));
        memset(dNameBuf, 0, domainBufSize * sizeof(wchar_t));
    }

    do
    {
        status = (LookupAccountSid(NULL, sid, oNameBuf, owNameSize, dNameBuf, dnNameSize, &peUse) == TRUE);

        if (!status)
        {
            retValue = GetLastError();

            if (*owNameSize > ownerBufSize)
            {
                // Reallocate memory for the buffer and try again.
                //wprintf(L"The account name buffer was too small. It will be reallocated.\n");
                free(oNameBuf);

                oNameBuf = (wchar_t*)malloc(*owNameSize * sizeof(wchar_t));

                if (oNameBuf == NULL)
                {
                    return ERROR_INSUFFICIENT_BUFFER;
                }

                ownerBufSize = *owNameSize;
                memset(oNameBuf, 0, ownerBufSize * sizeof(wchar_t));
            }

            else if (*dnNameSize > domainBufSize)
            {
                // Reallocate memory for the buffer and try again.
                //wprintf(L"The domain name buffer was too small. It will be reallocated.\n");
                free(dNameBuf);

                dNameBuf = (wchar_t*)malloc(*dnNameSize * sizeof(wchar_t));

                if (dNameBuf == NULL)
                {
                    return ERROR_INSUFFICIENT_BUFFER;
                }

                domainBufSize = *dnNameSize;
                memset(dNameBuf, 0, domainBufSize * sizeof(wchar_t));
            }

            else if (retValue == ERROR_NONE_MAPPED)
            {
                // A name could not be found for the SID due to an unexpected error occurred.
                if (ownerBufSize > wcslen(NONAME))
                {
                    wcscpy_s(oNameBuf, wcslen(NONAME) + 1, NONAME);
                    oNameBuf[wcslen(NONAME)] = L'\0';
                }

                dNameBuf[0] = L'\0';
                break;
            }

            else
            {
                wprintf(L"LookupAccountSid failed. GetLastError returned: %d\n", retValue);
                break;
            }
        }

    } while (!status);

    *owNameSize = ownerBufSize;
    *dnNameSize = domainBufSize;

    return (status ? ERROR_SUCCESS : retValue);
}

基本上,我的getSACLinfo函数基本相同,除了调用getNameBySID来获取这两个名称的正确大小的缓冲区外。

下面将产生预期的信息。

enter image description here