You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1776 lines
55 KiB
1776 lines
55 KiB
/*************************************************************************
|
|
*
|
|
* api.c
|
|
*
|
|
* WinStation Control API's for WIN32 subsystem.
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Includes.
|
|
*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ntuser.h"
|
|
|
|
#include <winsta.h>
|
|
#include <wstmsg.h>
|
|
#include <icadd.h>
|
|
#include <winbasep.h>
|
|
|
|
#define SESSION_ROOT L"\\Sessions"
|
|
#define MAX_SESSION_PATH 256
|
|
|
|
NTSTATUS CsrPopulateDosDevices(VOID);
|
|
NTSTATUS CleanupSessionObjectDirectories(VOID);
|
|
|
|
BOOL CtxInitUser32(VOID);
|
|
|
|
USHORT gHRes = 0;
|
|
USHORT gVRes = 0;
|
|
USHORT gColorDepth = 0;
|
|
|
|
#if DBG
|
|
ULONG gulConnectCount = 0;
|
|
#endif // DBG
|
|
|
|
DWORD gLUIDDeviceMapsEnabled = 0;
|
|
|
|
/*
|
|
* The following are gotten from ICASRV.
|
|
*/
|
|
HANDLE G_IcaVideoChannel = NULL;
|
|
HANDLE G_IcaMouseChannel = NULL;
|
|
HANDLE G_IcaKeyboardChannel = NULL;
|
|
HANDLE G_IcaBeepChannel = NULL;
|
|
HANDLE G_IcaCommandChannel = NULL;
|
|
HANDLE G_IcaThinwireChannel = NULL;
|
|
WCHAR G_WinStationName[WINSTATIONNAME_LENGTH];
|
|
HANDLE G_DupIcaVideoChannel = NULL;
|
|
HANDLE G_DupIcaCommandChannel = NULL;
|
|
|
|
|
|
HANDLE G_ConsoleShadowVideoChannel;
|
|
HANDLE G_ConsoleShadowMouseChannel;
|
|
HANDLE G_ConsoleShadowKeyboardChannel;
|
|
HANDLE G_ConsoleShadowBeepChannel;
|
|
HANDLE G_ConsoleShadowCommandChannel;
|
|
HANDLE G_ConsoleShadowThinwireChannel;
|
|
BOOL G_fCursorShadow;
|
|
HANDLE G_DupConsoleShadowVideoChannel = NULL;
|
|
HANDLE G_DupConsoleShadowCommandChannel = NULL;
|
|
|
|
/*
|
|
* Definition for the WinStation control API's dispatch table
|
|
*/
|
|
typedef NTSTATUS (*PWIN32WINSTATION_API)(IN OUT PWINSTATION_APIMSG ApiMsg);
|
|
|
|
typedef struct _WIN32WINSTATION_DISPATCH {
|
|
PWIN32WINSTATION_API pWin32ApiProc;
|
|
} WIN32WINSTATION_DISPATCH, *PWIN32WINSTATION_DISPATCH;
|
|
|
|
NTSTATUS W32WinStationDoConnect(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationDoDisconnect(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationDoReconnect(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationExitWindows(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationTerminate(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationNtSecurity(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationDoMessage(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationThinwireStats(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationShadowSetup(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationShadowStart(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationShadowStop(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationShadowCleanup(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationPassthruEnable(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationPassthruDisable(IN OUT PWINSTATION_APIMSG);
|
|
|
|
// This is the counter part to SMWinStationBroadcastSystemMessage
|
|
NTSTATUS W32WinStationBroadcastSystemMessage(IN OUT PWINSTATION_APIMSG);
|
|
// This is the counter part to SMWinStationSendWindowMessage
|
|
NTSTATUS W32WinStationSendWindowMessage(IN OUT PWINSTATION_APIMSG);
|
|
|
|
NTSTATUS W32WinStationSetTimezone(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationDoNotify(IN OUT PWINSTATION_APIMSG);
|
|
NTSTATUS W32WinStationDoLoadStringNMessage(IN OUT PWINSTATION_APIMSG);
|
|
HANDLE CreateTermSrvReadyEvent();
|
|
|
|
/*
|
|
* WinStation API Dispatch Table
|
|
*
|
|
* Only the API's that WIN32 implements as opposed to ICASRV
|
|
* are entered here. The rest are NULL so that the same WinStation API
|
|
* numbers may be used by ICASRV and WIN32. If this table is
|
|
* changed, the table below must be modified too, as well as the API
|
|
* dispatch table in the ICASRV.
|
|
*/
|
|
|
|
|
|
WIN32WINSTATION_DISPATCH Win32WinStationDispatch[SMWinStationMaxApiNumber] = {
|
|
NULL, // create
|
|
NULL, // reset
|
|
NULL, // disconnect
|
|
NULL, // WCharLog
|
|
NULL, // ApiWinStationGetSMCommand,
|
|
NULL, // ApiWinStationBrokenConnection,
|
|
NULL, // ApiWinStationIcaReplyMessage,
|
|
NULL, // ApiWinStationIcaShadowHotkey,
|
|
W32WinStationDoConnect,
|
|
W32WinStationDoDisconnect,
|
|
W32WinStationDoReconnect,
|
|
W32WinStationExitWindows,
|
|
W32WinStationTerminate,
|
|
W32WinStationNtSecurity,
|
|
W32WinStationDoMessage,
|
|
NULL,
|
|
W32WinStationThinwireStats,
|
|
W32WinStationShadowSetup,
|
|
W32WinStationShadowStart,
|
|
W32WinStationShadowStop,
|
|
W32WinStationShadowCleanup,
|
|
W32WinStationPassthruEnable,
|
|
W32WinStationPassthruDisable,
|
|
W32WinStationSetTimezone,
|
|
NULL, // [AraBern] this was missing: SMWinStationInitialProgram
|
|
NULL, // [AraBern] this was missing: SMWinStationNtsdDebug
|
|
W32WinStationBroadcastSystemMessage,
|
|
W32WinStationSendWindowMessage,
|
|
W32WinStationDoNotify,
|
|
W32WinStationDoLoadStringNMessage,
|
|
NULL, // SMWinStationWindowInvalid
|
|
};
|
|
|
|
#if DBG
|
|
PSZ Win32WinStationAPIName[SMWinStationMaxApiNumber] = {
|
|
"SmWinStationCreate",
|
|
"SmWinStationReset",
|
|
"SmWinStationDisconnect",
|
|
"SmWinStationWCharLog",
|
|
"SmWinStationGetSMCommand",
|
|
"SmWinStationBrokenConnection",
|
|
"SmWinStationIcaReplyMessage",
|
|
"SmWinStationIcaShadowHotkey",
|
|
"SmWinStationDoConnect",
|
|
"SmWinStationDoDisconnect",
|
|
"SmWinStationDoReconnect",
|
|
"SmWinStationExitWindows",
|
|
"SmWinStationTerminate",
|
|
"SmWinStationNtSecurity",
|
|
"SmWinStationDoMessage",
|
|
"SmWinstationDoBreakPoint",
|
|
"SmWinStationThinwireStats",
|
|
"SmWinStationShadowSetup",
|
|
"SmWinStationShadowStart",
|
|
"SmWinStationShadowStop",
|
|
"SmWinStationShadowCleanup",
|
|
"SmWinStationPassthruEnable",
|
|
"SmWinStationPassthruDisable",
|
|
"SMWinStationSetTimeZone",
|
|
"SMWinStationInitialProgram",
|
|
"SMWinStationNtsdDebug",
|
|
"W32WinStationBroadcastSystemMessage",
|
|
"W32WinStationSendWindowMessage",
|
|
"W32WinStationDoNotify",
|
|
"W32WinStationDoLoadStringNMessage",
|
|
"SMWinStationWindowInvalid",
|
|
};
|
|
#endif
|
|
|
|
NTSTATUS TerminalServerRequestThread(PVOID);
|
|
NTSTATUS Win32CommandChannelThread(PVOID);
|
|
NTSTATUS Win32ConsoleShadowChannelThread(PVOID);
|
|
NTSTATUS RemoteDoMessage(PWINSTATION_APIMSG pMsg);
|
|
NTSTATUS RemoteDoLoadStringNMessage(PWINSTATION_APIMSG pMsg);
|
|
NTSTATUS MultiUserSpoolerInit();
|
|
|
|
extern HANDLE g_hDoMessageEvent;
|
|
|
|
NTSTATUS RemoteDoBroadcastSystemMessage(PWINSTATION_APIMSG pMsg);
|
|
NTSTATUS RemoteDoSendWindowMessage(PWINSTATION_APIMSG pMsg);
|
|
BOOL CancelExitWindows(VOID);
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationAPIInit
|
|
*
|
|
* Creates and initializes the WinStation API port and thread.
|
|
*
|
|
* ENTRY:
|
|
* No Parameters
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationAPIInit(
|
|
VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
CLIENT_ID ClientId;
|
|
HANDLE ThreadHandle;
|
|
KPRIORITY Priority;
|
|
ULONG LUIDDeviceMapsEnabled;
|
|
#if DBG
|
|
static BOOL Inited = FALSE;
|
|
#endif
|
|
|
|
#if DBG
|
|
UserAssert(Inited == FALSE);
|
|
Inited = TRUE;
|
|
#endif
|
|
|
|
gSessionId = NtCurrentPeb()->SessionId;
|
|
|
|
//
|
|
// Check if LUID DosDevices are enabled.
|
|
//
|
|
Status = NtQueryInformationProcess(NtCurrentProcess(),
|
|
ProcessLUIDDeviceMapsEnabled,
|
|
&LUIDDeviceMapsEnabled,
|
|
sizeof(LUIDDeviceMapsEnabled),
|
|
NULL);
|
|
if (NT_SUCCESS(Status)) {
|
|
gLUIDDeviceMapsEnabled = LUIDDeviceMapsEnabled;
|
|
}
|
|
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0,
|
|
0,
|
|
TerminalServerRequestThread,
|
|
NULL,
|
|
&ThreadHandle,
|
|
&ClientId);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Failed to create TerminalServerRequestThread, Status = 0x%x",
|
|
Status);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Add thread to server thread pool.
|
|
*/
|
|
CsrAddStaticServerThread(ThreadHandle, &ClientId, 0);
|
|
|
|
/*
|
|
* Boost priority of ICA SRV Request thread
|
|
*/
|
|
Priority = THREAD_BASE_PRIORITY_MAX;
|
|
|
|
Status = NtSetInformationThread(ThreadHandle, ThreadBasePriority,
|
|
&Priority, sizeof(Priority));
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Failed to set thread priority, Status = 0x%x",
|
|
Status);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Resume the thread now that we've initialized things.
|
|
*/
|
|
NtResumeThread(ThreadHandle, NULL);
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
TerminalServerRequestThread(
|
|
PVOID ThreadParameter)
|
|
{
|
|
UNICODE_STRING PortName;
|
|
SECURITY_QUALITY_OF_SERVICE DynamicQos;
|
|
WINSTATIONAPI_CONNECT_INFO info;
|
|
ULONG ConnectInfoLength;
|
|
WINSTATION_APIMSG ApiMsg;
|
|
PWIN32WINSTATION_DISPATCH pDispatch;
|
|
NTSTATUS Status;
|
|
REMOTE_PORT_VIEW ServerView;
|
|
HANDLE CsrStartHandle, hevtTermSrvInit;
|
|
HANDLE hLPCPort = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(ThreadParameter);
|
|
|
|
if (NtCurrentPeb()->SessionId == 0) {
|
|
hevtTermSrvInit = CreateTermSrvReadyEvent();
|
|
} else {
|
|
hevtTermSrvInit = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\TermSrvReadyEvent");
|
|
}
|
|
|
|
if (hevtTermSrvInit == NULL) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Couldn't create TermSrvReadyEvent. Error = 0x%x",
|
|
GetLastError());
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
NtWaitForSingleObject(hevtTermSrvInit, FALSE, NULL);
|
|
NtClose(hevtTermSrvInit);
|
|
|
|
/*
|
|
* Connect to terminal server API port.
|
|
*/
|
|
DynamicQos.ImpersonationLevel = SecurityImpersonation;
|
|
DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
DynamicQos.EffectiveOnly = TRUE;
|
|
|
|
RtlInitUnicodeString(&PortName, L"\\SmSsWinStationApiPort");
|
|
|
|
/*
|
|
* Init the REMOTE_VIEW structure.
|
|
*/
|
|
ServerView.Length = sizeof(ServerView);
|
|
ServerView.ViewSize = 0;
|
|
ServerView.ViewBase = 0;
|
|
|
|
/*
|
|
* Fill in the ConnectInfo structure with our access request mask.
|
|
*/
|
|
info.Version = CITRIX_WINSTATIONAPI_VERSION;
|
|
info.RequestedAccess = 0;
|
|
ConnectInfoLength = sizeof(WINSTATIONAPI_CONNECT_INFO);
|
|
|
|
Status = NtConnectPort(&hLPCPort,
|
|
&PortName,
|
|
&DynamicQos,
|
|
NULL, // ClientView
|
|
&ServerView,
|
|
NULL, // Max message length [select default]
|
|
(PVOID)&info,
|
|
&ConnectInfoLength);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "TerminalServerRequestThread: Failed to connect to LPC port: Status = 0x%x", Status);
|
|
UserExitWorkerThread(Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Terminal Server calls into Session Manager to create a new Hydra session.
|
|
// The session manager creates and resume a new session and returns to Terminal
|
|
// server the session id of the new session. There is a race condition where
|
|
// CSR can resume and call into terminal server before terminal server can
|
|
// store the session id in its internal structure. To prevent this CSR will
|
|
// wait here on a named event which will be set by Terminal server once it
|
|
// gets the sessionid for the newly created session
|
|
//
|
|
|
|
if (NtCurrentPeb()->SessionId != 0) {
|
|
CsrStartHandle = CreateEvent(NULL, TRUE, FALSE, L"CsrStartEvent");
|
|
|
|
if (!CsrStartHandle) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Failed to create CsrStartEvent. Error = 0x%x",
|
|
GetLastError());
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Exit;
|
|
} else {
|
|
Status = NtWaitForSingleObject(CsrStartHandle, FALSE, NULL);
|
|
|
|
NtClose(CsrStartHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"Wait for CsrStartEvent failed: Status = 0x%x", Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory(&ApiMsg, sizeof(ApiMsg));
|
|
for (;;) {
|
|
|
|
/*
|
|
* Initialize LPC message fields.
|
|
*/
|
|
ApiMsg.h.u1.s1.DataLength = sizeof(ApiMsg) - sizeof(PORT_MESSAGE);
|
|
ApiMsg.h.u1.s1.TotalLength = sizeof(ApiMsg);
|
|
ApiMsg.h.u2.s2.Type = 0; // Kernel will fill in message type
|
|
ApiMsg.h.u2.s2.DataInfoOffset = 0;
|
|
ApiMsg.ApiNumber = SMWinStationGetSMCommand;
|
|
|
|
Status = NtRequestWaitReplyPort(hLPCPort,
|
|
(PPORT_MESSAGE)&ApiMsg,
|
|
(PPORT_MESSAGE)&ApiMsg);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"TerminalServerRequestThread wait failed: Status 0x%x", Status);
|
|
break;
|
|
}
|
|
|
|
if (ApiMsg.ApiNumber >= SMWinStationMaxApiNumber) {
|
|
RIPMSG1(RIP_WARNING,
|
|
"TerminalServerRequestThread: Bad API number %d", ApiMsg.ApiNumber);
|
|
|
|
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
|
|
} else {
|
|
/*
|
|
* We must VALIDATE which ones are implemented here.
|
|
*/
|
|
pDispatch = &Win32WinStationDispatch[ApiMsg.ApiNumber];
|
|
|
|
if (pDispatch->pWin32ApiProc) {
|
|
BOOL bRestoreDesktop = FALSE;
|
|
USERTHREAD_USEDESKTOPINFO utudi;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* For all the win32k callouts - with the exception of
|
|
* terminate and timezone setting - set this thread to the
|
|
* current desktop.
|
|
*/
|
|
if (ApiMsg.ApiNumber != SMWinStationTerminate && ApiMsg.ApiNumber != SMWinStationSetTimeZone) {
|
|
BOOL bAttachDesktop = TRUE;
|
|
if (ApiMsg.ApiNumber == SMWinStationDoConnect) {
|
|
WINSTATIONDOCONNECTMSG* m = &ApiMsg.u.DoConnect;
|
|
if (!m->ConsoleShadowFlag) {
|
|
bAttachDesktop = FALSE;
|
|
}
|
|
}
|
|
if (bAttachDesktop) {
|
|
utudi.hThread = NULL;
|
|
utudi.drdRestore.pdeskRestore = NULL;
|
|
Status = NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseActiveDesktop,
|
|
&utudi, sizeof(utudi));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
bRestoreDesktop = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Call the API
|
|
*/
|
|
if (Status == STATUS_SUCCESS) {
|
|
ApiMsg.ReturnedStatus = (pDispatch->pWin32ApiProc)(&ApiMsg);
|
|
} else {
|
|
ApiMsg.ReturnedStatus = Status;
|
|
}
|
|
|
|
if (bRestoreDesktop) {
|
|
NtUserSetInformationThread(NtCurrentThread(),
|
|
UserThreadUseDesktop,
|
|
&utudi,
|
|
sizeof(utudi));
|
|
}
|
|
|
|
/*
|
|
* Let's bail ...
|
|
*/
|
|
if (ApiMsg.ApiNumber == SMWinStationTerminate) {
|
|
break;
|
|
}
|
|
} else {
|
|
// This control API is not implemented in WIN32
|
|
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if (hLPCPort) {
|
|
NtClose(hLPCPort);
|
|
}
|
|
|
|
UserExitWorkerThread(Status);
|
|
return Status;
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
W32WinStationDumpReconnectInfo(
|
|
WINSTATIONDORECONNECTMSG *pDoReconnect,
|
|
BOOLEAN bReconnect)
|
|
{
|
|
PSTR pCallerName;
|
|
|
|
if (bReconnect) {
|
|
pCallerName = "W32WinStationDoReconnect";
|
|
} else {
|
|
pCallerName = "W32WinStationDoConnect";
|
|
}
|
|
|
|
DbgPrint(pCallerName);
|
|
DbgPrint(" - Display resolution information for session %d :\n", gSessionId);
|
|
|
|
DbgPrint("\tProtocolType : %04d\n", pDoReconnect->ProtocolType);
|
|
DbgPrint("\tHRes : %04d\n", pDoReconnect->HRes);
|
|
DbgPrint("\tVRes : %04d\n", pDoReconnect->VRes);
|
|
DbgPrint("\tColorDepth : %04d\n", pDoReconnect->ColorDepth);
|
|
|
|
DbgPrint("\tKeyboardType : %d\n", pDoReconnect->KeyboardType);
|
|
DbgPrint("\tKeyboardSubType : %d\n", pDoReconnect->KeyboardSubType);
|
|
DbgPrint("\tKeyboardFunctionKey : %d\n", pDoReconnect->KeyboardFunctionKey);
|
|
}
|
|
#else
|
|
#define W32WinStationDumpReconnectInfo(p, b)
|
|
#endif // DBG
|
|
|
|
NTSTATUS
|
|
W32WinStationDoConnect(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WINSTATIONDOCONNECTMSG* m = &pMsg->u.DoConnect;
|
|
WCHAR DisplayDriverName[10];
|
|
CLIENT_ID ClientId;
|
|
HANDLE ThreadHandle = NULL;
|
|
KPRIORITY Priority;
|
|
DOCONNECTDATA DoConnectData;
|
|
WINSTATIONDORECONNECTMSG mDoReconnect;
|
|
HANDLE hDisplayChangeEvent = NULL;
|
|
|
|
|
|
if (!m->ConsoleShadowFlag) {
|
|
UserAssert(gulConnectCount == 0);
|
|
|
|
if ((gLUIDDeviceMapsEnabled == 0) && (gSessionId != 0)) {
|
|
/*
|
|
* Populate the sessions \DosDevices from
|
|
* the current consoles settings
|
|
*/
|
|
Status = CsrPopulateDosDevices();
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "CsrPopulateDosDevices failed with Status %lx", Status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
G_IcaVideoChannel = m->hIcaVideoChannel;
|
|
G_IcaMouseChannel = m->hIcaMouseChannel;
|
|
G_IcaKeyboardChannel = m->hIcaKeyboardChannel;
|
|
G_IcaBeepChannel = m->hIcaBeepChannel;
|
|
G_IcaCommandChannel = m->hIcaCommandChannel;
|
|
G_IcaThinwireChannel = m->hIcaThinwireChannel;
|
|
|
|
RtlZeroMemory(G_WinStationName, sizeof(G_WinStationName));
|
|
memcpy(G_WinStationName, m->WinStationName,
|
|
min(sizeof(G_WinStationName), sizeof(m->WinStationName)));
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
G_IcaVideoChannel,
|
|
NtCurrentProcess(),
|
|
&G_DupIcaVideoChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "NtDuplicateObject failed with Status %lx", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
G_IcaCommandChannel,
|
|
NtCurrentProcess(),
|
|
&G_DupIcaCommandChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "NtDuplicateObject failed with Status %lx", Status);
|
|
NtClose(G_DupIcaVideoChannel);
|
|
G_DupIcaVideoChannel = NULL;
|
|
goto Exit;
|
|
}
|
|
} else {
|
|
G_ConsoleShadowVideoChannel = m->hIcaVideoChannel;
|
|
G_ConsoleShadowMouseChannel = m->hIcaMouseChannel;
|
|
G_ConsoleShadowKeyboardChannel = m->hIcaKeyboardChannel;
|
|
G_ConsoleShadowBeepChannel = m->hIcaBeepChannel;
|
|
G_ConsoleShadowCommandChannel = m->hIcaCommandChannel;
|
|
G_ConsoleShadowThinwireChannel = m->hIcaThinwireChannel;
|
|
|
|
G_fCursorShadow = FALSE;
|
|
SystemParametersInfo(SPI_GETCURSORSHADOW, 0, &G_fCursorShadow, 0);
|
|
if (G_fCursorShadow) {
|
|
SystemParametersInfo(SPI_SETCURSORSHADOW, 0, FALSE, 0);
|
|
}
|
|
|
|
hDisplayChangeEvent = m->hDisplayChangeEvent;
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
G_ConsoleShadowVideoChannel,
|
|
NtCurrentProcess(),
|
|
&G_DupConsoleShadowVideoChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "NtDuplicateObject failed with Status %lx", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
G_ConsoleShadowCommandChannel,
|
|
NtCurrentProcess(),
|
|
&G_DupConsoleShadowCommandChannel,
|
|
0,
|
|
0,
|
|
DUPLICATE_SAME_ACCESS );
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "NtDuplicateObject failed with Status %lx", Status);
|
|
NtClose(G_DupConsoleShadowVideoChannel);
|
|
G_DupConsoleShadowVideoChannel = NULL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This must be 8 unicode characters (file name) plus two zero wide characters.
|
|
*/
|
|
RtlZeroMemory(DisplayDriverName, sizeof(DisplayDriverName));
|
|
memcpy(DisplayDriverName, m->DisplayDriverName, sizeof(DisplayDriverName) - 2);
|
|
|
|
/*
|
|
* Give the information to the WIN32 driver.
|
|
*/
|
|
RtlZeroMemory(&DoConnectData, sizeof(DoConnectData));
|
|
|
|
DoConnectData.fMouse = m->fMouse;
|
|
DoConnectData.IcaBeepChannel = m->hIcaBeepChannel;
|
|
DoConnectData.IcaVideoChannel = m->hIcaVideoChannel;
|
|
DoConnectData.IcaMouseChannel = m->hIcaMouseChannel;
|
|
DoConnectData.fEnableWindowsKey = m->fEnableWindowsKey;
|
|
|
|
DoConnectData.IcaKeyboardChannel = m->hIcaKeyboardChannel;
|
|
DoConnectData.IcaThinwireChannel = m->hIcaThinwireChannel;
|
|
DoConnectData.fClientDoubleClickSupport = m->fClientDoubleClickSupport;
|
|
|
|
DoConnectData.DisplayChangeEvent = hDisplayChangeEvent;
|
|
|
|
/*
|
|
* Give the information to the keyboard type/subtype/number of functions.
|
|
*/
|
|
DoConnectData.ClientKeyboardType.Type = m->KeyboardType;
|
|
DoConnectData.ClientKeyboardType.SubType = m->KeyboardSubType;
|
|
DoConnectData.ClientKeyboardType.FunctionKey = m->KeyboardFunctionKey;
|
|
|
|
memcpy(DoConnectData.WinStationName, G_WinStationName,
|
|
min(sizeof(G_WinStationName), sizeof(DoConnectData.WinStationName)));
|
|
|
|
DoConnectData.drProtocolType = m->ProtocolType;
|
|
DoConnectData.drPelsHeight = m->VRes;
|
|
DoConnectData.drPelsWidth = m->HRes;
|
|
DoConnectData.drBitsPerPel = m->ColorDepth;
|
|
|
|
mDoReconnect.ProtocolType = m->ProtocolType;
|
|
mDoReconnect.HRes = m->HRes;
|
|
mDoReconnect.VRes = m->VRes;
|
|
mDoReconnect.ColorDepth = m->ColorDepth;
|
|
|
|
W32WinStationDumpReconnectInfo(&mDoReconnect, FALSE);
|
|
|
|
/* Give winstation protocol name */
|
|
RtlZeroMemory(DoConnectData.ProtocolName, sizeof(DoConnectData.ProtocolName));
|
|
memcpy(DoConnectData.ProtocolName, m->ProtocolName, sizeof(DoConnectData.ProtocolName) - 2);
|
|
|
|
/* Give winstation audio drver name */
|
|
RtlZeroMemory(DoConnectData.AudioDriverName, sizeof(DoConnectData.AudioDriverName));
|
|
memcpy(DoConnectData.AudioDriverName, m->AudioDriverName, sizeof(DoConnectData.AudioDriverName) - 2);
|
|
|
|
|
|
DoConnectData.fConsoleShadowFlag = (BOOL) m->ConsoleShadowFlag;
|
|
|
|
Status = NtUserRemoteConnect(&DoConnectData,
|
|
ARRAY_SIZE(DisplayDriverName),
|
|
DisplayDriverName);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RIPMSG1(RIP_WARNING, "NtUserRemoteConnect failed with Status %lx", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
TRUE,
|
|
0L,
|
|
0L,
|
|
0L,
|
|
Win32CommandChannelThread,
|
|
(PVOID)m->ConsoleShadowFlag,
|
|
&ThreadHandle,
|
|
&ClientId);
|
|
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RIPMSG1(RIP_WARNING, "RtlCreateUserThread failed with Status %lx", Status);
|
|
if (G_DupIcaVideoChannel) {
|
|
NtClose(G_DupIcaVideoChannel);
|
|
G_DupIcaVideoChannel = NULL;
|
|
}
|
|
|
|
if (G_DupIcaCommandChannel) {
|
|
NtClose(G_DupIcaCommandChannel);
|
|
G_DupIcaCommandChannel = NULL;
|
|
}
|
|
|
|
if (G_DupConsoleShadowVideoChannel) {
|
|
NtClose(G_DupConsoleShadowVideoChannel);
|
|
G_DupConsoleShadowVideoChannel = NULL;
|
|
}
|
|
|
|
if (G_DupConsoleShadowCommandChannel) {
|
|
NtClose(G_DupConsoleShadowCommandChannel);
|
|
G_DupConsoleShadowCommandChannel = NULL;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Add thread to server thread pool only if we are in regular sessions.
|
|
* In console shadow, we don't do this as it will leak the handle --
|
|
* CSRSS doesn't close these handles.
|
|
*/
|
|
if (!m->ConsoleShadowFlag && CsrAddStaticServerThread(ThreadHandle, &ClientId, 0) == NULL) {
|
|
RIPMSG0(RIP_WARNING, "CsrAddStaticServerThread failed");
|
|
|
|
/*
|
|
* Close the handle as the above function does not have a reference
|
|
* to us in its list.
|
|
*/
|
|
CloseHandle(ThreadHandle);
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Boost priority of thread
|
|
*/
|
|
Priority = THREAD_BASE_PRIORITY_MAX;
|
|
|
|
Status = NtSetInformationThread(ThreadHandle, ThreadBasePriority,
|
|
&Priority, sizeof(Priority));
|
|
|
|
UserAssert(NT_SUCCESS(Status));
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RIPMSG1(RIP_WARNING, "NtSetInformationThread failed with Status %lx", Status);
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Resume the thread now that we've initialized things.
|
|
*/
|
|
NtResumeThread(ThreadHandle, NULL);
|
|
|
|
if (!m->ConsoleShadowFlag) {
|
|
if (CsrConnectToUser() == NULL) {
|
|
RIPMSG0(RIP_WARNING, "CsrConnectToUser failed");
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
if (!CtxInitUser32()) {
|
|
RIPMSG0(RIP_WARNING, "CtxInitUser32 failed");
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
/*
|
|
* Create the Spooler service thread
|
|
*/
|
|
if (gSessionId != 0) {
|
|
Status = MultiUserSpoolerInit();
|
|
}
|
|
|
|
/*
|
|
* Save the resolution
|
|
*/
|
|
gHRes = mDoReconnect.HRes;
|
|
gVRes = mDoReconnect.VRes;
|
|
gColorDepth = mDoReconnect.ColorDepth;
|
|
} else {
|
|
/*
|
|
* By now, the object has been referenced in kernel mode.
|
|
*/
|
|
CloseHandle(hDisplayChangeEvent);
|
|
}
|
|
|
|
Exit:
|
|
|
|
#if DBG
|
|
if (!m->ConsoleShadowFlag) {
|
|
if (Status == STATUS_SUCCESS) {
|
|
gulConnectCount++;
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
/*
|
|
* Close the thread handle now if we are in console shadow
|
|
*/
|
|
if (m->ConsoleShadowFlag && NULL != ThreadHandle) {
|
|
CloseHandle(ThreadHandle);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationDoDisconnect(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WINSTATIONDODISCONNECTMSG *m = &pMsg->u.DoDisconnect;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
if (!m->ConsoleShadowFlag) {
|
|
RtlZeroMemory(G_WinStationName, sizeof(G_WinStationName));
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_XXXREMOTEDISCONNECT);
|
|
} else {
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_XXXREMOTECONSOLESHADOWSTOP);
|
|
|
|
if (G_ConsoleShadowMouseChannel) {
|
|
CloseHandle(G_ConsoleShadowMouseChannel);
|
|
G_ConsoleShadowMouseChannel = NULL;
|
|
}
|
|
if (G_ConsoleShadowKeyboardChannel) {
|
|
CloseHandle(G_ConsoleShadowKeyboardChannel);
|
|
G_ConsoleShadowKeyboardChannel = NULL;
|
|
}
|
|
|
|
// Instead send a IOCTL to termdd
|
|
if (G_ConsoleShadowCommandChannel) {
|
|
Status = NtDeviceIoControlFile(
|
|
G_ConsoleShadowCommandChannel,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
IOCTL_ICA_CHANNEL_CLOSE_COMMAND_CHANNEL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
CloseHandle(G_ConsoleShadowCommandChannel);
|
|
G_ConsoleShadowCommandChannel = NULL;
|
|
}
|
|
|
|
if (G_ConsoleShadowVideoChannel) {
|
|
Status = NtDeviceIoControlFile(
|
|
G_ConsoleShadowVideoChannel,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
IOCTL_ICA_CHANNEL_CLOSE_COMMAND_CHANNEL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
CloseHandle(G_ConsoleShadowVideoChannel);
|
|
G_ConsoleShadowVideoChannel = NULL;
|
|
}
|
|
|
|
if (G_ConsoleShadowBeepChannel) {
|
|
CloseHandle(G_ConsoleShadowBeepChannel);
|
|
G_ConsoleShadowBeepChannel = NULL;
|
|
}
|
|
|
|
if (G_ConsoleShadowThinwireChannel) {
|
|
CloseHandle(G_ConsoleShadowThinwireChannel);
|
|
G_ConsoleShadowThinwireChannel = NULL;
|
|
}
|
|
|
|
if (G_fCursorShadow) {
|
|
SystemParametersInfo(SPI_SETCURSORSHADOW, 0, (LPVOID)TRUE, 0);
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemoteDisconnect failed with Status %lx", Status);
|
|
}
|
|
|
|
#if DBG
|
|
if (!m->ConsoleShadowFlag && Status == STATUS_SUCCESS) {
|
|
gulConnectCount--;
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationDoReconnect(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DORECONNECTDATA DoReconnectData;
|
|
WINSTATIONDORECONNECTMSG* m = &pMsg->u.DoReconnect;
|
|
|
|
UserAssert(gulConnectCount == 0);
|
|
|
|
RtlZeroMemory(&DoReconnectData, sizeof(DoReconnectData));
|
|
|
|
DoReconnectData.fMouse = m->fMouse;
|
|
DoReconnectData.fEnableWindowsKey = m->fEnableWindowsKey;
|
|
DoReconnectData.fClientDoubleClickSupport = m->fClientDoubleClickSupport;
|
|
|
|
memcpy(G_WinStationName, m->WinStationName,
|
|
min(sizeof(G_WinStationName), sizeof(m->WinStationName)));
|
|
|
|
memcpy(DoReconnectData.WinStationName, G_WinStationName,
|
|
min(sizeof(G_WinStationName), sizeof(DoReconnectData.WinStationName)));
|
|
|
|
DoReconnectData.drProtocolType = m->ProtocolType;
|
|
DoReconnectData.drPelsHeight = m->VRes;
|
|
DoReconnectData.drPelsWidth = m->HRes;
|
|
DoReconnectData.drBitsPerPel = m->ColorDepth;
|
|
if (m->fDynamicReconnect) {
|
|
DoReconnectData.fChangeDisplaySettings = TRUE;
|
|
} else {
|
|
DoReconnectData.fChangeDisplaySettings = FALSE;
|
|
}
|
|
// DoReconnectData.drDisplayFrequency is no yet setup
|
|
DoReconnectData.drDisplayFrequency = 0;
|
|
|
|
/*
|
|
* Give the information to the keyboard type/subtype/number of functions.
|
|
*/
|
|
DoReconnectData.ClientKeyboardType.Type = m->KeyboardType;
|
|
DoReconnectData.ClientKeyboardType.SubType = m->KeyboardSubType;
|
|
DoReconnectData.ClientKeyboardType.FunctionKey = m->KeyboardFunctionKey;
|
|
|
|
|
|
RtlZeroMemory(DoReconnectData.DisplayDriverName, sizeof(DoReconnectData.DisplayDriverName));
|
|
|
|
UserAssert(sizeof(m->DisplayDriverName) <= sizeof(WCHAR) * DR_DISPLAY_DRIVER_NAME_LENGTH);
|
|
memcpy(DoReconnectData.DisplayDriverName, m->DisplayDriverName, sizeof(m->DisplayDriverName) - 2);
|
|
|
|
RtlZeroMemory(DoReconnectData.ProtocolName, sizeof(DoReconnectData.ProtocolName));
|
|
|
|
UserAssert(sizeof(m->DisplayDriverName) <= sizeof(WCHAR) * WPROTOCOLNAME_LENGTH);
|
|
memcpy(DoReconnectData.ProtocolName, m->ProtocolName, sizeof(m->ProtocolName) - 2);
|
|
|
|
RtlZeroMemory(DoReconnectData.AudioDriverName, sizeof(DoReconnectData.AudioDriverName));
|
|
memcpy(DoReconnectData.AudioDriverName, m->AudioDriverName, sizeof(m->AudioDriverName) - 2);
|
|
|
|
W32WinStationDumpReconnectInfo(m, TRUE);
|
|
|
|
/*
|
|
* Give the information to the WIN32 driver.
|
|
*/
|
|
|
|
Status = (NTSTATUS)NtUserCallOneParam((ULONG_PTR)&DoReconnectData,
|
|
SFI_XXXREMOTERECONNECT);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemoteReconnect failed with Status %lx", Status);
|
|
} else {
|
|
|
|
/*
|
|
* Save the resolution
|
|
*/
|
|
gHRes = m->HRes;
|
|
gVRes = m->VRes;
|
|
gColorDepth = m->ColorDepth;
|
|
|
|
#if DBG
|
|
gulConnectCount++;
|
|
#endif // DBG
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationDoNotify(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DONOTIFYDATA DoNotifyData;
|
|
WINSTATIONDONOTIFYMSG* m = &pMsg->u.DoNotify;
|
|
|
|
switch (m->NotifyEvent) {
|
|
case WinStation_Notify_DisableScrnSaver:
|
|
DoNotifyData.NotifyEvent = Notify_DisableScrnSaver;
|
|
break;
|
|
|
|
case WinStation_Notify_EnableScrnSaver:
|
|
DoNotifyData.NotifyEvent = Notify_EnableScrnSaver;
|
|
break;
|
|
|
|
case WinStation_Notify_Disconnect:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_Disconnect;
|
|
break;
|
|
|
|
case WinStation_Notify_SyncDisconnect:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_SyncDisconnect;
|
|
break;
|
|
|
|
case WinStation_Notify_Reconnect:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_Reconnect;
|
|
break;
|
|
|
|
case WinStation_Notify_PreReconnect:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_PreReconnect;
|
|
break;
|
|
|
|
case WinStation_Notify_PreReconnectDesktopSwitch:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_PreReconnectDesktopSwitch;
|
|
break;
|
|
|
|
case WinStation_Notify_HelpAssistantShadowStart:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_HelpAssistantShadowStart;
|
|
break;
|
|
|
|
case WinStation_Notify_HelpAssistantShadowFinish:
|
|
|
|
DoNotifyData.NotifyEvent = Notify_HelpAssistantShadowFinish;
|
|
break;
|
|
|
|
case WinStation_Notify_DisconnectPipe:
|
|
DoNotifyData.NotifyEvent = Notify_DisconnectPipe;
|
|
break;
|
|
|
|
default:
|
|
RIPMSGF1(RIP_ERROR, "Unknown NotifyEvent 0x%x", m->NotifyEvent);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* Give the information to the WIN32 driver.
|
|
*/
|
|
|
|
Status = (NTSTATUS)NtUserCallOneParam((ULONG_PTR)&DoNotifyData,
|
|
SFI_XXXREMOTENOTIFY);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemoteNotify failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationExitWindows(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WINSTATIONEXITWINDOWSMSG* m = &pMsg->u.ExitWindows;
|
|
|
|
|
|
UserAssert(gulConnectCount <= 1);
|
|
|
|
|
|
/*
|
|
* Cancel any existing ExitWindows call so that we can force logoff the user
|
|
*/
|
|
CancelExitWindows();
|
|
|
|
/*
|
|
* Tell winlogon to logoff
|
|
*/
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_REMOTELOGOFF);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteLogoff failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationTerminate(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HANDLE hevtRitExited, hevtRitStuck, hevtShutDown;
|
|
DONOTIFYDATA DoNotifyData;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
|
|
UNREFERENCED_PARAMETER(pMsg);
|
|
|
|
gbExitInProgress = TRUE;
|
|
|
|
/*
|
|
* Get rid of hard error thread.
|
|
*/
|
|
if (gdwHardErrorThreadId != 0) {
|
|
|
|
BoostHardError(-1, BHE_FORCE);
|
|
|
|
/*
|
|
* Poll (!!?) for hard error thread completion. The thread does
|
|
* not exit.
|
|
*/
|
|
while (gdwHardErrorThreadId != 0) {
|
|
RIPMSG0(RIP_WARNING, "Waiting for hard error thread to stop...");
|
|
|
|
Sleep(3 * 1000);
|
|
}
|
|
|
|
RIPMSG0(RIP_WARNING, "Stopped hard error thread");
|
|
}
|
|
|
|
if (g_hDoMessageEvent) {
|
|
NtSetEvent(g_hDoMessageEvent, NULL);
|
|
}
|
|
|
|
/*
|
|
* Give the information that we want to stop reading to the WIN32 driver.
|
|
*/
|
|
|
|
DoNotifyData.NotifyEvent = Notify_StopReadInput;
|
|
|
|
Status = (NTSTATUS)NtUserCallOneParam((ULONG_PTR)&DoNotifyData,
|
|
SFI_XXXREMOTENOTIFY);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "W32WinStationTerminate : xxxRemoteNotify failed with Status %lx", Status);
|
|
}
|
|
|
|
|
|
if (G_IcaMouseChannel) {
|
|
CloseHandle(G_IcaMouseChannel);
|
|
G_IcaMouseChannel = NULL;
|
|
}
|
|
|
|
if (G_IcaKeyboardChannel) {
|
|
CloseHandle(G_IcaKeyboardChannel);
|
|
G_IcaKeyboardChannel = NULL;
|
|
}
|
|
|
|
// Instead send a IOCTL to termdd
|
|
if (G_IcaCommandChannel) {
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
G_IcaCommandChannel,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
IOCTL_ICA_CHANNEL_CLOSE_COMMAND_CHANNEL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
CloseHandle(G_IcaCommandChannel);
|
|
G_IcaCommandChannel = NULL;
|
|
}
|
|
|
|
if (G_IcaVideoChannel) {
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
G_IcaVideoChannel,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
IOCTL_ICA_CHANNEL_CLOSE_COMMAND_CHANNEL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
CloseHandle(G_IcaVideoChannel);
|
|
G_IcaVideoChannel = NULL;
|
|
}
|
|
|
|
if (G_IcaBeepChannel) {
|
|
CloseHandle(G_IcaBeepChannel);
|
|
G_IcaBeepChannel = NULL;
|
|
}
|
|
if (G_IcaThinwireChannel) {
|
|
CloseHandle(G_IcaThinwireChannel);
|
|
G_IcaThinwireChannel = NULL;
|
|
}
|
|
|
|
hevtShutDown = OpenEvent(EVENT_ALL_ACCESS,
|
|
FALSE,
|
|
L"EventShutDownCSRSS");
|
|
if (hevtShutDown == NULL) {
|
|
/*
|
|
* This case is for cached sessions where RIT and Destiop thread have
|
|
* not been created.
|
|
*/
|
|
RIPMSG0(RIP_WARNING, "W32WinStationTerminate terminating CSRSS ...");
|
|
|
|
if (gLUIDDeviceMapsEnabled == 0) {
|
|
|
|
Status = CleanupSessionObjectDirectories();
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
hevtRitExited = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
L"EventRitExited");
|
|
|
|
UserAssert(hevtRitExited != NULL);
|
|
|
|
hevtRitStuck = CreateEvent(NULL,
|
|
FALSE,
|
|
FALSE,
|
|
L"EventRitStuck");
|
|
|
|
UserAssert(hevtRitStuck != NULL);
|
|
|
|
/*
|
|
* RIT is created. Signal this event that starts the
|
|
* cleanup in win32k.
|
|
*/
|
|
SetEvent(hevtShutDown);
|
|
|
|
TAGMSG0(DBGTAG_TermSrv, "EventShutDownCSRSS set in CSRSS ...");
|
|
|
|
while (1) {
|
|
HANDLE arHandles[2] = {hevtRitExited, hevtRitStuck};
|
|
DWORD result;
|
|
|
|
result = WaitForMultipleObjects(2, arHandles, FALSE, INFINITE);
|
|
|
|
switch (result) {
|
|
case WAIT_OBJECT_0:
|
|
goto RITExited;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
|
|
/*
|
|
* The RIT is stuck because there are still GUI threads
|
|
* assigned to desktops. One reason for this is that winlogon
|
|
* died w/o calling ExitWindowsEx.
|
|
*/
|
|
break;
|
|
default:
|
|
FRE_RIPMSG1(RIP_ERROR,
|
|
"WFMO returned unexpected value 0x%x",
|
|
result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RITExited:
|
|
|
|
TAGMSG0(DBGTAG_TermSrv, "EventRitExited set in CSRSS ...");
|
|
|
|
CloseHandle(hevtRitExited);
|
|
CloseHandle(hevtRitStuck);
|
|
CloseHandle(hevtShutDown);
|
|
|
|
Status = CleanupSessionObjectDirectories();
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationNtSecurity(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(pMsg);
|
|
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_REMOTENTSECURITY);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteNtSecurity failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationDoMessage(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Status = RemoteDoMessage(pMsg);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteDoMessage failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
// This is different from W32WinStationDoMessage in that it loads the string and displays the message.
|
|
NTSTATUS
|
|
W32WinStationDoLoadStringNMessage(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RemoteDoLoadStringNMessage(pMsg);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteDoLoadStringNMessage failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
// This is the counter part to SMWinStationBroadcastSystemMessage
|
|
NTSTATUS
|
|
W32WinStationBroadcastSystemMessage(
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RemoteDoBroadcastSystemMessage(pMsg);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteDoBroadcastSystemMessage(): failed with status 0x%lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
// This is the counter part to SMWinStationSendWindowMessage
|
|
NTSTATUS
|
|
W32WinStationSendWindowMessage(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = RemoteDoSendWindowMessage(pMsg);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteDoSendWindowMessage failed with Status 0x%lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationThinwireStats(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONTHINWIRESTATSMSG* m = &pMsg->u.ThinwireStats;
|
|
|
|
Status = (NTSTATUS)NtUserCallOneParam((ULONG_PTR)&m->Stats,
|
|
SFI_REMOTETHINWIRESTATS);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteThinwireStats failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationShadowSetup(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONSHADOWSETUPMSG* m = &pMsg->u.ShadowSetup;
|
|
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_XXXREMOTESHADOWSETUP);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemoteShadowSetup failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationShadowStart(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONSHADOWSTARTMSG* m = &pMsg->u.ShadowStart;
|
|
|
|
Status = (NTSTATUS)NtUserCallTwoParam((ULONG_PTR)m->pThinwireData,
|
|
(ULONG_PTR)m->ThinwireDataLength,
|
|
SFI_REMOTESHADOWSTART);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteShadowStart failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationShadowStop(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONSHADOWSTOPMSG* m = &pMsg->u.ShadowStop;
|
|
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_XXXREMOTESHADOWSTOP);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemoteShadowStop failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationShadowCleanup(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
WINSTATIONSHADOWCLEANUPMSG* m = &pMsg->u.ShadowCleanup;
|
|
|
|
Status = (NTSTATUS)NtUserCallTwoParam((ULONG_PTR)m->pThinwireData,
|
|
(ULONG_PTR)m->ThinwireDataLength,
|
|
SFI_REMOTESHADOWCLEANUP);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemoteShadowCleanup failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationPassthruEnable(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(pMsg);
|
|
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_XXXREMOTEPASSTHRUENABLE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "xxxRemotePassthruEnable failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationPassthruDisable(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(pMsg);
|
|
|
|
Status = (NTSTATUS)NtUserCallNoParam(SFI_REMOTEPASSTHRUDISABLE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "RemotePassthruDisable failed with Status %lx", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CleanupSessionObjectDirectories(
|
|
VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE LinkHandle;
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
BOOLEAN RestartScan;
|
|
UCHAR DirInfoBuffer[ 4096 ];
|
|
WCHAR szSessionString [ MAX_SESSION_PATH ];
|
|
ULONG Context = 0;
|
|
ULONG ReturnedLength;
|
|
HANDLE DosDevicesDirectory;
|
|
HANDLE *HandleArray;
|
|
ULONG Size = 100;
|
|
ULONG i, Count = 0;
|
|
|
|
swprintf(szSessionString,L"%ws\\%ld\\DosDevices",SESSION_ROOT,NtCurrentPeb()->SessionId);
|
|
|
|
RtlInitUnicodeString(&UnicodeString, szSessionString);
|
|
|
|
InitializeObjectAttributes(&Attributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenDirectoryObject(&DosDevicesDirectory,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&Attributes);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSG1(RIP_WARNING, "NtOpenDirectoryObject failed with Status %lx", Status);
|
|
return Status;
|
|
}
|
|
|
|
Restart:
|
|
HandleArray = (HANDLE *)LocalAlloc(LPTR, Size * sizeof(HANDLE));
|
|
|
|
if (HandleArray == NULL) {
|
|
|
|
NtClose(DosDevicesDirectory);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
RestartScan = TRUE;
|
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer;
|
|
|
|
while (TRUE) {
|
|
Status = NtQueryDirectoryObject(DosDevicesDirectory,
|
|
(PVOID)DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
RestartScan,
|
|
&Context,
|
|
&ReturnedLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!wcscmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) {
|
|
if (Count >= Size) {
|
|
for (i = 0; i < Count; i++) {
|
|
NtClose(HandleArray[i]);
|
|
}
|
|
Size += 20;
|
|
Count = 0;
|
|
LocalFree(HandleArray);
|
|
goto Restart;
|
|
|
|
}
|
|
|
|
InitializeObjectAttributes(&Attributes,
|
|
&DirInfo->Name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DosDevicesDirectory,
|
|
NULL);
|
|
|
|
Status = NtOpenSymbolicLinkObject(&LinkHandle,
|
|
SYMBOLIC_LINK_ALL_ACCESS,
|
|
&Attributes);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = NtMakeTemporaryObject(LinkHandle);
|
|
if (NT_SUCCESS(Status)) {
|
|
HandleArray[Count] = LinkHandle;
|
|
Count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
RestartScan = FALSE;
|
|
}
|
|
|
|
for (i = 0; i < Count; i++) {
|
|
NtClose (HandleArray[i]);
|
|
}
|
|
|
|
LocalFree(HandleArray);
|
|
NtClose(DosDevicesDirectory);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
W32WinStationSetTimezone(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets Time Zone Information as global shared data
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
TIME_ZONE_INFORMATION tzi;
|
|
|
|
tzi.Bias = pMsg->u.SetTimeZone.TimeZone.Bias;
|
|
tzi.StandardBias = pMsg->u.SetTimeZone.TimeZone.StandardBias;
|
|
tzi.DaylightBias = pMsg->u.SetTimeZone.TimeZone.DaylightBias;
|
|
memcpy(&tzi.StandardName,&(pMsg->u.SetTimeZone.TimeZone.StandardName),sizeof(tzi.StandardName));
|
|
memcpy(&tzi.DaylightName,&(pMsg->u.SetTimeZone.TimeZone.DaylightName),sizeof(tzi.DaylightName));
|
|
|
|
tzi.StandardDate.wYear = pMsg->u.SetTimeZone.TimeZone.StandardDate.wYear;
|
|
tzi.StandardDate.wMonth = pMsg->u.SetTimeZone.TimeZone.StandardDate.wMonth;
|
|
tzi.StandardDate.wDayOfWeek = pMsg->u.SetTimeZone.TimeZone.StandardDate.wDayOfWeek;
|
|
tzi.StandardDate.wDay = pMsg->u.SetTimeZone.TimeZone.StandardDate.wDay;
|
|
tzi.StandardDate.wHour = pMsg->u.SetTimeZone.TimeZone.StandardDate.wHour;
|
|
tzi.StandardDate.wMinute = pMsg->u.SetTimeZone.TimeZone.StandardDate.wMinute;
|
|
tzi.StandardDate.wSecond = pMsg->u.SetTimeZone.TimeZone.StandardDate.wSecond;
|
|
tzi.StandardDate.wMilliseconds = pMsg->u.SetTimeZone.TimeZone.StandardDate.wMilliseconds;
|
|
|
|
tzi.DaylightDate.wYear = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wYear;
|
|
tzi.DaylightDate.wMonth = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wMonth;
|
|
tzi.DaylightDate.wDayOfWeek = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wDayOfWeek;
|
|
tzi.DaylightDate.wDay = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wDay;
|
|
tzi.DaylightDate.wHour = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wHour;
|
|
tzi.DaylightDate.wMinute = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wMinute;
|
|
tzi.DaylightDate.wSecond = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wSecond;
|
|
tzi.DaylightDate.wMilliseconds = pMsg->u.SetTimeZone.TimeZone.DaylightDate.wMilliseconds;
|
|
|
|
//call to kernel32
|
|
SetClientTimeZoneInformation(&tzi);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// Create \\Globals\TermSrvReady event with security descriptor, where only system can
|
|
// set/reset it's state and others can wait on it. Create this event only in session 0.
|
|
//
|
|
|
|
HANDLE CreateTermSrvReadyEvent()
|
|
{
|
|
NTSTATUS Status;
|
|
SID_IDENTIFIER_AUTHORITY SystemAuth = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
PSID pSystemSid = NULL;
|
|
PSID pWorldSid = NULL;
|
|
PSECURITY_DESCRIPTOR pSd = NULL;
|
|
PACL pEventDacl;
|
|
HANDLE hTermSrvReady = NULL;
|
|
ULONG AclLength;
|
|
|
|
// Allocate and Initialize the "System" Sid.
|
|
Status = RtlAllocateAndInitializeSid( &SystemAuth,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pSystemSid );
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// Allocate and Initialize the "World" Sid.
|
|
Status = RtlAllocateAndInitializeSid( &WorldAuth,
|
|
1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pWorldSid );
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// Allocate space for the security descriptor.
|
|
AclLength = (ULONG)sizeof(ACL) +
|
|
(2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
|
|
RtlLengthSid( pSystemSid ) +
|
|
RtlLengthSid( pWorldSid ) +
|
|
8;
|
|
|
|
pSd = (PSECURITY_DESCRIPTOR) LocalAlloc(0, SECURITY_DESCRIPTOR_MIN_LENGTH + AclLength);
|
|
if (pSd == NULL) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
pEventDacl = (PACL) ((BYTE*)(pSd) + SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
|
|
// Set up a new ACL with no ACE.
|
|
Status = RtlCreateAcl(pEventDacl, AclLength, ACL_REVISION2);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// WORLD access
|
|
Status = RtlAddAccessAllowedAce( pEventDacl,
|
|
ACL_REVISION2,
|
|
SYNCHRONIZE,
|
|
pWorldSid
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// SYSTEM access
|
|
Status = RtlAddAccessAllowedAce( pEventDacl,
|
|
ACL_REVISION2,
|
|
EVENT_MODIFY_STATE,
|
|
pSystemSid
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// Now initialize security descriptors that export this protection
|
|
Status = RtlCreateSecurityDescriptor(pSd, SECURITY_DESCRIPTOR_REVISION1);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
Status = RtlSetDaclSecurityDescriptor(pSd, TRUE, pEventDacl, FALSE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// Fill the Security Attributes
|
|
ZeroMemory(&SecurityAttributes, sizeof(SecurityAttributes));
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.lpSecurityDescriptor = pSd;
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
// Now create TermSrvReady event with this security attributes.
|
|
hTermSrvReady = CreateEventW(&SecurityAttributes, TRUE, FALSE, L"Global\\TermSrvReadyEvent");
|
|
if (hTermSrvReady == NULL) {
|
|
goto TermSrvReadyErr;
|
|
}
|
|
|
|
// Check if someone bad has already created this event for ill-purpose.
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
|
NtClose(hTermSrvReady);
|
|
hTermSrvReady = NULL;
|
|
}
|
|
|
|
TermSrvReadyErr:
|
|
|
|
if (pSystemSid) {
|
|
RtlFreeSid(pSystemSid);
|
|
}
|
|
|
|
if (pWorldSid) {
|
|
RtlFreeSid(pWorldSid);
|
|
}
|
|
|
|
if (pSd) {
|
|
LocalFree(pSd);
|
|
}
|
|
|
|
return hTermSrvReady;
|
|
}
|
|
|
|
|
|
|