0

I want to create a file that is:

  • Read-only accessible for all local users.
  • Read-write accessible only when application runs with elevated privileges.

I have found Windows-classic-samples here. I modified it a bit, so it gives the creator full access and everyone else GENERIC_READ:

#include <Accctrl.h>
#include <Aclapi.h>
#include <stdexcept>
#include <filesystem>

#define SCOPE_EXIT_LINE(...) /* Assume ignored. */

void accessImpl(const std::filesystem::path &path, bool readWrite)
{
    PACL pAcl = nullptr;
    DWORD dwAclSize;
    SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_LOCAL_SID_AUTHORITY;
    SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
    PSID pEveryoneSid = nullptr;
    PSID pOwnerSid = nullptr;

    dwAclSize = sizeof(ACL) + 2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)
                + GetSidLengthRequired(1); 
    pAcl = static_cast<PACL>(LocalAlloc(0, dwAclSize));
    if(pAcl == nullptr)
    {
        throw std::runtime_error("Failed to allocate ACL");
    }
    InitializeAcl(pAcl, dwAclSize, ACL_REVISION);
    SCOPE_EXIT_LINE(if(pAcl) LocalFree(pAcl));

    AllocateAndInitializeSid(&siaWorld, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSid);
    SCOPE_EXIT_LINE(if(pEveryoneSid) FreeSid(pEveryoneSid));

    AllocateAndInitializeSid(&siaCreator, 1, SECURITY_CREATOR_OWNER_RID, 0, 0, 0, 0, 0, 0, 0, &pOwnerSid);
    if(AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_READ | (readWrite ? GENERIC_WRITE : 0) | GENERIC_EXECUTE, pEveryoneSid)
       == 0)
    {
        throw std::runtime_error("Failed to set AddAccessAllowedAce");
    }
    if(AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, pOwnerSid) == 0)
    {
        throw std::runtime_error("Failed to set AddAccessAllowedAce");
    }
    const auto pSD = static_cast<PSECURITY_DESCRIPTOR>(LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
    if(pSD == nullptr)
    {
        throw std::runtime_error("Failed to set LocalAlloc SD");
    }
    if(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) == 0
       || SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE) == 0
       || SetFileSecurityW(path.c_str(), DACL_SECURITY_INFORMATION, pSD) == 0) 
    {
        throw std::runtime_error("Failed to set permissions");
    }
}

Then I create the file with elevated privileges. Which seems to create the desired access structure:

enter image description here

But for some reason, writing to the file fails even with elevated privileges. (I tried with PowerShell, NotePad++, etc)

What am I doing wrong?

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • Is the creator an admin user? Running elevated means running as an admin with elevated rights. Which means the process is likely running as a different user when elevated versus non-elevated. Which user is actually creating the file, and which user is trying to write to the file? Window security is based on users, not processes. – Remy Lebeau Oct 31 '22 at 19:33
  • @RemyLebeau I run the executable that creates this file with "Run as Administrator" option. In the screenshot (at the very top), you can see that the "Owner" is "Administrators". Then I try to write with PowerShell, again through "Run as Administrator" from the same account. It fails to write. – Aykhan Hagverdili Oct 31 '22 at 19:36
  • You never granted Administrator write permission. The "CREATOR OWNER" entry doesn't control access to the item. It is a template applied to child items. – Raymond Chen Oct 31 '22 at 20:08
  • @RaymondChen I see. Can you please help me do that? – Aykhan Hagverdili Oct 31 '22 at 20:11
  • simply set `D:P(A;;FA;;;BA)(A;;FR;;;WD)` on file – RbMm Oct 31 '22 at 20:31
  • @RbMm Can you please show me how to do that from C++? – Aykhan Hagverdili Oct 31 '22 at 20:34
  • `ConvertStringSidToSidW` + `SetFileSecurityW` or `SetKernelObjectSecurity`. – RbMm Oct 31 '22 at 20:57
  • @RbMm `ConvertStringSidToSidW(L"D:P(A;;FA;;;BA)(A;;FR;;;WD)", &pSid)` gives me `ERROR_INVALID_SID`. – Aykhan Hagverdili Oct 31 '22 at 21:12
  • Should be ConvertStringSecurityDescriptorToSecurityDescriptor. Or when you call AllocateAllocateAndInitializeSid, create the administrator SID instead of the CREATOR_OWNER SID. – Raymond Chen Oct 31 '22 at 21:15
  • @AyxanHaqverdili - typo. `ConvertStringSecurityDescriptorToSecurityDescriptor` i mean – RbMm Oct 31 '22 at 21:18
  • That worked. Thank you @RbMm and RaymondChen – Aykhan Hagverdili Oct 31 '22 at 21:34

1 Answers1

0

As @RbMm and @RaymondChen show in the comments, this can be done very cleanly:

#include <Windows.h>

#include <Accctrl.h>
#include <Aclapi.h>
#include <sddl.h>
#include <filesystem>

enum class FileAccess { ReadOnly, ReadWrite };

void grantAllAccess(const std::filesystem::path &file, const FileAccess access)
{
    const auto sidString = (access == FileAccess::ReadWrite) ?
        L"D:PAI(A;;0x12019f;;;WD)(A;;FA;;;BA)" : // Everyone Read/Write, Admin full access
        L"D:P(A;;FA;;;BA)(A;;FR;;;WD)";          // Everyone Read only, Admin full access

    PSID pSid = nullptr;
    if(ConvertStringSecurityDescriptorToSecurityDescriptorW(
            sidString, SDDL_REVISION_1, &pSid, nullptr) == 0)
    {
        throw std::runtime_error("Failed to create SID");
    }

    if(SetFileSecurityW(file.c_str(), DACL_SECURITY_INFORMATION, pSid) == 0)
    {
        LocalFree(pSid);
        throw std::runtime_error("Failed to set SID to file");
    }
    
    LocalFree(pSid);
}

See this answer for how to generate these cryptic strings. See this repo to convert these strings to readable text.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • It's normal to build [EXPLICIT_ACCESS](https://learn.microsoft.com/en-us/windows/win32/api/accctrl/ns-accctrl-explicit_access_w) explicitly. see [Taking Object Ownership in C++](https://learn.microsoft.com/en-us/windows/win32/secauthz/taking-object-ownership-in-c--). And as @RaymondChen said, [CREATOR_OWNER, which is used as a placeholder in an inheritable ACE. When the ACE is inherited, the system replaces the CREATOR_OWNER SID with the SID of the object's creator](https://learn.microsoft.com/en-us/windows/win32/secauthz/well-known-sids). – YangXiaoPo-MSFT Nov 01 '22 at 02:59