potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20&Win API ファイルセキュリティのグループ名またはユーザー名をまとめて取得する

ファイルのセキュリティ記述子に含まれるグループ名またはユーザー名をまとめて取得するコードです。実際には個々のACEから名前を取得して、それをstd::setでまとめています。

#include <array>
#include <string>
#include <set>
#include <vector>

#define STRICT
#define NOMINMAX
#include <Windows.h>

std::wstring GetWindowsDirectoryW()
{
    auto len = ::GetWindowsDirectoryW(nullptr, 0);
    if (len == 0)
        return {};
    std::wstring s(len - 1, L'\0');
    if (::GetWindowsDirectoryW(s.data(), len) == 0)
        return {};
    return s;
}

std::vector<BYTE> GetFileSecurityW(
    std::wstring_view filename,
    SECURITY_INFORMATION requestedInformation)
{
    DWORD needSize;
    if (!::GetFileSecurityW(
        filename.data(),
        requestedInformation,
        nullptr,
        0,
        &needSize))
    {
        if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            return {};
    }

    std::vector<BYTE> v(needSize);
    if (!::GetFileSecurityW(
        filename.data(),
        requestedInformation,
        v.data(),
        needSize,
        &needSize))
    {
        return {};
    }
    return v;
}

struct LookupAccountSidWResult {
    std::wstring name;
    std::wstring referenceDomainName;
    SID_NAME_USE use;
};

LookupAccountSidWResult LookupAccountSidW(
    PSID psid,
    std::wstring_view systemName = {})
{
    DWORD nameLen{};
    DWORD refDomainNameLen{};
    SID_NAME_USE use;
    if (!::LookupAccountSidW(systemName.data(), psid,
        nullptr, &nameLen, nullptr, &refDomainNameLen, &use))
    {
        if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            return {};
    }
    std::wstring name(nameLen - 1, L'\0');
    std::wstring refDomainName(refDomainNameLen - 1, L'\0');
    if (!::LookupAccountSidW(systemName.data(), psid,
        name.data(), &nameLen, refDomainName.data(), &refDomainNameLen, &use))
    {
        return {};
    }
    return LookupAccountSidWResult(std::move(name), std::move(refDomainName), use);
}

PSID GetAceSid(PACE_HEADER pheader)
{
    auto pv{static_cast<PVOID>(pheader)};
    switch (pheader->AceType)
    {
    case ACCESS_ALLOWED_ACE_TYPE:
        return &static_cast<PACCESS_ALLOWED_ACE>(pv)->SidStart;
    case ACCESS_DENIED_ACE_TYPE:
        return &static_cast<PACCESS_DENIED_ACE>(pv)->SidStart;
    case SYSTEM_AUDIT_ACE_TYPE:
        return &static_cast<PSYSTEM_AUDIT_ACE>(pv)->SidStart;
    case SYSTEM_ALARM_ACE_TYPE:
        return &static_cast<PSYSTEM_ALARM_ACE>(pv)->SidStart;
    case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
        return nullptr;
    case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
        return &static_cast<PACCESS_ALLOWED_OBJECT_ACE>(pv)->SidStart;
    case ACCESS_DENIED_OBJECT_ACE_TYPE:
        return &static_cast<PACCESS_DENIED_OBJECT_ACE>(pv)->SidStart;
    case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
        return &static_cast<PSYSTEM_AUDIT_OBJECT_ACE>(pv)->SidStart;
    case SYSTEM_ALARM_OBJECT_ACE_TYPE:
        return &static_cast<PSYSTEM_ALARM_OBJECT_ACE>(pv)->SidStart;
    case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
        return &static_cast<PACCESS_ALLOWED_CALLBACK_ACE>(pv)->SidStart;
    case ACCESS_DENIED_CALLBACK_ACE_TYPE:
        return &static_cast<PACCESS_DENIED_CALLBACK_ACE>(pv)->SidStart;
    case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
        return &static_cast<PACCESS_ALLOWED_CALLBACK_OBJECT_ACE>(pv)->SidStart;
    case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
        return &static_cast<PACCESS_DENIED_CALLBACK_OBJECT_ACE>(pv)->SidStart;
    case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
        return &static_cast<PSYSTEM_AUDIT_CALLBACK_ACE>(pv)->SidStart;
    case SYSTEM_ALARM_CALLBACK_ACE_TYPE:
        return &static_cast<PSYSTEM_ALARM_CALLBACK_ACE>(pv)->SidStart;
    case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
        return &static_cast<PSYSTEM_AUDIT_CALLBACK_OBJECT_ACE>(pv)->SidStart;
    case SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE:
        return &static_cast<PSYSTEM_ALARM_CALLBACK_OBJECT_ACE>(pv)->SidStart;
    case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
        return &static_cast<PSYSTEM_MANDATORY_LABEL_ACE>(pv)->SidStart;
    case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE:
        return &static_cast<PSYSTEM_RESOURCE_ATTRIBUTE_ACE>(pv)->SidStart;
    case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE:
        return &static_cast<PSYSTEM_SCOPED_POLICY_ID_ACE>(pv)->SidStart;
    case SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE:
        return &static_cast<PSYSTEM_PROCESS_TRUST_LABEL_ACE>(pv)->SidStart;
    case SYSTEM_ACCESS_FILTER_ACE_TYPE:
        return &static_cast<PSYSTEM_ACCESS_FILTER_ACE>(pv)->SidStart;
    default:
        return nullptr;
    }
}

#include <format>
#include <iostream>

int main()
{
    std::wcout.imbue(std::locale("", std::locale::ctype));

    auto windir{GetWindowsDirectoryW()};
    auto secutiryDescriptor{::GetFileSecurityW(windir,
        OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION)};

    PACL pdacl;
    BOOL daclPresent;
    BOOL daclDefaulted;
    if (!::GetSecurityDescriptorDacl(secutiryDescriptor.data(), &daclPresent, &pdacl, &daclDefaulted))
    {
        return -1;
    }

    std::set<std::wstring> names;
    for (DWORD aceIndex = 0; aceIndex < pdacl->AceCount; aceIndex++)
    {
        LPVOID pace;
        if (!::GetAce(pdacl, aceIndex, &pace))
        {
            return -1;
        }

        auto paceHeader{static_cast<PACE_HEADER>(pace)};
        auto psidAce{GetAceSid(paceHeader)};
        auto aceSidNames{LookupAccountSidW(psidAce)};

        names.emplace(aceSidNames.name);
    }

    std::wcout << L"Windowsディレクトリのファイルセキュリティグループ名またはユーザー名:" << std::endl;
    for (std::wstring name : names)
    {
        std::wcout << std::format(L"  {}", name) << std::endl;
    }

    return 0;
}