An application running as a non-admin in Windows 7 has read/write permissions to HKEY_LOCAL_MACHINE
, but apparently not in Windows 10.
A lot of applications store stuff in HKEY_LOCAL_MACHINE
which are usually meant to be for all users, and they work fine (like the Visual studio) without being run as an admin, but not my application.
How can I make my own application access HKEY_LOCAL_MACHINE
without having to run it as an admin user?
The issue is that RegCreateKeyEx()
and RegOpenKeyEx()
return ERROR_ACCESS_DENIED
in Windows 10, when I run the application as a standard user, but they work fine if I run it as an admin user. I don't want to run the application as an admin just for this.
Update
Based on the comments, a non-admin application only has read access to HKLM and I confirmed that with calling application with only READ writes and they can read but it is noted that windows 7 also doesn't have write access but my test application does work fine in Windows 7. Here is the code
void CRegistryPermissionsView::OnBnClickedButtonRegkey()
{
CRegKey regKey;
LRESULT ret = 0;
CString strKey = _T("Software\\ATestApp\\TestAppNode");
ret = regKey.Open(HKEY_LOCAL_MACHINE, strKey, KEY_ALL_ACCESS);
if (ret == ERROR_FILE_NOT_FOUND)
ret = regKey.Create(HKEY_LOCAL_MACHINE, strKey);
if (ret == ERROR_SUCCESS)
{
ret = regKey.SetStringValue(_T("Entry"), _T("EntryValue"));
if (ret == ERROR_SUCCESS)
{
AfxMessageBox(_T("Success"));
}
}
}
I don't know why the above works in Windows 7 but my main question is where should we store global settings that apply to all users in Windows 10?
Yes, I can create the registry entries through installer which has admin rights but my application would still want to update some of the settings! Do we have to store this locally on a drive now?
All the configuration in the application needs to be applicable to all users.
As I said in my comments above, what you have to do here is to create whatever keys you need in HKLM in your installer (which needs to run elevated) and then change the protection on them so that they can be accessed by an app running with standard permissions (i.e., as a member of group BUILTIN\Users
, and running non-elevated).
Here's the code to 'unprotect' a registry key. Code written in Notepad so may not quite compile and it's obviously a bit hacky, sorry.
Utility function BuildAce (was missing originally, sorry about that):
// Build an access allowed, access denied, system alarm or system audit ACE. Caller must free.
ACE_HEADER * BuildACE (PSID sid, int ace_type, int ace_flags, ACCESS_MASK ace_mask)
{
int sid_len = GetLengthSid (sid);
int ace_len = sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + sid_len;
ACCESS_ALLOWED_ACE *ace = (ACCESS_ALLOWED_ACE *) malloc (ace_len);
memset (ace, 0, ace_len);
ace->Header.AceType = ace_type;
ace->Header.AceFlags = ace_flags;
ace->Header.AceSize = ace_len;
ace->Mask = ace_mask;
memcpy (&ace->SidStart, sid, sid_len);
return (ACE_HEADER *) ace;
}
Utility function AddOrRemoveACE:
// Add or remove an ACE to/from an ACL
// Returns a copy of the original ACL with the relevant changes made; caller must free
// Sequence of ACE's must be:
// not inherited, denied
// not inherited, allowed
// inherited, denied
// inherited, allowed
ACL *AddOrRemoveACE (ACL *acl, ACE_HEADER *new_ace, bool add)
{
int acl_size = acl->AclSize;
if (add)
acl_size += new_ace->AceSize;
ACL *new_acl = (ACL *) malloc (acl_size);
BOOL ok = InitializeAcl (new_acl, acl_size, ACL_REVISION);
assert (ok);
// Add new denied ACE at start of list
if (add && new_ace->AceType == ACCESS_DENIED_ACE_TYPE)
{
ok = AddAce (new_acl, ACL_REVISION, MAXDWORD, new_ace, new_ace->AceSize);
assert (ok);
}
// Copy all non-inherited ACE's, removing anything matching new_ace
for (int i = 0; ; ++i)
{
ACE_HEADER *old_ace;
if (!GetAce (acl, i, (VOID **) &old_ace))
break;
if ((old_ace->AceFlags & INHERITED_ACE) == 0 &&
(old_ace->AceSize != new_ace->AceSize ||
memcmp (old_ace, new_ace, old_ace->AceSize) != 0))
{
ok = AddAce (new_acl, ACL_REVISION, MAXDWORD, old_ace, old_ace->AceSize);
assert (ok);
}
}
// Add new allowed ACE at end of list
if (add && new_ace->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
ok = AddAce (new_acl, ACL_REVISION, MAXDWORD, new_ace, new_ace->AceSize);
assert (ok);
}
// Copy all inherited ACE's
for (int j = 0; ; ++j)
{
ACE_HEADER *old_ace;
if (!GetAce (acl, j, (VOID **) &old_ace))
break;
if (old_ace->AceFlags & INHERITED_ACE)
{
ok = AddAce (new_acl, ACL_REVISION, MAXDWORD, old_ace, old_ace->AceSize);
assert (ok);
}
}
// Store the actual size of the acl data (not reversible)
ACL_SIZE_INFORMATION asi;
ok = GetAclInformation (new_acl, &asi, sizeof (asi), AclSizeInformation);
assert (ok);
new_acl->AclSize = (WORD) asi.AclBytesInUse;
return new_acl;
}
Utility function AddOrRemoveRegKeyACE (returns error code):
// Add or remove a registry key ACE. hKey must have WRITE_DAC access
DWORD AddOrRemoveRegKeyACE
(HKEY hKey, bool add, int ace_type, PSID sid, ACCESS_MASK access_mask)
{
LONG err;
PSECURITY_DESCRIPTOR psd;
DWORD buflen = 1024;
// Read current security information
for ( ; ; )
{
psd = malloc (buflen);
err = RegGetKeySecurity (hKey, DACL_SECURITY_INFORMATION, psd, &buflen);
if (err == 0)
break;
free (psd);
if (err == ERROR_INSUFFICIENT_BUFFER)
continue;
return err;
}
SECURITY_DESCRIPTOR_RELATIVE *sdr = (SECURITY_DESCRIPTOR_RELATIVE *) psd;
ACL *pdacl = (ACL *) ((BYTE *) sdr + sdr->Dacl);
ACE_HEADER *ace = BuildACE (sid, ace_type, CONTAINER_INHERIT_ACE, access_mask);
ACL *new_acl = AddOrRemoveACE (pdacl, ace, add);
free (ace);
free (psd);
SECURITY_DESCRIPTOR sd;
BOOL ok = InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
assert (ok);
ok = SetSecurityDescriptorControl (&sd,
SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED,
SE_DACL_AUTO_INHERIT_REQ | SE_DACL_AUTO_INHERITED);
assert (ok);
ok = SetSecurityDescriptorDacl (&sd, TRUE, new_acl, FALSE);
assert (ok);
// apply the security descriptor to the registry key
err = RegSetKeySecurity (hKey, DACL_SECURITY_INFORMATION, &sd);
free (new_acl);
return err;
}
And now a function to grant acces to a registry key to everyone in group 'Users'. Returns TRUE on success:
BOOL grant_access_to_registry_key (HKEY hKey)
{
// Give the Users group access to hKey
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
PSID pUsersSid;
BOOL ok = AllocateAndInitializeSid (&sia, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS,
0, 0, 0, 0, 0, 0, &pUsersSid);
if (!ok)
return FALSE;
ok = AddOrRemoveRegKeyACE (hKey, true, ACCESS_ALLOWED_ACE_TYPE,
pUsersSid, KEY_ALL_ACCESS) == 0;
FreeSid (pUsersSid);
return ok;
}