5

Using System.Diagnostic.Process.Start() from IIS Express running in my interactive session, I can execute a program running as a different user with correction functionality. Unfortunately, it seems that this doesn't work from non-interactive sessions.

Process.Start internally calls CreateProcessWithLogonW(CPLW) when credentials are specified. CreateProcessWithLogonW cannot be called from a Windows Service Environment (such as an IIS WCF service). It can only be called from an Interactive Process (an application launched by a user who logged on via CTRL-ALT-DELETE). -- from this SO answer

I need to publish this site to IIS 8 from the app pool account. So I CreateProcessAsUser as suggested by the above-quoted answer. I've set the service account and agent account with Local Security Policies and restarted as suggested in that answer - service account can replace token, modify quotas and agent account can logon as batch (and as service for that test). But I can't get it to work in IIS Express (or a console test app) nor IIS 8. I've tried running as LOGON32_LOGON_BATCH, LOGON32_LOGON_NETWORK_CLEARTEXT, and LOGON32_LOGON_SERVICE, and even LOGON32_LOGON_INTERACTIVE. I've even given my own account "logon as service" and "act as part of the operating system" privilege with no change - all tested after a reboot.

I'm getting "A required privilege is not held by the client" from IIS Express for all configurations. On the server, I get the same running the console app. But publishing the app, it seems to start the process just fine, but then I seem to be getting permissions errors subsequently.

I'd like to know WHICH privilege my accounts are missing when running locally so I can debug them properly (and eventually figure out whatever permissions error I'm getting). Is there any way to determine that? Either way, if you know what the issue is, I'd like that too!

Thanks!

Community
  • 1
  • 1
Jason Kleban
  • 20,024
  • 18
  • 75
  • 125
  • 1
    The permissions errors are probably because you haven't changed the permissions on the window station and desktop. The second piece of code in [this answer](http://stackoverflow.com/a/21718198/886887) shows how to do that. – Harry Johnston Dec 08 '14 at 20:37
  • Have you restarted the machine since making the security policy changes? They won't take effect until then. – nateirvin Dec 08 '14 at 20:58
  • @uosɐſ, whoops, sorry, read too fast. – nateirvin Dec 08 '14 at 21:04
  • @HarryJohnston - different in my scenario is that I don't need to run in Admin mode and it is being run from a service in a batch logon rather than in any interactive session (though access to a GUI may be necessary, I don't know). Should I be creating a new Windows Station and Desktop for the process and then explicitly set the rights to them, as you showed in that other answer? Or will the batch logon spawned process already have it's own of each and I just need to upgrade them (as you show)? Thanks! – Jason Kleban Dec 09 '14 at 15:28
  • 1
    The code as shown will work for launching non-admin users, in your case you might need to use CreateProcessAsUser instead of CreateProcessWithLogonW. However, it does potentially expose the admin account to attack from any malicious code running in the non-admin account. Ideally you would create a new window station and desktop, assigning appropriate permissions to them. But I recommend that you get it working the easy way first, make sure there are no other issues before introducing this complication. – Harry Johnston Dec 09 '14 at 20:30
  • 1
    If the non-admin code needs to present a GUI to a currently logged in user, that's a completely different scenario; best practice is to run such code in that users own security context, which you can do using WTSQueryUserToken (but you have to be running as local system). – Harry Johnston Dec 09 '14 at 20:34
  • It does not need to *present* a GUI to anybody, but it might need to *think* it is presenting it - as you noted, the lack of access to a GUI for a process which is expecting one could cause problems - I'm not sure if that will happen in my case. – Jason Kleban Dec 09 '14 at 21:20
  • 1
    So long as the process is able to access the window station and desktop, it should be able to use GUI functions. The display surfaces associated with non-interactive desktops are dummies, but that shouldn't matter. (There may be some rare edge cases.) – Harry Johnston Dec 10 '14 at 03:00
  • Thanks. I'm taking it on faith and working through your sample and translating to p/invoke in c# .. so far I'm to SetEntriesInAcl and it's not bombing out on me ... I'll have to go back and clean up all my handles once this works! – Jason Kleban Dec 10 '14 at 03:24
  • OK, `CreateProcessWithTokenW()` works with your code and notepad.exe (I can see it in the task list for that user), but I get "Access is Denied" when attempting to `LoadUserProfile()` in preparation of `CreateProcessAsUser()` instead. `CreateProcessWithTokenW()` is failing for Internet Explorer (process quits immediately, I guess, without errors) and getting a `KernelBase.dll 0xc06d007e` logged by my true target application. – Jason Kleban Dec 10 '14 at 17:44

1 Answers1

5

Per the documentation:

CreateProcessAsUser function

Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable. If this function fails with ERROR_PRIVILEGE_NOT_HELD (1314), use the CreateProcessWithLogonW function instead. CreateProcessWithLogonW requires no special privileges, but the specified user account must be allowed to log on interactively. Generally, it is best to use CreateProcessWithLogonW to create a process with alternate credentials.
...
If hToken is a restricted version of the caller's primary token, the SE_ASSIGNPRIMARYTOKEN_NAME privilege is not required. If the necessary privileges are not already enabled, CreateProcessAsUser enables them for the duration of the call.

The calling thread can use OpenThreadToken() and AdjustTokenPrivileges() to enable individual privileges as needed before calling CreateProcessAsUser(). But since it does that internally anyway, that implies the user associated with the calling thread does not have those privileges available to begin with.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • If you check the error code after calling AdjustTokenPrivileges (even if the call was successful; a rare exception to the usual rule) you can determine whether you have the privileges requested or not. – Harry Johnston Dec 08 '14 at 20:35
  • Is this quoted statement false?: "CreateProcessWithLogonW cannot be called from a Windows Service Environment (such as an IIS WCF service)" Otherwise, I'm not sure what you're recommending. – Jason Kleban Dec 08 '14 at 20:47
  • I'm seeing "The only privileges required for `CreateProcessAsUser` are the "Adjust memory quotas for a process" and "Replace a process level token" according to the docs. You could enable these explicitly, but since `CreateProcessAsUser` will enable them itself, the fact that you're getting this error suggests that the calling user context does not have the necessary permissions do to that at all." And as I understand it, CreateProcessWithLogonW cannot be called from a non-interactive process such as IIS. – Jason Kleban Dec 08 '14 at 21:51
  • 1
    I'm not sure whether CreateProcessWithLogonW can be called from a non-interactive process or not. But Remy is talking about what you need to do to make CreateProcessAsUser work. – Harry Johnston Dec 09 '14 at 20:37
  • So, I guess Remy is providing a definitive source documentation saying that "it should work" as long as I have, in fact, given those privileges. It doesn't, so I guess more info is necessary. This is helpful as a sanity-check. Harry Johnston seems to have a lot of insight in his comments, but as they aren't given as an Answer, I'll accept this one! Thanks guys. I'll start a new question if necessary once I follow Harry's guidance to their end. – Jason Kleban Dec 09 '14 at 21:30
  • 1
    I know for a fact that `CreateProcessAsUser()` works in a non-interactive service, because I use it in one of my own services and it works fine. And yes, I do explicitly enable the privileges in code using `AdjustTokenPrivilege()` before calling `CreateProcessAsUser()`. – Remy Lebeau Dec 10 '14 at 00:01