How to get original user token? UAC, manifest, installer

290 views Asked by At

We have a program with manifest attached and option "level="requireAdministrator". After program launched under standard user(not as admin), its elevating to admin user and from that moment SHGetFolderPath(0,CSIDL_LOCAL_APPDATA,0,SHGFP_TYPE_CURRENT,@buf)) returns admin local directory but not user's where it was originally started. Of course we could pass standard user token as third parameter to SHGetFolderPath but how we can get it in elevated program? Is there any way to do it?

Of course because of this problem we have another question. How to start another program with ShellExecute not as admin in elevated program? Of course we could use CreateProcessWithTokenW but still the problem with token exists(how to get this token). Right now we looking for IShellDispatch2 interface and try to start application not as administrator using explorer, but something tells me that this is wrong way.

I know that we have to start program as standard user then using com objects elevate only parts of code which should be executed with admin's rights. But still, the main question is with token.

Also there was an idea to make manifest with "level="asInvoker" and start first program as standard user, detect all local user paths and pass it as parameters to ShellExecuteEx with runas and SEE_MASK_NOCLOSEPROCESS options and run the same program but as admin. After the elevated program will exit we can execute any program from not elevated program not as admin. Is this right way?

2

There are 2 answers

0
Alejandro On BEST ANSWER

Maybe a little late, but your alternative option is the right way to go. Once the program is elevated, there is no reliable way of starting yet another program but without the elevation, or even know what user started it, if it was done with different credentials.

The general solution to all those problems is to split the program in 2 different processes (which may be implemented in 1 or 2 different .EXEs, as you like). The "main" program, the one that the user runs, runs without elevation, manifested as asInvoker, and it in turn launches the second "worker" process, manifested as requireAdministrator. The main one can pass the data from the original user to the second by means of command line, unnamed pipes, window messages or any other form of IPC while the worker process does all the admin-only stuff. Possibly you may want to notify back to the main process when it's time to spawn another non-elevated program.

0
Ian Boyd On

From Raymond Chen:

How can I launch an unelevated process from my elevated process and vice versa?

Going from an unelevated process to an elevated process is easy. You can run a process with elevation by passing the runas verb to Shell­Execute or Shell­Execute­Ex.

Going the other way is trickier. For one thing, it's really hard to munge your token to remove the elevation nature properly. And for another thing, even if you could do it, it's not the right thing to do, because the unelevated user may be different from the elevated user.

The solution here is to go back to Explorer and ask Explorer to launch the program for you. Since Explorer is running as the original unelevated user, the program will run as the original unelevated user.

i tried to translate the code out of C++, but it got too hairy to manage.

The short version is: it's very hard.