This is a question that has been coming up a lot lately, so I figured I would answer the question once here, so I can just start handing out the link when it gets asked again in the future. Here is the essence of the question:
“I am trying to create a process as another user, using CreateProcessWithLogonW. Even if I launch it from an elevated process, it spawns a Medium IL process. If I mark it as requireAdministrator, it returns ERROR_ELEVATION_REQUIRED. What gives?”
First, let’s explain why you always get a filtered token. When you are launching an application using this API, somewhere behind the scenes you are logging on that user. With UAC enabled, if you are a member of the local Administrators group, that’s going to result in generating two different user tokens: your full administrator’s token, and a filtered administrators token. So, you’ll end up with both logon sessions (and tokens) generated, but then we’ll use the filtered one until you explicitly elevate.
OK, so far so good. The logon event itself causes me to generate both logons and use the less powerful one by default. (Assuming it’s an interactive login, that is.) I get that, so now I’m going to manifest my application to get around that and use the more powerful one, and I bounce into failure again (and failure sucks). Why is that happening? ERROR_ELEVATION_REQUIRED? I mean, come on – I know that, that’s why I’m trying to elevate!
Well, there’s a good explanation for that one too. This is based on the layering work we’re doing in Windows. Rather than letting any Windows binary depend on any other Windows binary, we invest huge efforts in maintaining the architectural purity of layering in Windows. Binaries that are at a low layer just can’t depend on binaries in a higher layer.
Larry describes the layering process here: http://blogs.msdn.com/larryosterman/archive/2005/08/23/455193.aspx.
What’s the layering problem? Well, CreateProcess is really low in the layers. What can you do without the ability to create a process? Not a whole lot. Elevation, however, is a different story. It requires a trip to the app elevation service. This then calls into consent.exe, which has to know how to read group policy and, if necessary, switch to the secure desktop and pop open a window and ask the user for permission / credentials, etc. We don’t even need to take all of these features, let’s just take the dialog box. What can you do on a system with processes but not a CreateWindow function? Why, be server core, of course!
Now, for creating a process that requires elevation, normally you just switch up APIs. The shell sits in a much higher layer, and consequently is able to take a dependency on elevation. So, you’d just swap out your call to CreateProcess with a call to ShellExecute. So far, so good. But wait … if you do a web search on ShellExecuteWithLogon, you return 0 hits. That’s right, there is no API for this!
So, how can you work around this? You need a bootstrapper. Some process which will let you do the transition to the alternate user, which could be responsible for running the requireAdministrator application. So, you could design something like this:
We did something very similar to this for Sysinternals’ ShellRunAs, only instead of writing a bootstrapper, Jon used cmd.exe to bounce through to the target executable (which is a technique you could use as well, though Mark had to do a bit of work to try to keep this hidden wherever possible).
(Why did we use cmd.exe? Because there’s a lot of logic built into this already, such as handling different file types and targets, which would have been challenging to get as correct as this already extensively tested application.)
Why don’t we just create the ShellExecuteWithLogonW API? I’ll never say never, and we might at some point. But today, the use cases for this APIs have been use cases where there has been an alternate design which is superior. The most common request is for people writing home-grown software deployment software, where they’d like to encode credentials right into the app and elevate their own process. The real problem here is not the lack of the API, it’s that you have admin credentials encoded in your app for the world to read. If you have that, what you want is a way to get to a place where you do not have that as quickly as possible, not make it easier to build on that design. I’ve also seen scenarios where moving the code in question to a service was the better design. (Adding new APIs is a big deal, so we tend to look for whitespace with no workarounds if we’re going to do that.)