I am trying to enumerate Windows power plans through very straightforward code that has been working for several years. On my Windows 10 64-bit machine, however, I am getting errors when I try to enumerate a collection of power plans.
I realize Visual Foxpro code is like a fossil, but it is still pretty easy to read:
loSchemes = CREATEOBJECT("Collection")
loWMIService = GETOBJECT("winmgmts:\\.\root\cimv2\power")
loItems = loWMIService.ExecQuery("SELECT * FROM Win32_PowerPlan")
FOR EACH loItem IN loItems
loSchemes.Add(loItem)
ENDFOR
The code has been working for years, from Windows XP up through (I think, anyway), Windows 10.
The error happens after loItems is instantiated via the ExecQuery() method call. The object exists and has visible properties, but if I try to access anything in the debugger, it says the expression cannot be evaluated. If I wrap the iterating FOR loop in a TRY..CATCH, the error I get is:
OLE error code 0x80070668: Only administrators have permission to add,
remove, or configure server software during a Terminal services remote
session. If you want to install or configure software on the server,
contact your network administrator.
So, it looks like I am being locked out from power plan information because the process thinks I am remotely trying to change the configuration even though I am accessing WMI data from the local machine (where I do have administrator rights, incidentally).
I downloaded a "WMI Explorer" tool from Code Plex, and I actually get the same problem there when I try to iterate over Win32_PowerPlan. The log returns an error:
Failed to enumerate instances from Win32_PowerPlan. ERROR:
(That is the literal response -- no actual error is listed.)
This makes me think this isn't just some sort of Foxpro issue.
Edit
I downloaded a WMI process explorer from Sapien, and it displays a UAC prompt every time it starts, running it with elevated privileges. Both the 32-bit and 64-bit versions of that software can query Win32_PowerPlan and display results. I then ran the Code Plex WMI Explorer as administrator and it was also able to iterate over Win32_PowerPlan without errors. So, the issue appears to be unrelated to "bit"-ness and has everything to do with WMI data access requiring administrator privileges even though I am running locally using a login that does, in fact, have administrator privileges. Needless to say, I am still stumped... For the record, I can still access all sorts of other information via WMI: processor info, memory usage, processes, services, IP Address, and OS description. All of those modules still work perfectly. But when it comes to the \root\cimv2\power namespace and accessing power plans, no joy.
Further edit
Some other questions mention ImpersonationLevel, saying I might need to explicitly set the level to "impersonate" (an enumeration constant = 3). I am playing with my WMIService object and can read and write the impersonation level, but it is 3 by default. I raised it to 4 ("delegate") and still cannot access the power plan items. The query runs fine, but an error gets thrown when I try to access any properties of loItems. If I reduce the impersonation level to 1 ("anonymous"), then an "Access denied" error gets thrown on the ExecQuery() call. Finally, level 2 ("identify") allows the query and I can access the Count property without an error being thrown. But zero items are returned where there should be 5. I am confused now more than ever.
In summary, I cannot access power plan information from my local machine, even though I have administrator privileges, regardless. This is on a Windows 10 Professional 64-bit installation (all updates up-to-date), definitely no Terminal Server software installed.
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.
I have been dabbling with working nicely with UAC for a while and I found about a few things:
With UAC enabled, a program in the Startup folder, that requires to be run as admin (say by an embedded manifest), cannot be run according to this Stack Overflow thread.
Another method of running a program at startup is by creating a key containing the path to that application in: HKLM or HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run or HKLM or HKCU\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run in 64 bit machines.
Yet another method is using the task scheduler setting the Run with highest privileges option. This is the only method that bypasses the problem stated in point 1.
Coming from a Linux background, I had no clue about all these admin rights related problems. If someone can list out scenarios which absolutely need administrator privileges, it would be of great help!
I'm asking this because when I'm developing some application, I keep encountering several problems during implementation mostly because my application required admin rights when it shouldn't.
If I know, at design time, all possible scenarios that require admin rights, I could possibly design a common service for all my applications that takes care of all the administrator tasks (I think services are the Windows way of doing things like this).
There really isn't a list of scenarios or API function calls that require elevation. Your best option will probably be to focus on what API calls require elevation. The reason for this is that it may be required only if certain values are passed to the function. For instance CreateFile can create a file in your home directory without elevation but requires it for creating a file in C:\Windows. If the directory is provided through user input the only way you can know if elevation is required is to check the error code when the call fails. If elevation is required the function will set the error status to ERROR_ACCESS_DENIED and return a value indicating failure.
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.
On Vista using IE8, I have an instance of IWebBrowser2, which I am using to obtain the current HTML document:
IWebBrowser2* browser;
CComPtr<IDispatch> disp;
HRESULT hr = browser->get_Document(&disp);
When this is executed by an Administrator, the call succeeds. However, when called by a regular user, the call returns an error code of "800706B5" ("The interface is unknown") despite the page being on a "Trusted Site" and "Protected Mode" being off.
This same code worked without problems on IE7 on Vista, and with IE8 on XP.
Does anyone know why this error might be occurring, and what I can do to resolve the issue? Running as administrator is less than ideal, and Jon Skeet is stumped by this one too :)
Update: the question seems to revolve around UAC: turning off UAC completely allows things to work for a regular user (though it's distinctly unsafe)
The error is occurring because on Vista, IE 8 runs in "low integrity" mode, whereas my test code, running as a normal user, runs at "medium integrity". The security model is designed so that code can send instructions to lower integrity components, but data cannot be read from "lower" to "higher" components by default.
More information is available in this document about How the Integrity Mechanism Is Implemented in Windows Vista
Was IE8 installed by the Administrator "For this user only"? I don't know why or if that would give this error, just a hunch.
I was trying to get a Uuid via NtAllocateUuids or simply calling UuidCreateSequential,
but Windows wasn't able to get an Ethernet or token-ring hardware address for my laptop.
And so, when the system is booting, windows sets the UuidSeed to a random number instead of a given MAC.
--> uniqueness is guaranteed only until the system is next restarted.
I was trying to manual set the UuidSeed with NtSetUuidSeed but i was getting a STATUS_ACCESS_DENIED error.
"Windows NT/2000 Native API Reference" has following remarks:
--The token of the calling thread must have an AuthenticationId of SYSTEM_LUID
Is there any way to achieve this from a process, running as Administrator?
Something like ImpersonateLoggedOnUser() could work but afaik this is also only accessible as LocalSystem :/
Thx ;)
You can create a program and schedule to run it using the AT command. The scheduling service run as the local system account. On Vista things are different and I'm not sure if that is possible any more. You could write your own service of course.
ImpersonateLoggedOnUser worked fine with a token handle returned from:
HANDLE GetLSAToken() //duplicate a system token from the "System" process
or
BOOL CreatePureSystemToken(HANDLE &hToken) //create a new system token
http://www.codeproject.com/KB/system/RunUser.aspx
CoreCode.cpp