问题描述
我有一个旧的 Windows 服务器服务和(生成的)应用程序,它在 XP-64 和 W2K3 中运行良好,但在 W2K8 上运行失败.我相信这是因为新的会话 0 隔离"功能.
因此,我正在寻找代码示例/安全设置 mojo,让您从 Windows 2008 Server 的 Windows 服务创建一个新进程,以便我可以恢复(并可能超越)以前的行为.我需要一个解决方案:
- 在非零会话中创建新进程以绕过会话 0 隔离限制(无法从会话 0 访问图形硬件) - 微软官方对此的说明是:
因为会话 0 不再是用户会话,正在运行的服务会话 0 无权访问视频驱动程序.这意味着任何服务尝试呈现图形失败.查询显示Session 中的分辨率和颜色深度0 报告正确的结果系统最高可达 1920x1200 at每像素 32 位.
新进程获得一个 windows 工作站/桌面(例如 winsta0/default),可用于创建 windows DC.我在这里找到了一个解决方案(在交互式会话中启动正常):在 C++ 中启动交互式客户端进程
Windows DC,当用作 OpenGL 的基础时DescribePixelFormat 枚举,能够找到并使用硬件加速格式(在适当配备 OpenGL 硬件的系统上.)请注意,我们当前的解决方案在 XP-64 和 W2K3 上运行正常,除非终端服务会话是运行(VNC 工作正常.)同时允许进程工作(即,即使在终端服务会话打开时也使用 OpenGL 硬件加速运行)的解决方案将非常棒,尽管不是必需的.
我目前被困在第 1 项上,尽管有一些类似的帖子讨论了这个问题(例如 this 和 this - 它们不是合适的解决方案,因为不能保证用户会话已经登录到获取"会话 ID,我也不是从 LocalSystem 帐户运行(我从服务的域帐户运行,为此我可以在合理范围内调整其权限,尽管我不想升级优先包括 SeTcbPrivileges.)
例如 - 这是一个我认为应该工作的存根,但总是在 SetTokenInformation 调用上返回错误 1314(即使 AdjustTokenPrivileges 没有返回错误)我也使用了一些涉及LogonUser"的替代策略(而不是打开现有的进程令牌),但我似乎无法换出会话 ID.
我也怀疑在所有情况下都使用 WTSActiveConsoleSessionId(例如,如果没有交互式用户登录) - 尽管在没有登录会话的情况下运行的服务的快速测试似乎返回了一个合理的会话值(1).
为了便于阅读,我删除了错误处理(仍然有点乱 - 抱歉)
//这里也试过使用LogonUser(..)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();/* 不能使用这个 - 需要非常高的权限(仅限本地,还有 SeTcbPrivileges)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)//查找 TCB 名称权限的 LUID.LookupPrivilegeValue(NULL, SE_TCB_NAME, &tp.Privileges[0].Luid))//在令牌中启用 TCB 名称权限.tp.PrivilegeCount = 1;tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;if (!AdjustTokenPrivileges(hDupToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, 0)){DisplayError("AdjustTokenPrivileges");...}如果 (GetLastError() == ERROR_NOT_ALL_ASSIGNED){DEBUG("令牌没有必要的权限.
");} 别的 {DEBUG( "AdjustTokenPrivileges 未报告错误!
");}//这里永远不会出错DEBUG(LM_INFO, "正在尝试将 sessionId 设置为:%d
", consoleSessionId );if (!SetTokenInformation(hDupToken, TokenSessionId, &consoleSessionId, sizeof(DWORD)))*** 总是以 1314 失败 ***
在调用 SetTokenInformation 之前,所有调试输出看起来都很好 - 我看到会话 0 是我当前的进程会话,在我的情况下,它正在尝试设置会话 1(WTSGetActiveConsoleSessionId 的结果).(请注意,我是通过 VNC 而不是 RDC 登录 W2K8 框的)
所以 - 一个问题:
- 这种方法是否有效,还是所有服务启动的进程都有意限制在会话 0 中?
- 是否有更好的方法(缺少登录时启动"和服务器自动登录?)
- 这段代码有什么问题吗,或者有什么不同的方式来创建一个进程令牌,我可以在其中交换会话 ID 以表明我想在新会话中生成进程?我确实尝试使用 LogonUser 而不是 OpenProcessToken,但这也不起作用.(我不在乎所有产生的进程此时是否共享同一个非零会话.)
非常感谢任何帮助 - 谢谢!
对于任何有兴趣解决此问题的人:
我与 LogonSDK 团队的 MS 支持人员讨论了这个问题.似乎不可能以编程方式完全模拟交互式用户,这样您就可以获得物理控制台和相关的 GDI 构造,而且我们基本上很幸运"直到现在它都能工作.他们确实确认会话 0 隔离是回归的根本原因.
他们的建议是启用自动登录到交互式会话,并重构服务以与交互式会话中的新客户端组件对话.为解决此问题的安全隐患,他们建议实施 shell 替换,以在登录时将服务器置于Kiosk"模式(例如,在没有适当凭据的情况下无法访问资源管理器等)
从好的方面来说,这应该可以解决我们在终端服务会话中遇到的导致硬件加速失效的问题.
我将向 MS 提交请求,在未来的版本中考虑这种渲染农场"用例以支持代理用户会话",这样服务器就可以生成硬件加速进程,而无需牺牲现有的安全性要在控制台登录的客户端用户进程.
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:
- 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:
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++
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 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.
");
} else {
DEBUG( "No error reported from AdjustTokenPrivileges!
");
} // Never errors here
DEBUG(LM_INFO, "Attempting setting of sessionId to: %d
", 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:
- Is this approach valid, or are all service-initiated processes restricted to session 0 intentionally?
- Is there a better approach (short of "Launch on logon" and auto-logon for the servers?)
- 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.
这篇关于Windows 2008 RenderFarm 服务:CreateProcessAsUser“Session 0 Isolation"和 OpenGL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!