Служба RenderFarm для Windows 2008: CreateProcessAsUser "Изоляция сеанса 0" и OpenGL
У меня есть устаревшая служба сервера Windows и (порожденное) приложение, которое отлично работает в XP-64 и W2K3, но не работает на W2K8. Я полагаю, что это из-за новой функции " изоляции сеанса 0".
Следовательно, я ищу примеры кода / параметры безопасности mojo, которые позволят вам создать новый процесс из службы Windows для Windows 2008 Server, чтобы я мог восстановить (и, возможно, превзойти) предыдущее поведение. Мне нужно решение, которое:
- Создает новый процесс в ненулевом сеансе, чтобы обойти ограничения изоляции сеанса 0 (нет доступа к графическому оборудованию из сеанса 0) - официальная строка MS на это:
Поскольку сеанс 0 больше не является сеансом пользователя, службы, работающие в сеансе 0, не имеют доступа к видеодрайверу. Это означает, что любая попытка службы визуализации графики завершится неудачей. Запрос разрешения экрана и глубины цвета в сеансе 0 сообщает о правильных результатах для системы вплоть до 1920x1200 максимум при 32 битах на пиксель.
Новый процесс получает станцию Windows / рабочий стол (например, winsta0/default), которую можно использовать для создания контроллеров домена Windows. Я нашел решение (которое запускает ОК в интерактивном сеансе) для этого здесь: Запуск интерактивного клиентского процесса в C++
Windows DC, когда он используется в качестве основы для перечисления OpenGL DescribePixelFormat, может находить и использовать формат с аппаратным ускорением (в системе, соответствующим образом оборудованной OpenGL.) Обратите внимание, что наше текущее решение работает нормально на XP-64 и W2K3 За исключением случаев, когда сеанс служб терминалов запущен (VNC работает нормально.) Решение, которое также позволило процессу работать (т. е. работать с аппаратным ускорением OpenGL, даже когда сеанс служб терминалов открыт), было бы фанатичным, хотя и не обязательным.
Я застрял в пункте № 1 в настоящее время, и хотя есть некоторые подобные публикации, которые обсуждают это (как это, и это - они не являются подходящими решениями, так как нет никакой гарантии того, что пользовательский сеанс уже вошел в систему, чтобы "принять" идентификатор сеанса от, и при этом я не работаю с учетной записью LocalSystem (я работаю с учетной записью домена для службы, для которой я могу настроить привилегии, в пределах разумного, хотя я предпочел бы не увеличивать приоритеты, чтобы включить SeTcbPrivileges.)
Например, вот заглушка, которая, как мне кажется, должна работать, но всегда возвращает ошибку 1314 при вызове SetTokenInformation (даже несмотря на то, что AdjustTokenPrivileges не вернула ошибок) Я также использовал несколько альтернативных стратегий, включающих LogonUser (вместо открытия существующей маркер процесса), но я не могу поменять идентификатор сессии.
Я также сомневаюсь в том, чтобы использовать WTSActiveConsoleSessionId во всех случаях (например, если не зарегистрирован ни один интерактивный пользователь) - хотя быстрый тест службы, работающей без сеансов, вошедших в систему, по-видимому, возвращал разумное значение сеанса (1).
Я удалил обработку ошибок для удобства чтения (все еще немного грязно - извинения)
//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 ***
Все выходные данные отладки выглядят нормально до вызова SetTokenInformation - я вижу, что сеанс 0 - это мой текущий сеанс процесса, и в моем случае он пытается установить сеанс 1 (результат WTSGetActiveConsoleSessionId). (Обратите внимание, что я вошел в окно W2K8 через VNC, а не RDC)
Итак - вопросы:
- Является ли этот подход допустимым, или все процессы, инициированные службой, преднамеренно ограничены сеансом 0?
- Есть ли лучший подход (если не считать "Запуск при входе в систему" и автоматический вход для серверов?)
- Что-то не так с этим кодом, или другой способ создания токена процесса, где я могу поменять идентификатор сеанса, чтобы указать, что я хочу порождать процесс в новом сеансе? Я попытался использовать LogonUser вместо OpenProcessToken, но это тоже не сработало. (Мне все равно, будут ли все порожденные процессы использовать один и тот же ненулевой сеанс или нет).
Любая помощь высоко ценится - спасибо!
3 ответа
Для тех, кто заинтересован в решении этого вопроса:
Я обсуждал эту проблему с MS Support для команды LogonSDK. Похоже, что невозможно полностью олицетворять интерактивного пользователя программно, так что вы получаете физическую консоль и связанные конструкции GDI, и нам, по сути, "просто повезло", что это работало до сих пор. Они подтвердили, что изоляция сеанса 0 была основной причиной регрессии.
Их рекомендация состоит в том, чтобы включить автоматический вход в интерактивный сеанс и реорганизовать службу для взаимодействия с новым клиентским компонентом в интерактивном сеансе. Чтобы устранить этот недостаток безопасности, они рекомендуют реализовать замену оболочки, чтобы при входе сервер переводился в режим "Киоск" (например, отсутствие доступа к проводнику без соответствующих учетных данных и т. Д.)
С другой стороны, это должно решить проблемы, с которыми мы столкнулись во время сеансов обслуживания терминалов, убивающих наше аппаратное ускорение.
Я буду отправлять запрос в MS, чтобы рассмотреть вариант использования "фермы рендеринга" для поддержки "сеанса прокси-пользователя" в будущих выпусках, чтобы сервер мог порождать процессы с аппаратным ускорением без компромисса в плане безопасности, требующего существующего пользовательского пользовательского процесса. войти в систему с консоли.
Я не закончил учебный курс, но я нашел учебное пособие "Обход изоляции Session 0" на сайте Microsoft-MSDN:
http://msdn.microsoft.com/en-us/windows7trainingcourse_sessionisolation_unit.aspx
У меня та же проблема изоляции сеанса 0, которая проявляется в запланированной задаче.
FarmComm запустит приложения по вашему выбору в сеансе 0 без графического интерфейса пользователя, но с доступом к графическому оборудованию, независимо от того, вошли пользователи или нет. Он также реагирует на действия пользователя в любом активном сеансе (включая сеанс 0 или "защищенный рабочий стол", если это активный сеанс). Таким образом, он предназначен для запуска приложений, когда пользователи бездействуют, и прекращает их при возобновлении работы пользователя из бездействия, но эти условия выполнения могут быть легко изменены в исходных сценариях AutoHotkey.
https://github.com/r-alex-hall/farmComm
Он порождает приложения в сеансе 0 "невидимо", но его очень легко изменить (измените переменную со значением "hide" на "show"), чтобы увидеть GUI порожденных процессов (если они имеют GUI). Однако, если они видны, они могут либо вызвать помехи Windows, чтобы увидеть "сообщения" в сеансе 0, и / или быть видимыми только в сеансе 0 (что, по-видимому, включает каждый раз, когда "защищенный рабочий стол" виден - для Например, когда рабочая станция заблокирована или отключена от сеансов пользователей, или пользователи не вошли в систему).
На момент написания этой статьи, если какой-либо сеанс удаленного рабочего стола (RDP) начинается, когда процессы, порожденные farmComm, запускаются, farmComm завершает эти процессы и пытается перезапустить их для ответа на сеанс RDP, который, если они являются приложениями, которые пытаются получить доступ графическое оборудование, может вызвать их сбой (потому что RDP ограничивает доступ к графическому оборудованию). Возможно, эту проблему RDP тоже можно обойти.,, или вы можете настроить источник так, чтобы он никогда не завершал процессы или никогда не переходил на другие сеансы. (ПРИМЕЧАНИЕ: возможное запланированное изменение состоит в том, чтобы позволить вам создавать сценарии, когда и когда farmComm завершает, не завершает, не приостанавливает или не возобновляет процессы - или в этом отношении, сценарий запускает совершенно другие процессы, когда пользователи выходят из режима ожидания).
Скрипты могут быть скомпилированы в исполняемые файлы, которые в моем дистрибутиве они есть.
Основными элементами этого набора инструментов являются конкретная версия paexec (которая запускает приложения в сеансе 0), а также очень надежные ответы AutoHotkey на действия пользователя (или его отсутствие) и получение системной информации о сеансе. Возможность запускать процессы "скрыто" (без видимого GUI) также через AutoHotkey.
Раскрытие информации: я написал сценарий (или закодировал) farmComm и выпустил его в общественное достояние.