开发者

Windows 2008 RenderFarm Service: CreateProcessAsUser "Session 0 Isolation" and OpenGL

I have a legacy Windows server service and (spawned) application that works fine in XP-64 and W2K3, but fails on W2K8. I believe it is because of the new "Session 0 isolation" feature.

Consequently, I'm looking for code samples/security settings mojo that let you create a new process from a windows service for Windows 2008 Server such that I can restore (and possibly surpass) the previous behavior. I need a solution that:

  1. Creates the new process in a non-zero session to get around session-0 isolation restrictions (no access to graphics hardware from session 0) - the official MS line on this is:

Because Session 0 is no longer a user session, services that are running in Session 0 do not have access to the video driver. This means that any attempt that a service makes to render graphics fails. Querying the display resolution and color depth in Session 0 reports the correct results for the system up to a maximum of 1920x1200 at 32 bits per pixel.

  1. The new process gets a windows station/desktop (e.g. winsta0/default) that can be used to create windows DCs. I've found a solution (that launches OK in an interactive session) for this here: Starting an Interactive Client Process in C++

  2. The windows DC, when used as the basis for an OpenGL DescribePixelFormat enumeration, is able to find and use the hardware-accelerated format (on a system appropriately equipped with OpenGL hardware.) Note that our current solution works OK on XP-64 and W2K3, except if a terminal services session is running (VNC works fine.) A solution that also allowed the process to work (i.e. run with OpenGL hardware acceleration even when a terminal services session is open) would be fanastic, although not required.

I'm stuck at item #1 currently, and although there are some similar postings that discuss this (like this, and this - they are not suitable solutions, as there is no guarantee of a user session logged in already to "take" a session id from, nor am I running from a LocalSystem account (I'm running from a domain account for the service, for which I can adjust the privileges of, within reason, although I'd prefer to not have to escalate priorities to include SeTcbPrivileges.)

For instance - here's a stub 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 (for instance, if no interactive user is logged in) - although a quick test of the service running with no sessions logged in seemed to return a reasonable session开发者_JAVA技巧 value (1).

I’ve removed error handling for ease of reading (still a bit messy - apologies)

    //Also tried using LogonUser(..) here
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 (the result of the WTSGetActiveConsoleSessionId). (Note that I'm logged into the W2K8 box via VNC, not RDC)

So - a the questions:

  1. Is this approach valid, or are all service-initiated processes restricted to session 0 intentionally?
  2. Is there a better approach (short of "Launch on logon" and auto-logon for the servers?)
  3. Is there something wrong with this code, or a different way to create a process token where I can swap out the session id to indicate I want to spawn the process in a new session? I did try using LogonUser instead of OpenProcessToken, but that didn't work either. (I don't care if all spawned processes share the same non-zero session or not at this point.)

Any help much appreciated - thanks!


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.


I have not completed the training course, but I have found a "Session 0 isolation workaround" tutorial on the Microsoft-MSDN site:

http://msdn.microsoft.com/en-us/windows7trainingcourse_sessionisolation_unit.aspx

I have the same session-0-isolation problem manifested within a scheduled task.


farmComm will launch the application(s) of your choosing into session 0, with no GUI visible, but with access to graphics hardware, whether users are logged in or not. It also responds to user activity in whatever session is active (including session 0 or the "secure desktop," when that is the active session). It is designed to launch applications thus when users are idle, and terminate them upon user resume from idle, but those run conditions could easily be altered in the source AutoHotkey scripts.

https://github.com/r-alex-hall/farmComm

It spawns applications in session 0 "invisibly," but it can very easily be modified (change a variable with the value "hide" to "show") to have the GUIs of spawned processes visible (if they have a GUI). If they are visible, however, they may either trigger Windows nags to see "messages" in session 0, and/or only be visible from session 0 (which, it seems, includes any time the "secure desktop" is visible--for example when a workstation is locked, or disconnected from user sessions, or no users are logged on).

At this writing, if any Remote Desktop (RDP) session is begun while processes spawned by farmComm run, farmComm will terminate those processes and attempt to re-launch them to respond to the RDP session, which, if they are applications which attempt to access graphics hardware, may cause them to crash (because RDP restricts access to graphics hardware). Probably this RDP problem could be worked around, too . . . or you can tweak the source to never terminate processes, or never migrate to other sessions. (NOTE: a possible planned change is to enable you to script whether and when farmComm terminates, does not terminate, suspends, or resumes processes--or for that matter, script it to run entirely different processes when users resume from idle).

The scripts can be compiled to executables, which in my distribution, they are.

The linchpins of this toolset are a particular version of paexec (which launches applications into session 0), and AutoHotkey's very reliable responses to user activity (or lack thereof), and retrieval of system information about a session. The option to launch processes "hidden" (with no GUI visible) is also via AutoHotkey.

Disclosure: I scripted (or coded) farmComm, and released it into the Public Domain.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜