5

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

Community
  • 1
  • 1
Kerido
  • 2,930
  • 2
  • 21
  • 34
  • Have you taken a cursory look at MSDN? What problem did you face? – dirkgently Mar 13 '10 at 12:25
  • ... or even CodeProject? – dirkgently Mar 13 '10 at 12:32
  • I did look on both. The problem with MSDN is that there is no sample I could understand. Looking at functions one by one doesn't give good understanding. It's pretty much the same way when I was learning Win32 UI development -- you look at individual messages/functions/structures and you don't see how they all integrate. The problem with CodeProject is that code samples are mostly in .NET-enabled languages. Do you know any good C++ sample? – Kerido Mar 13 '10 at 12:50
  • Out of curiosity, whose key ACL do you want to change? For which reason? Usually the permissions for registry keys should be left as they are (they inherit from the HKEY_* the "right" permissions). – Matteo Italia Mar 13 '10 at 15:25
  • In my product, KO Approach, users can create a folder where they place shortcuts to items that they access frequently. This folder is stored under the user profile, of course. This whole functionality is called **Approach Items**. During uninstall, I need to display a list of folders that have been created (by all users of a computer) as a reminder that *traces of the application* might still exist. So obviously, I need a list of all **Approach Items** paths stored somewhere. In my case, it's `HKLM\Software\KO Software\Approach`. So I need to make this key writable. – Kerido Mar 13 '10 at 15:33
  • Ok, I agree that it's the best solution; still, I would create under HKLM\Software\KO Software\Approach\Items a subkey for each user, allowing only the creator of them (and obviously the admins) to access them. – Matteo Italia Mar 13 '10 at 16:38

4 Answers4

4

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.

Luke
  • 11,211
  • 2
  • 27
  • 38
2

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.

2

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

InsanityPants
  • 121
  • 2
  • 5
0

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 ... ;
}
alex yi
  • 11
  • 3