Launching an interactive process from a service used to be straight forward. You could either configure your service to be interactive or you could specify “Winsta0\\Default” as the desktop (in CreateProcess API) and as long as the launched process had the appropriate permissions to the desktop, the launched process would run interactively.
These techniques stopped working with the introduction of Windows VISTA and continues today on Windows 8. Beginning with Windows VISTA, Windows Services were isolated in their own session. (Session 0). This meant if you launched a process from a Windows Service, the process was going to run in session 0 and processes running in session 0 are not interactive.
The session the launched process would run in was determined by the user’s token. If you set a different session ID in the token, the process would launch in that session. You’ll notice that you won’t be able to use CreateProcess() any more since it is going to launch in the same session as the service. Since you need to specify a token, you’ll need to call CreateProcessAsUser(). The other issue you need to address in order to launch a process interactively is that the process needs FULL permissions to the interactive desktop which is “Winsta0\\Default”. There is a lot of old sample code (http://support.microsoft.com/kb/165194 - I actually wrote this article) out there that demonstrates how to modify the security permissions to grant a user access to the Interactive desktop. Unfortunately this code will not work anymore since you can’t modify the permissions for the desktop that is located in a different session. You can only modify the desktop in the session where your code is running in.
The recommend way to launch an interactive process is to determine the session ID of the session you are targeting and to obtain the token of the interactive user in that session. If you know the session ID, you can call WTSQueryUserToken() to obtain the token. You can then launch a process into this session as this user. You do need to have the SeTcbPrivilege in order to make the call to WTSQueryUserToken() so it’s typically something only a highly privileged user should do.
If you want to launch a process as a different user (other than the interactive user running in the session), the best way to do this is to have a process running in that session call CreateProcessWithLogonW(). This is equivalent to using the runas command.
great post, thanks for this comprehensive description, was for me very useful.
I have a similar problem on Windows Server 2012: I need to start processes with CreateProcessAsUser without interaction, but these calls always failing with error code 5 (access denied). The application is hosted in IIS, and uses DuplicateTokenEx for converting the impersonating token to a primary token. The same code is executed successfully on Windows 7 and Windows Server 2008 R2. Do you have any idea?
If you are receiving an "access denied" from CreateProessAsUser, this means the caller doesn't have access to the exe itself. I would suggest running ProcMon to see where the "access denied" is coming from.
there is no error shown by the procmon, each file operation succeeds, the IIS apppool is able to read the executable, which I want to start under another token.
I forgot to mention that you may not have the correct permissions in the token passed to CreateProcessAsUser(). I would review the permissions you are requesting in DuplicateTokenEx(). I know the required permissions in the token might have changed between operating systems.
Hi Frank! I hope that you are still monitoring this post. I am trying to do something similar to this but different. I want a service to run a console application as a specific arbitrary user. I am running the service as a domain user with the rights Adjust memory quotas for a process, Create a token object, Impersonate a client after authentication, Log on as a service, and Replace a process level token. My code is written in .Net, but I have tried to use Process.Start() with authentication information, as well as CreateProcessAsUser, CreateProcessWithLogon, and CreateProcessWithToken through P/Invoke. I have tried them "regular" and after calling WindowsIdentity.Impersonate(). I won't enumerate every possible combination and result here, but most often, I end up getting an exit code of 3221225794 and/or an Exception of "Access Denied". I can't tell what is trying to access what, and where access can't be granted. I've looked at Standard I/O, Session/Desktop, and everything else I can think of. Any info or advice you can give on making this happen and/or finding out where the access violation is here?
I have a blog post on this topic.
Thank you for the response Frank, but as I said, I am not using the LocalSystem account for this specific reason. I am instead using a domain account which (I thought) had the correct rights to do this. I have spent plenty of time trying to figure out which desktop to run my "headless" application, but so far I've left it as the default since, in the case of CreateProcessWithTokenW, the documentation says "the new process inherits the desktop and window station of its parent process. The function adds permission for the specified user account to the inherited window station and desktop." As far as I can tell, LogonUser does not actually create a station\desktop for use. I am looking to do something very similar to a telnet/ssh daemon. I suppose I could look for the code to some of those, but many of them will go through Posix layers like Cygwin, making Win32 understanding difficult. Anything else that you can do to help would be appreciated.
That blog post would also apply to a Service Account. Essentially, for your scenario, .NET isn't going to work. You should be able to use CreateProcessAsUser(). Your code for pinvoking CPAU may not be correct and it isn't clear to me if the console app being launched needs to be interactive or not. There are a lot of scenarios you must consider. Unfortunately a BLOG post on all the scenarios isn't possible.
if you are still having issues, I would suggest creating an support incident so we can investigate further.
Thank you. the P/Invoke functions all work when running as a console application in a user session, but not as a service. It is likely that tweaking one of the many options is the key. The app I am testing with has zero input or output. All it does for now is Thread.Sleep so that I can confirm that it is running and as which user in the Task List.