locked
W2K8: CreateProcessAsUser from Service & can't start process in new Session (>0)? RRS feed

  • Question

  • I have an issue with CreateProcessAsUser being invoked from a Windows W2K8 service that is a little bit unusual.  I've found numerous postings, but they are all assuming interaction between a service and a user-initiated logon session.  In my case, I think I need the service create a proxy logon session in response to a remote request.

    Additionally, I have a very specific requirement for a W2K8 server to be able to spawn a process from a windows service (currently configured to be running in a domain account - long story) such that the spawned process(es) can get access to a hardware-accelerated windows DC.  The child processes communicate with the service via shared memory.  The code has been working fine on XP and W2K3, but it now consistently fails on W2K8 when the spawned process attempts to use hardware-accelerated graphics calls (the application correctly detects that it can't get access to the acceleration and fails.)

    I presume this is because of the documented "Service Hardening" in which session 0 is now exclusively reserved for services (and therefore, my spawned process, which I've verified using ProcExplorer) will not have access to the video card (due to service hardening security changes.)

    I need to find some way around this, and the best bet seemed to be to simulate an interactive user login to the "physical" desktop via the winsta0/default station/desktop.
    After some research, the article on "Starting an Interactive Client Process in C++" and API documentation on CreateProcessAsUser (or alternately, CreateProcessLogonW) looked like a way around this, and that if my new process could simply start in a new session, all would be well.  Unfortunately, I have yet to find a combination that allows me to successfully start in a new session, and even the ones that do seem to be requiring user/service privileges that I'd prefer not to use.

    For instance - here's a stub (error handling removed) that I think should work, but always returns an error 1314 on the SetTokenInformation call (even though the AdjustTokenPrivileges returned no errors)  I've used some alternate strategies involving "LogonUser" as well (instead of opening the existing process token), but I can't seem to swap out the session id.

    I'm also dubious about using the WTSActiveConsoleSessionId in all cases - although a quick test of the service running with no sessions logged in seemed to return a reasonable session value (3).

    This code stub is the part that is causing me grief - the rest of the code that follows (taken from the link above) seems to launch a new interactive client correctly - the only problem being that it is still in session 0 when created from a service.

    OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
    
                             | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID
    
                             | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY
    
                             | TOKEN_DUPLICATE, &hToken)
    
    GetTokenInformation( hToken, TokenSessionId, &logonSessionId, sizeof(DWORD), &dwTokenLength )
    
    
    
    DWORD consoleSessionId = WTSGetActiveConsoleSessionId();
    
    /* Can't use this - requires very elevated privileges (LOCAL only, SeTcbPrivileges as well)   
    
       if( !WTSQueryUserToken(consoleSessionId, &hToken))
    
    ...
    
       */
    
    
    
    DuplicateTokenEx(hToken, (TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID
    
                             | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE), 
    
                             NULL, SecurityIdentification, TokenPrimary, &hDupToken))
    
    
    
        // Look up the LUID for the TCB Name privilege.
    
    LookupPrivilegeValue(NULL, SE_TCB_NAME, &tp.Privileges[0].Luid))
    
    
    
        // Enable the TCB Name privilege in the token.
    
    tp.PrivilegeCount = 1;
    
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
        if (!AdjustTokenPrivileges(hDupToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, 0))
    
        {
    
            DisplayError("AdjustTokenPrivileges");
    
    		...
    
        }
    
        if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    
        {
    
            DEBUG( "Token does not have the necessary privilege.\n");
    
        } else {
    
            DEBUG( "No error reported from AdjustTokenPrivileges!\n");
    
        }																							// Never errors here
    
    
    
       DEBUG(LM_INFO, "Attempting setting of sessionId to: %d\n", consoleSessionId );
    
       if (!SetTokenInformation(hDupToken, TokenSessionId, &consoleSessionId, sizeof(DWORD)))
    
    		*** ALWAYS FAILS WITH 1314 HERE ***
    
    
    All the debug output looks fine up until the SetTokenInformation call - I see session 0 is my current process session, and in my case, it's trying to set session 1.  I'm logged into the W2K8 box via VNC, not RDC.  Showpriv and scqprivs on my service show all the privileges set that I can think of:

    • SeTakeOwnershipPrivilege
    • SeTcbPrivilege
    • SeChangeNotifyPrivilege
    • SeIncreaseQuotaPrivilege
    • SeAssignPrimaryTokenPrivilege
    • SeCreateTokenPrivilege

    Does anyone have any ideas on why this is failing, or suggestions for a better approach?  Note that this is for a server app, so there is not necessarily a logged in user at the time that I'm invoking this.

    Thanks in advance!
    Tuesday, March 16, 2010 10:39 PM

Answers

  • For anyone interested in the resolution of this issue:

    I discussed this issue with MS Support for the LogonSDK team.  It appears that it is not possible to fully impersonate an interactive user programatically, such that you get a physical console and associated GDI constructs, and we've essentially been "just lucky" that it worked until now.  They did confirm that session 0 isolation was the root cause of the regression.

    Their recommendation is to enable auto-logon to an interactive session, and refactor the service to speak to a new client component in the interactive session.  To address the security disadvantage of this, they recommend implementing a shell replacement to place the server in a "Kiosk" mode on logon (e.g. no Explorer access without appropriate credentials, etc.)

    On the up-side, this should address the issues we've been encountering with terminal service sessions killing our hardware acceleration.

    I will be submitting a request to MS consider this kind of "render farm" use case for "proxy user session" support in future releases, such that a server can spawn hardware-accelerated processes without the security compromise of requiring an existing client user process to be logged on at the console.

    • Marked as answer by Holtavolt Thursday, March 18, 2010 11:00 PM
    Thursday, March 18, 2010 11:00 PM

All replies

  • For anyone interested in the resolution of this issue:

    I discussed this issue with MS Support for the LogonSDK team.  It appears that it is not possible to fully impersonate an interactive user programatically, such that you get a physical console and associated GDI constructs, and we've essentially been "just lucky" that it worked until now.  They did confirm that session 0 isolation was the root cause of the regression.

    Their recommendation is to enable auto-logon to an interactive session, and refactor the service to speak to a new client component in the interactive session.  To address the security disadvantage of this, they recommend implementing a shell replacement to place the server in a "Kiosk" mode on logon (e.g. no Explorer access without appropriate credentials, etc.)

    On the up-side, this should address the issues we've been encountering with terminal service sessions killing our hardware acceleration.

    I will be submitting a request to MS consider this kind of "render farm" use case for "proxy user session" support in future releases, such that a server can spawn hardware-accelerated processes without the security compromise of requiring an existing client user process to be logged on at the console.

    • Marked as answer by Holtavolt Thursday, March 18, 2010 11:00 PM
    Thursday, March 18, 2010 11:00 PM
  • For anyone interested in the resolution of this issue:

    I discussed this issue with MS Support for the LogonSDK team.  It appears that it is not possible to fully impersonate an interactive user programatically, such that you get a physical console and associated GDI constructs, and we've essentially been "just lucky" that it worked until now.  They did confirm that session 0 isolation was the root cause of the regression.

    Their recommendation is to enable auto-logon to an interactive session, and refactor the service to speak to a new client component in the interactive session.  To address the security disadvantage of this, they recommend implementing a dvd shell replacement to place the server in a "Kiosk" mode on logon (e.g. no Explorer access without appropriate credentials, etc.)

    On the up-side, this should address the issues we've been encountering with terminal service sessions killing our hardware acceleration.

    I will be submitting a request to MS consider this kind of "render farm" use case for "proxy user session" support in future releases, such that a server can spawn hardware-accelerated processes without the security compromise of requiring an existing client user process to be logged on at the console.


    Thanks for your sharing. Indeed helpful to me.
    Tuesday, July 6, 2010 7:36 AM
  • Thanks, very useful, good solution. I was thinking, would it be possible to create your own authentication package register it with lsa and somehow with ActiveDirectory?

    Thursday, July 22, 2010 10:20 PM