Kerido
Kerido

Reputation: 2940

Programmatically allow write access for a Registry key

I need to programmatically modify the Access Descriptors on a known Registry key during product installation. The way I want it to work is:

  1. The installer is run in Administrative mode.
  2. A Registry key is created.
  3. A function (the one I need) queries the ACL from the key.
  4. If this function finds that the group 'Users' already has write access, nothing should be done.
  5. If not, it should add a new permission allowing write access to the 'Users' group.
  6. The permissions are saved for the Registry key.

This question is similar to Setting Registry key write permissions using .NET, however, I need a C++/Win32 implementation.

Thanks in advance

Upvotes: 5

Views: 9022

Answers (4)

alex yi
alex yi

Reputation: 11

Sup, hope OP is still interested in the answer. Here is the working code adding ACEs to ACLs, it may be used to add ACEs to registry or filesystem DACLs. I haven't tried it with anything else yet. As you may notice, no nasty RegGetKeySecurity or manual ACL composing needed. There's even no need to RegOpenKeyEx. For more info, check this MS doc.

UPD Of course it will need admin rights for execution.

// sk - alloced string / path to needed key
// common look: MACHINE\\Software\\... where MACHINE == HKEY_LOCAL_MACHINE
// google for more address abbrevations
PSECURITY_DESCRIPTOR pSD = 0;
EXPLICIT_ACCESS ea;
PACL pOldDACL = 0, pNewDACL = 0;
if (ERROR_SUCCESS == GetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, &pOldDACL, 0, &pSD)) {
    memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
    ea.grfAccessPermissions = KEY_ALL_ACCESS;
    ea.grfAccessMode = GRANT_ACCESS;
    ea.grfInheritance = NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    ea.Trustee.ptstrName = <USERNAME HERE>; //DOMAIN\\USERNAME
    if (ERROR_SUCCESS == SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) {
        if (ERROR_SUCCESS == SetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, pNewDACL, 0)) {
            if (pSD != 0) LocalFree((HLOCAL)pSD);
            if (pNewDACL != 0) LocalFree((HLOCAL)pNewDACL);
            SAFE_FREE(sk);
            // WE'RE GOOD!
            return ... ;
        } else {
            if (pSD) LocalFree((HLOCAL)pSD);
            if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
            SAFE_FREE(sk);
            // SetNamedSecurityInfo failed
            return  ... ;
        }
    } else {
        if (pSD) LocalFree((HLOCAL)pSD);
        if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
        SAFE_FREE(sk);
        // SetEntriesInAcl failed
        return ... ;
    }
} else {
    if (pSD) LocalFree((HLOCAL)pSD);
    SAFE_FREE(sk);
    // GetNamedSecurityInfo failed
    return ... ;
}

Upvotes: 0

InsanityPants
InsanityPants

Reputation: 121

Just to expand on Mikhail Vorotilov's answer, and also drawing inspiration from the example code at https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl

bool RegistryGrantAll(HKEY hKey)
{
    bool bResult = false;

    PSECURITY_DESCRIPTOR sd = nullptr;

    const TCHAR* szSD =
        TEXT("D:")                  // Discretionary ACL
        TEXT("(D;OICI;KA;;;BG)")    // Deny access to built-in guests
        TEXT("(D;OICI;KA;;;AN)")    // Deny access to anonymous logon
        TEXT("(A;OICI;KRKW;;;AU)")  // Allow KEY_READ and KEY_WRITE to authenticated users ("AU")
        TEXT("(A;OICI;KA;;;BA)");   // Allow KEY_ALL_ACCESS to administrators ("BA" = Built-in Administrators)

    if (ConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)szSD, SDDL_REVISION_1, &sd, 0))
    {
        auto result = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, sd);
        if (ERROR_SUCCESS == result)
            bResult = true;
        else
            SetLastError(result);

        // Free the memory allocated for the SECURITY_DESCRIPTOR.
        LocalFree(sd);
    }

    return bResult;
}

If the function returns false then call GetLastError() for more information on the failure cause.

Code compiles on VS2019 and appears to work.
I have not added code to check that hKey is a valid registry handle.

Edit: I've edited this a few times following testing. Sorry about all the edits. What I ended up with was far closer to Mikhail's answer than I started with.

Links to further info:

https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl

https://learn.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw

https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings

Upvotes: 2

Mikhail Vorotilov
Mikhail Vorotilov

Reputation: 21

The smallest code to grant access consists of 3 API calls. It gives full access to the given hkey for all authenticated users and administrators.

This snippet does not contain proper error handling and reporting. Do not copy/paste it into the production code.

    PSECURITY_DESCRIPTOR sd = nullptr;
    ULONG sd_size = 0;
    TCHAR* rights = TEXT( "D:" )                // Discretionary ACL
                  TEXT( "(A;OICI;GA;;;AU)" )    // Allow full control to all authenticated users
                  TEXT( "(A;OICI;GA;;;BA)" );   // Allow full control to administrators

    ConvertStringSecurityDescriptorToSecurityDescriptor( rights, SDDL_REVISION_1, &sd, &sd_size );
    RegSetKeySecurity( hkey, DACL_SECURITY_INFORMATION, sd );
    LocalFree( sd );

Detecting if "Users" have write access to the key might be more difficult than expected. I ended up with writing a test value to the registry and checking the result of that write.

Upvotes: 2

Luke
Luke

Reputation: 11421

For getting and setting the ACL of the key you need to use RegGetKeySecurity and RegSetKeySecurity. Then you need to iterate through the ACEs, examining any that apply to the "Users" group SID. Then you'll either modify/remove the existing one and/or add a new one. Be advised that working with ACLs in plain old Win32 C is a pain.

Upvotes: 4

Related Questions