I have an old Windows program that reads information from the registry, like so:
CRegKey rkey;
if(rkey.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\xxx\\yyy", KEY_READ) == ERROR_SUCCESS)
{
...
}
On a Windows 10 locked down user account, this fails!
It cannot even read the registry, at least not that part of it.
I've looked on Microsoft's support site, and I could not find where it addresses this.
Can anyone point me to info, or just tell me, how accessing the registry from a (C++) program is affected by UAC in Windows 10?
I need to retract this question. Further investigation shows that the User account has read access to HKLM etc in the registry, and that is not the problem. In fact, the installer already wrote the registry entries I was looking for under HKCU (current user) where User account has both R and W permission. ... So, I blamed Windows 10 user mode unnecessarily. Issue seems to be communication from that program to another is not succeeding. We will look into it. I apologize for posting the question which, on investigation, turned out not to be the source of the problem.
Related
I am having a problem with calling the function MsiOpenDatabase (https://msdn.microsoft.com/en-us/library/aa370338(v=vs.85).aspx) from inside a program when I choose to "run as administrator". When I run it under an admin account but without explicitly starting the executable as elevated it all works just fine. This indicates that the path to MSI file etc should be correct.
So, when running elevated the MsiOpenDatabase() I get an error code of 110 (0x6e).
I have tried to call MsiGetLastErrorRecord as explained here (https://msdn.microsoft.com/en-us/library/aa370124(v=vs.85).aspx) but nothing happens when I try to print the code in a message box. It simply doesn't get there.
I do not have Visual studio for debuggning on the target machine, so debugging is a bit of a pain.
Target machine is Windows 7 x64. Application is 32-bit.
But just the pure fact that it works un-elevated but fails when run as an administrator...it feels like there should be some kind of answer to this which can be derived from this fact perhaps?
Thankful for any help!
EDIT:
I finally solved it!
Apparently I had to go to the network share where the MSI file is located (which I am trying to call MsiOpenDatabase on) and right cklick on a file there and choose "run as administrator" because then and only then did I get a UAC dialog box asking for credentials (I mean I was able to open Windows Explorer as admin and navigate to the network share without problem so I never thought that it would be what would give me these peoblems). After haing done that I was able to run my application and it did no longer fail on any MsiOpenDatabase call.
But, why must I do this procedure to get access to run file on a network share since I already had access (execute rights) with the same user but when not elevated? How come Windows needs to ask the same user for credentials if it is already running elevated on the very same account that already has access to the network share? Seems strange to me, but I suppose I am missing some crucial part?
SAMPLE CODE
LPCTSTR szPersist = MSIDBOPEN_READONLY;
MSIHANDLE handleDB;
UINT result = MsiOpenDatabase(strPath, szPersist, &handleDB); // strPath is something like _T("\\server\MSI\Setup.msi");
result variable has value 110 when this error occurrs as explained above and keep the part in the update section in mind. I find it strange, but perhaps someone knows UAC better than me and why I have to provide credentials again by going to a file on the netowrk share and choose to run as admin to get it working (since I have already provided credentials as non-admin with the same account earlier at that very same network share location)?
This is standard UAC behavior since Windows Vista and is not related to MSI at all. Do a google search for "uac network drives".
You should be closing your MSI handles though as I commented above. Use PMSIHANDLE instead of MSIHANDLE.
A similar question got asked by others here at least twice already (see links below) but never properly answered, perhaps because it wasn't put the right way. Let me have a go:
I have a process running under the local account on Windows and I need locale information about the user who is logged into the interactive session if there is one or any interactive session if there is more than one.
I need to know the user's language and country settings and it has to work on all kinds of Windows starting XP Service Pack 3.
I very much appreciate your answers!
The previous related questions that didn't really get answered:
C# window xp current user when using run as
https://stackoverflow.com/questions/5778378/creating-a-process-as-logged-on-user
I would do it in the following way:
Enumerate logon sessions using LsaEnumerateLogonSessions
Call LsaGetLogonSessionData to get logged on user SID identifier.
Call ConvertSidToStringSid function to get a string representation of a sid.
Look up registry setting HKEY_USERS[USER_SID_STRING]\Control Panel\International
This key has all sorts of information needed, and looks like all listed WINAPI finctions are available starting at Windows XP.
So this should work.
Ok, so we have a C++ app that runs fine in Windows XP.
It has the following code in the initialization
// Register all OLE server (factories) as running. This enables the
// OLE libraries to create objects from other applications.
COleObjectFactory::RegisterAll();
Now like i said, it works fine in Windows XP, but as far I understand the program tries to register its COM interface Which is fine in XP, but this may be a problem in Windows Vista and Windows 7 because of the UAC. Especially if its is run as a standard user (with no elevated privileges).
If i understand it correctly this is needed for the program to run properly, but it cant execute this code without elevated privileges. If it will run every time the app runs (this usually runs when CWinApp::init() is run)
Before you say just use admin privileges, the user will not have them, there is no way to change that
So, now my questions are:
1) am i correct in my assumptions?
2) if I am correct, what is the best way around this? Can i just remove this? Do i need to set up some other thing? (we changed some VB modules to use a XML file instead of stuff in the registry
PS: the modules compiles into DLLs
PPS: UAC MUST be on
Take note that:
The documentation for these functions makes no mention of any privilege requirements; and
nobody online seems to be having trouble with these functions in limited privilege environments; and
it's 2012, I think someone would have noticed if these functions didn't work under UAC by now.
So (with nothing to suggest otherwise) I'd say It Just Works.
Notwithstanding the above, I looked at the implementation of COleObjectFactory::RegisterAll() and COleObjectFactory::UpdateRegistryAll().
RegisterAll
Ultimately calling RegisterAll ends up in olefact.cpp:135 where CoRegisterClassObject is called. From MSDN:
Registers an EXE class object with OLE so other applications can connect to it.
I believe this registration will be limited to the current user's session and the lifetime of the application. The Remarks section touches on privileges (As of Windows Server 2003...) but doesn't provide anything concrete.
There's an object known as the Running Object Table (ROT) that can be retrieved via GetRunningObjectTable. The documentation has this snippet:
Each workstation has a local ROT that maintains a table of the objects that have been registered as running on that computer.
The COM Elevation Moniker has some more information about the ROT and privileges (it suggests processes of various privilege levels work fine with it). The links on the left-hand side might help, too.
Overall it seems there's nothing to suggest that CoRegisterClassObject requires administrator permissions.
UpdateRegistryAll
This function ends up in olefact.cpp:375 where it opens HKEY_CLASSES_ROOT. At this point the documentation gets a bit better:
Registry functions such as RegOpenKeyEx or RegQueryValueEx allow you to specify the HKEY_CLASSES_ROOT key. When you call these functions from a process running in the interactive user account, the system merges the default settings in HKEY_LOCAL_MACHINE\Software\Classes with the interactive user's settings at HKEY_CURRENT_USER\Software\Classes.
Further on:
If you write keys to a key under HKEY_CLASSES_ROOT, the system stores the information under HKEY_LOCAL_MACHINE\Software\Classes
The documentation doesn't define what happens when you try to write to HKEY_CLASSES_ROOT under limited privileges (i.e. a standard user can't write to HKLM), but I believe that you'll end up writing to HKCU instead.
And finally, note:
Windows Server 2003 and Windows XP/2000: Applications can register dependent COM objects to either the per-machine or per-user COM configuration store (HKEY_LOCAL_MACHINE\Software\Classes or HKEY_CURRENT_USER\Software\Classes).
So if it falls through to HKCU, you should be fine.
Caveat Implementor: Don't rely on implementation details.
Everything I've been able to find about escalating to the appropriate privileges for my needs has agreed with my current methods, but the problem exists. I'm hoping maybe someone has some Windows Vista/Windows 7 internals experience that might shine some light where there is only darkness. I'm sure this will get long, but please bear with me.
Context
I'm working on an application that requires accessing the memory of other processes on the current machine. This, obviously, requires administrator rights. It also requires SeDebugPrivilege (no, it is not a misspelling of SetDebugPrivilege), which I believe myself to be acquiring correctly, although I question if more privileges aren't necessary and thus the cause of my problems. Code has so far worked successfully on all versions of Windows XP, and on my test Vista 32 bit and Windows 7 64 bit environments.
Process
Program will Always be run with Administrator Rights. This can be assumed throughout this post.
Escalating the current process's Access Token to include SeDebugPrivilege rights.
Using EnumProcesses to create a list of current PIDs on the system
Opening a handle using OpenProcess with PROCESS_ALL_ACCESS access rights
Using ReadProcessMemory to read the memory of the other process.
Problem:
Everything has been working fine during development and my personal testing (including Windows XP 32 & 64, Windows Vista 32, and Windows 7 x64). However, during a test deployment onto both Windows Vista (32-bit) and Windows 7 (64-bit) machines of a colleague, there seems to be a privilege/rights problem with OpenProcess failing with a generic Access Denied error. This occurs both when running as a limited User (as would be expected) and also when run explicitly as Administrator (Right-click → Run as Administrator and when run from an Administrator level command prompt).
However, this problem has been unreproducible for myself in my test environment. I have witnessed the problem first hand, so I trust that the problem exists. The only difference that I can discern between the actual environment and my test environment is that the actual error is occurring when using a Domain Administrator account at the UAC prompt, whereas my tests (which work with no errors) use a local administrator account at the UAC prompt.
It appears that although the credentials being used allow UAC to 'run as administrator', the process is still not obtaining the correct rights to be able to OpenProcess on another process. I am not familiar enough with the internals of Vista/Windows 7 to know what this might be, and I am hoping someone has an idea of what could be the cause.
The Kicker
The person who has reported this error, and who's environment can regularly reproduce this bug, has a small application named along the lines of RunWithDebugEnabled which is a small bootstrap program which appears to escalate its own privileges and then launch the executable passed to it (thus inheriting the escalated privileges). When run with this program, using the same Domain Administrator credentials at UAC prompt, the program works correctly and is able to successfully call OpenProcess and operates as intended.
So this is definitely a problem with acquiring the correct privileges, and it is known that the Domain Administrator account is an administrator account that should be able to access the correct rights. (Obviously obtaining this source code would be great, but I wouldn't be here if that were possible).
Notes
As noted, the errors reported by the failed OpenProcess attempts are Access Denied. According to MSDN documentation of OpenProcess:
If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the contents of the security descriptor.
This leads me to believe that perhaps there is a problem under these conditions either with (1) Obtaining SeDebugPrivileges or (2) Requiring other privileges which have not been mentioned in any MSDN documentation, and which might differ between a Domain Administrator account and a Local Administrator account
Sample Code:
void sample()
{
/////////////////////////////////////////////////////////
// Note: Enabling SeDebugPrivilege adapted from sample
// MSDN # http://msdn.microsoft.com/en-us/library/aa446619%28VS.85%29.aspx
// Enable SeDebugPrivilege
HANDLE hToken = NULL;
TOKEN_PRIVILEGES tokenPriv;
LUID luidDebug;
if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != FALSE)
{
if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidDebug) != FALSE)
{
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luidDebug;
tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, NULL, NULL) != FALSE)
{
// Always successful, even in the cases which lead to OpenProcess failure
cout << "SUCCESSFULLY CHANGED TOKEN PRIVILEGES" << endl;
}
else
{
cout << "FAILED TO CHANGE TOKEN PRIVILEGES, CODE: " << GetLastError() << endl;
}
}
}
CloseHandle(hToken);
// Enable SeDebugPrivilege
/////////////////////////////////////////////////////////
vector<DWORD> pidList = getPIDs(); // Method that simply enumerates all current process IDs
/////////////////////////////////////////////////////////
// Attempt to open processes
for(int i = 0; i < pidList.size(); ++i)
{
HANDLE hProcess = NULL;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pidList[i]);
if(hProcess == NULL)
{
// Error is occurring here under the given conditions
cout << "Error opening process PID(" << pidList[i] << "): " << GetLastError() << endl;
}
CloseHandle(hProcess);
}
// Attempt to open processes
/////////////////////////////////////////////////////////
}
Thanks!
If anyone has some insight into what possible permissions, privileges, rights, etc. that I may be missing to correctly open another process (assuming the executable has been properly "Run as Administrator"ed) on Windows Vista and Windows 7 under the above conditions, it would be most greatly appreciated.
I wouldn't be here if I weren't absolutely stumped, but I'm hopeful that once again the experience and knowledge of the group shines bright. I thank you for taking the time to read this wall of text. The good intentions alone are appreciated, thanks for being the type of person that makes Stack Overflow so useful to all!
So after a lot of debugging and bothering a lot of people for information, I was finally able to track down the guy who wrote the RunWithDebugEnabled application and get a rundown of how it operates.
The problem, in this case, is that Debug programs privilege in the local policy for the domain administrator had been removed, and thus the SeDebugPrivilege token was not present in the process's access token. It can't be enabled if it's not present at all, and I still know of no way to add the privilege to an existing access token.
How the current magic works:
So the RunWithDebugEnabled magic application would use its Administrator rights to install itself as a service and start itself, thus running under the SYSTEM user account rather than the Domain Administrator. With SYSTEM privileges, the app then creates a new access token that is identical to the Administrator token, only with the SeDebugPrivilege token present. This new token is used to CreateProcessAsUser() and run the program with the newly enabled SeDebugPrivilege that was missing before.
I actually do not like this "solution" here, and have been continuing my search for a 'cleaner' way to obtain this privilege. I will be posting this as another question here on SO, which I will try to remember to link here as well to help others follow along and for future reference.
EDIT: Impersonate SYSTEM (or equivalent) from Administrator Account
I thank you all for your time and energies in helping to debug and solve this problem. It really is much appreciated!
I have a dll made in cpp which tries to read/write some Registry Keys. This code runs perfectly fine in Windows XP (32 bit environment) but it fails in Windows 7(64 bit environment).
The registry keys which this application accesses is the shared registry keys. These keys are not part of 32 bit registry cache(wow32 bit) or 64 bit registry cache.
Please provide your valuable inputs on this.
Thanks in advance.
Jits
when you say "shared" you mean for example under HKLM? Only elevated applications are allowed to write to those on Windows 7 and Vista. If this is news to you I suggest searching on User Account Control or UAC.
Check out this: RegQueryValueEx Function
And this: Registry Key Security and Access Rights
IMO you have to check your permission settings that you use to open the key. Either remove the settings that require elevated privileges or run your app in elevated mode.
Maybe you should initialize the value of "lpcbData", the last parameter of RegQueryValueEx.