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.
2215 lines
75 KiB
2215 lines
75 KiB
|
|
/*************************************************************************
|
|
*
|
|
* wstlpc.c
|
|
*
|
|
* WinStation LPC Initialization and dispatch functions for NT ICA Server
|
|
*
|
|
* Copyright Microsoft Corporation, 1998
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
/*
|
|
* Includes
|
|
*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <rpc.h>
|
|
#include "icaevent.h"
|
|
|
|
/*
|
|
* August 19, 1996 JohnR:
|
|
*
|
|
* The ICASRV and WinStation API's have been now reorganized.
|
|
*
|
|
* The main visible API's that client applications such as winadmin,
|
|
* winquery, and system components such as the spooler see are now
|
|
* based on RPC.
|
|
*
|
|
* Internally where ICASRV communicates with WinStations, the reverse
|
|
* LPC is used. This is because the client of these API's is
|
|
* the WIN32K.SYS kernel mode module. Non-system mode callers of
|
|
* the LPC API's are no longer allowed, and must use RPC.
|
|
*/
|
|
|
|
|
|
typedef NTSTATUS (*PWINSTATION_API) (
|
|
IN PLPC_CLIENT_CONTEXT pContext,
|
|
IN OUT PWINSTATION_APIMSG ApiMsg
|
|
);
|
|
|
|
|
|
/*
|
|
* entry for the list that keeps track of currently active LPC contexts
|
|
*/
|
|
typedef struct _TERMSRVLPCCONTEXT {
|
|
LIST_ENTRY Links;
|
|
PVOID pContext;
|
|
} TERMSRVLPCCONTEXT, *PTERMSRVLPCCONTEXT;
|
|
|
|
LIST_ENTRY gTermsrvLpcListHead;
|
|
|
|
|
|
/*
|
|
* External Functions
|
|
*/
|
|
NTSTATUS SendWinStationCommand( PWINSTATION, PWINSTATION_APIMSG, ULONG );
|
|
BOOL
|
|
IsKernelDebuggerAttached();
|
|
|
|
/*
|
|
* Internal Functions
|
|
*/
|
|
VOID RemoveLpcContext(PVOID pContext);
|
|
BOOL GetSessionIdFromLpcContext(PLPC_CLIENT_CONTEXT pContext, PULONG pSessionId);
|
|
NTSTATUS WinStationLpcThread( IN PVOID ThreadParameter );
|
|
NTSTATUS WinStationLpcHandleConnectionRequest( PPORT_MESSAGE );
|
|
VOID WinStationLpcClientHasTerminated( PLPC_CLIENT_CONTEXT );
|
|
|
|
NTSTATUS WinStationInternalCreate( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationInternalReset( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationInternalDisconnect( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationWCharLog( PLPC_CLIENT_CONTEXT pContext, PWINSTATION_APIMSG pMsg );
|
|
NTSTATUS WinStationGetSMCommand( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationBrokenConnection( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationIcaReplyMessage( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationIcaShadowHotkey( PLPC_CLIENT_CONTEXT, PWINSTATION_APIMSG );
|
|
NTSTATUS WinStationWindowInvalid( PLPC_CLIENT_CONTEXT pContext,PWINSTATION_APIMSG pMsg );
|
|
|
|
/*
|
|
* External functions we call out to do the actual WinStation control
|
|
*/
|
|
NTSTATUS WinStationDisconnectWorker( ULONG, BOOLEAN, BOOLEAN );
|
|
NTSTATUS WinStationDoDisconnect( PWINSTATION, PRECONNECT_INFO, BOOLEAN );
|
|
NTSTATUS WinStationExceptionFilter( PWSTR, PEXCEPTION_POINTERS );
|
|
NTSTATUS QueueWinStationCreate( PWINSTATIONNAME );
|
|
PSECURITY_DESCRIPTOR BuildSystemOnlySecurityDescriptor();
|
|
|
|
/*
|
|
* Local variables
|
|
*/
|
|
ULONG MinApiThreads;
|
|
ULONG MaxApiThreads;
|
|
ULONG NumApiThreads;
|
|
ULONG WaitingApiThreads;
|
|
RTL_CRITICAL_SECTION ApiThreadLock;
|
|
HANDLE SsWinStationLpcPort;
|
|
BOOLEAN ShutdownInProgress;
|
|
ULONG MessageId = 1;
|
|
|
|
/*
|
|
* ICASRV WinStation LPC Dispatch Table
|
|
*
|
|
* If this table is changed, the table below must be modified too.
|
|
*/
|
|
PWINSTATION_API WinStationLpcDispatch[SMWinStationMaxApiNumber] = {
|
|
|
|
WinStationInternalCreate, // for ICASRV internal use only
|
|
WinStationInternalReset, // for ICASRV internal use only
|
|
WinStationInternalDisconnect, // for ICASRV internal use only
|
|
WinStationWCharLog, // for ICASRV internal use only
|
|
WinStationGetSMCommand,
|
|
WinStationBrokenConnection,
|
|
WinStationIcaReplyMessage,
|
|
WinStationIcaShadowHotkey,
|
|
NULL, // WinStationDoConnect, // needed for connect and reconnect (I.E. InitMouse)
|
|
NULL, // WinStationDoDisconnect, // needed for disconnect (I.E. disable screen)
|
|
NULL, // WinStationDoReconnect // Reconnect
|
|
NULL, // WinStationExitWindows, // Logoff
|
|
NULL, // WinStationTerminate, // Terminate process (less gentle than logoff?)
|
|
NULL, // WinStationNtSecurity, // CTL-ALT-DEL screen
|
|
NULL, // WinStationDoMessage, // Message box
|
|
NULL, // WinStationDoBreakPoint // WinStation breakpoint
|
|
NULL, // WinStationThinwireStats // Get thinwire stats
|
|
NULL, // WinStationShadowSetup,
|
|
NULL, // WinStationShadowStart,
|
|
NULL, // WinStationShadowStop,
|
|
NULL, // WinStationShadowCleanup,
|
|
NULL, // WinStationPassthruEnable,
|
|
NULL, // WinStationPassthruDisable,
|
|
NULL, // WinStationSetTimeZone, // Set Time Zone
|
|
NULL, // WinStationInitialProgram,
|
|
NULL, // WinStationNtsdDebug,
|
|
NULL, // WinStationBroadcastSystemMessage // For PNP: This is the counter part to BroadcastSystemMessage on console
|
|
NULL, // WinStationSendWindowMessage // General Window's SendMessage() API
|
|
NULL, // SMWinStationNotify
|
|
NULL, // SMWinStationDoLoadStringNMessage
|
|
WinStationWindowInvalid
|
|
};
|
|
|
|
#if DBG
|
|
PSZ WinStationLpcName[SMWinStationMaxApiNumber] = {
|
|
"WinStationInternalCreate",
|
|
"WinStationInternalReset",
|
|
"WinStationInternalDisconnect",
|
|
"WinStationWCharLog",
|
|
"WinStationGetSMCommand",
|
|
"WinStationBrokenConnection",
|
|
"WinStationIcaReplyMessage",
|
|
"WinStationShadowHotkey",
|
|
"WinStationDoConnect",
|
|
"WinStationDoDisconnect",
|
|
"WinStationDoReconnect",
|
|
"WinStationExitWindows",
|
|
"WinStationTerminate",
|
|
"WinStationNtSecurity",
|
|
"WinStationDoMessage",
|
|
"WinStationDoBreakPoint",
|
|
"WinStationThinwireStats",
|
|
"WinStationShadowSetup",
|
|
"WinStationShadowStart",
|
|
"WinStationShadowStop",
|
|
"WinStationShadowCleanup",
|
|
"WinStationPassthruEnable",
|
|
"WinStationPassthruDisable",
|
|
"WinStationSetTimeZone",
|
|
"WinStationInitialProgram",
|
|
"WinStationNtsdDebug",
|
|
"WinStationBroadcastSystemMessage",
|
|
"WinStationSendWindowMessage",
|
|
"SMWinStationNotify",
|
|
"SMWinStationDoLoadStringNMessage",
|
|
"WinStationWindowInvalid"
|
|
};
|
|
|
|
PSZ WinStationStateName[] = {
|
|
"Active",
|
|
"Connected",
|
|
"ConnectQuery",
|
|
"VirtualIO",
|
|
"Disconnected",
|
|
"Idle",
|
|
"Off",
|
|
"Reset",
|
|
"Down",
|
|
"Init",
|
|
};
|
|
#endif // DBG
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationInitLPC
|
|
*
|
|
* Create the Session manager WinStation API LPC port and Thread
|
|
*
|
|
* ENTRY:
|
|
* No Parameters
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationInitLPC()
|
|
{
|
|
ULONG i;
|
|
NTSTATUS st;
|
|
ANSI_STRING Name;
|
|
UNICODE_STRING UnicodeName;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
ULONG Length;
|
|
SYSTEM_BASIC_INFORMATION SysInfo;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WinSta: Init WinStation LPC Channels\n"));
|
|
|
|
/*
|
|
* Initialize PC context LIst
|
|
*/
|
|
InitializeListHead(&gTermsrvLpcListHead);
|
|
|
|
/*
|
|
* create a security descriptor that allows only SYSTEM access
|
|
*/
|
|
SecurityDescriptor = BuildSystemOnlySecurityDescriptor();
|
|
|
|
if (!SecurityDescriptor)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/*
|
|
* Create the port for the WIN32 CSRSS's to connect to.
|
|
*/
|
|
RtlInitAnsiString( &Name, "\\SmSsWinStationApiPort" );
|
|
st = RtlAnsiStringToUnicodeString( &UnicodeName, &Name, TRUE);
|
|
if (!NT_SUCCESS(st))
|
|
{
|
|
MemFree( SecurityDescriptor );
|
|
return st;
|
|
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjA, &UnicodeName, 0, NULL,
|
|
SecurityDescriptor );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: Creating SsApiPort\n"));
|
|
|
|
ASSERT( sizeof(WINSTATION_APIMSG) <= PORT_MAXIMUM_MESSAGE_LENGTH );
|
|
|
|
st = NtCreatePort( &SsWinStationLpcPort,
|
|
&ObjA,
|
|
sizeof(WINSTATIONAPI_CONNECT_INFO),
|
|
sizeof(WINSTATION_APIMSG),
|
|
sizeof(WINSTATION_APIMSG) * 32 );
|
|
|
|
RtlFreeUnicodeString(&UnicodeName);
|
|
|
|
/*
|
|
* Clean up security stuff
|
|
*/
|
|
MemFree( SecurityDescriptor );
|
|
|
|
if (!NT_SUCCESS(st))
|
|
{
|
|
return st;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Determine min/max number of API threads we will support
|
|
*/
|
|
if (g_bPersonalTS) {
|
|
MinApiThreads = 1;
|
|
MaxApiThreads = 100;
|
|
}
|
|
else {
|
|
MinApiThreads = 3;
|
|
st = NtQuerySystemInformation( SystemBasicInformation,
|
|
&SysInfo, sizeof(SysInfo), &Length );
|
|
if ( NT_SUCCESS( st ) )
|
|
MaxApiThreads = 100; // (3 + SysInfo.NumberOfProcessors * 2);
|
|
else {
|
|
DBGPRINT(( "TERMSRV: NtQuerySystemInfo failed, rc=0x%x\n", st ));
|
|
MaxApiThreads = 100;
|
|
}
|
|
}
|
|
NumApiThreads = 0;
|
|
WaitingApiThreads = 0;
|
|
st = RtlInitializeCriticalSection( &ApiThreadLock );
|
|
if(!(NT_SUCCESS(st))) {
|
|
return(st);
|
|
}
|
|
|
|
/*
|
|
* Create Initial Set of Server Threads
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: Creating WinStation LPC Server Threads\n"));
|
|
|
|
for ( i = 0; i < MinApiThreads; i++ ) {
|
|
DWORD ThreadId;
|
|
HANDLE Handle;
|
|
|
|
Handle = CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)WinStationLpcThread,
|
|
NULL,
|
|
THREAD_SET_INFORMATION,
|
|
&ThreadId );
|
|
if ( !Handle ) {
|
|
return( STATUS_TOO_MANY_THREADS );
|
|
} else {
|
|
NtClose( Handle );
|
|
}
|
|
}
|
|
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
NumApiThreads += MinApiThreads;
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: Done Creating Service API Service Threads\n" ));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationLpcThread
|
|
*
|
|
* Main service thread for internal Winstation LPC connections.
|
|
*
|
|
* ENTRY:
|
|
* ThreadParameter (input)
|
|
* Not used standard NT ThreadCreate() parameter
|
|
*
|
|
* EXIT:
|
|
* Should never exit
|
|
*
|
|
****************************************************************************/
|
|
|
|
|
|
NTSTATUS
|
|
WinStationLpcThread( IN PVOID ThreadParameter )
|
|
{
|
|
WINSTATION_APIMSG ApiMsg;
|
|
PWINSTATION_APIMSG ReplyMsg;
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
|
|
ReplyMsg = NULL;
|
|
|
|
/*
|
|
* Loop forever processing API requests
|
|
*/
|
|
for ( ; ; ) {
|
|
|
|
/*
|
|
* If there are more than the minimum number of API threads active,
|
|
* and at least 1 waiting thread, then this thread will terminate.
|
|
* But first, any pending reply message must be sent.
|
|
*/
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
#ifdef notdef
|
|
if ( NumApiThreads > MinApiThreads && WaitingApiThreads ) {
|
|
NumApiThreads--;
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
if ( ReplyMsg ) {
|
|
(VOID) NtReplyPort( SsWinStationLpcPort,
|
|
(PPORT_MESSAGE) ReplyMsg );
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Increment the number of waiting threads and wait for an LPC request
|
|
*/
|
|
WaitingApiThreads++;
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
Status = NtReplyWaitReceivePort( SsWinStationLpcPort,
|
|
(PVOID *) &pContext,
|
|
(PPORT_MESSAGE) ReplyMsg,
|
|
(PPORT_MESSAGE) &ApiMsg );
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got a message\n" ));
|
|
/*
|
|
* If there are no more waiting threads,
|
|
* then create a new API thread to process requests.
|
|
*/
|
|
if ( --WaitingApiThreads == 0 && NumApiThreads < MaxApiThreads ) {
|
|
DWORD ThreadId;
|
|
|
|
NumApiThreads++;
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
Handle = CreateThread( NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)WinStationLpcThread,
|
|
NULL,
|
|
THREAD_SET_INFORMATION,
|
|
&ThreadId );
|
|
|
|
if ( !Handle ) {
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
NumApiThreads--;
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
} else {
|
|
NtClose( Handle );
|
|
}
|
|
|
|
} else {
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
}
|
|
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
ReplyMsg = NULL;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
|
|
/*
|
|
* Process connection request from a new client
|
|
*/
|
|
if ( ApiMsg.h.u2.s2.Type == LPC_CONNECTION_REQUEST ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got connection message\n" ));
|
|
// CONNECT_INFO is in ApiMsg from NtReplyWaitReceivePort() when
|
|
// a connection request is received. This differs from
|
|
// NtListenPort() which passes separate pointers for CONNECT_INFO.
|
|
|
|
WinStationLpcHandleConnectionRequest( (PPORT_MESSAGE)&ApiMsg );
|
|
ReplyMsg = NULL;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Process port closed message
|
|
*/
|
|
if ( ApiMsg.h.u2.s2.Type == LPC_PORT_CLOSED ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got PORT_CLOSED message pContext %p\n",
|
|
pContext));
|
|
// NOTE: This function frees the CONTEXT struct
|
|
WinStationLpcClientHasTerminated( pContext );
|
|
ReplyMsg = NULL;
|
|
continue;
|
|
}
|
|
|
|
ASSERT(sizeof(WinStationLpcDispatch)/sizeof(WinStationLpcDispatch[0]) == SMWinStationMaxApiNumber);
|
|
ASSERT(sizeof(WinStationLpcName)/sizeof(WinStationLpcName[0]) == SMWinStationMaxApiNumber);
|
|
|
|
/*
|
|
* Process API request from client
|
|
*/
|
|
ReplyMsg = &ApiMsg;
|
|
if ((ULONG) ApiMsg.ApiNumber >= (ULONG)SMWinStationMaxApiNumber ) {
|
|
DBGPRINT(( "TERMSRV: WinStation LPC Service Thread Bad API number %d\n",
|
|
ApiMsg.ApiNumber ));
|
|
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
|
|
|
|
} else {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStation LPC Service Thread got %s message\n",
|
|
WinStationLpcName[ApiMsg.ApiNumber] ));
|
|
if ( WinStationLpcDispatch[ApiMsg.ApiNumber] ) {
|
|
|
|
// Save Msg for use by CheckClientAccess
|
|
NtCurrentTeb()->Win32ThreadInfo = &ApiMsg;
|
|
|
|
// The functions set ApiMsg.ReturnedStatus
|
|
Status = (WinStationLpcDispatch[ApiMsg.ApiNumber])( pContext, &ApiMsg );
|
|
|
|
// Clear thread Msg pointer
|
|
NtCurrentTeb()->Win32ThreadInfo = NULL;
|
|
|
|
} else {
|
|
// This API is not implemented in Session Manager
|
|
ApiMsg.ReturnedStatus = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* If client does not expect a reply or reply is pending
|
|
* (will be sent asynchronously), then clear ReplyMsg pointer.
|
|
*/
|
|
if ( !ApiMsg.WaitForReply || Status == STATUS_PENDING )
|
|
ReplyMsg = NULL;
|
|
}
|
|
} except( WinStationExceptionFilter( L"WinStationLpcThread trapped!!",
|
|
GetExceptionInformation() ) ) {
|
|
ReplyMsg = NULL;
|
|
}
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationLpcHandleConnectionRequest
|
|
*
|
|
* Handle connection requests and create our local data structures
|
|
*
|
|
* ENTRY:
|
|
* ConnectionRequest (input)
|
|
* NT LPC PORT_MESSAGE describing the request
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationLpcHandleConnectionRequest(
|
|
IN PPORT_MESSAGE ConnectionRequest
|
|
)
|
|
{
|
|
NTSTATUS st;
|
|
HANDLE CommunicationPort = NULL;
|
|
BOOLEAN Accept;
|
|
PWINSTATIONAPI_CONNECT_INFO info;
|
|
REMOTE_PORT_VIEW ClientView;
|
|
REMOTE_PORT_VIEW *pClientView = NULL;
|
|
PORT_VIEW ServerView;
|
|
PORT_VIEW * pServerView = NULL;
|
|
LARGE_INTEGER SectionSize;
|
|
HANDLE PortSection = NULL ;
|
|
PWINSTATION pWinStation;
|
|
PLPC_CLIENT_CONTEXT pContext = NULL;
|
|
ULONG ClientLogonId;
|
|
PTERMSRVLPCCONTEXT pLpcContextEntry = NULL;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationLpcHandleConnectionRequest called\n" ));
|
|
|
|
Accept = TRUE; // Assume we will accept
|
|
|
|
// An undocumented NT LPC feature is that the CONNECT_INFO structure
|
|
// follows the PORT_MESSAGE header when the connection request is
|
|
// received through NtReplyWaitReceivePort(), which is useful since we
|
|
// only have to maintain (1) thread for WinStation LPC API's, and
|
|
// do not have to dedicated one to NtListenPort() just for connection
|
|
// requests.
|
|
|
|
if ( ConnectionRequest->u1.s1.DataLength != sizeof(WINSTATIONAPI_CONNECT_INFO) ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: WSTAPI: Bad CONNECTINFO length %d\n",
|
|
ConnectionRequest->u1.s1.DataLength ));
|
|
Accept = FALSE;
|
|
} else {
|
|
|
|
info = (PWINSTATIONAPI_CONNECT_INFO)
|
|
((ULONG_PTR)ConnectionRequest + sizeof(PORT_MESSAGE));
|
|
|
|
//
|
|
// We can set Accept to FALSE at anytime here for certain types
|
|
// of requests and/or caller identities.
|
|
//
|
|
if ( ConnectionRequest->ClientViewSize == 0 ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WSTAPI: Creating View memory\n" ));
|
|
|
|
pServerView = &ServerView;
|
|
|
|
// Setup Port memory for larger data transfers
|
|
|
|
SectionSize.LowPart = WINSTATIONAPI_PORT_MEMORY_SIZE;
|
|
SectionSize.HighPart = 0;
|
|
|
|
st = NtCreateSection(&PortSection, SECTION_ALL_ACCESS, NULL,
|
|
&SectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: Error Creating Section 0x%x\n", st));
|
|
Accept = FALSE;
|
|
info->AcceptStatus = st;
|
|
} else {
|
|
ServerView.Length = sizeof(ServerView);
|
|
ServerView.SectionHandle = PortSection;
|
|
ServerView.SectionOffset = 0;
|
|
ServerView.ViewSize = SectionSize.LowPart;
|
|
ServerView.ViewBase = 0;
|
|
ServerView.ViewRemoteBase = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ( Accept ) {
|
|
// Init the REMOTE_VIEW structure
|
|
ClientView.Length = sizeof(ClientView);
|
|
ClientView.ViewSize = 0;
|
|
ClientView.ViewBase = 0;
|
|
pClientView = &ClientView;
|
|
|
|
info->AcceptStatus = STATUS_SUCCESS;
|
|
|
|
if ( info->Version != CITRIX_WINSTATIONAPI_VERSION ) {
|
|
info->AcceptStatus = 1; // Fill in bad version param code
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR,"TERMSRV: WSTAPI: Bad Version %d\n", info->Version));
|
|
Accept = FALSE;
|
|
}
|
|
|
|
// Add checks for info.RequestedAccess against the requesting
|
|
// threads security rights for WinStation access. Use the Se* stuff
|
|
// to do the checking and audit generation
|
|
|
|
// On Security Access failure:
|
|
// Accept = FALSE;
|
|
// info->AcceptStatus = NT invalid rights message
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Get the ClientLogonId
|
|
//
|
|
if ( Accept ) {
|
|
HANDLE ClientProcessHandle = NULL;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
st = NtOpenProcess( &ClientProcessHandle, GENERIC_READ,
|
|
&ObjA, &ConnectionRequest->ClientId );
|
|
|
|
if (NT_SUCCESS(st)) {
|
|
GetProcessLogonId( ClientProcessHandle, &ClientLogonId );
|
|
NtClose( ClientProcessHandle );
|
|
} else {
|
|
Accept = FALSE;
|
|
info->AcceptStatus = st;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate a context connection control block.
|
|
// The address of this block is used as the
|
|
// port context in all calls from a client process
|
|
//
|
|
|
|
if ( Accept ) {
|
|
pContext = MemAlloc( sizeof(LPC_CLIENT_CONTEXT) );
|
|
if ( pContext ) {
|
|
pContext->CommunicationPort = NULL;
|
|
pContext->AccessRights = info->RequestedAccess;
|
|
} else {
|
|
Accept = FALSE;
|
|
info->AcceptStatus = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
// More undocumented NT. Many parameters are missing here and in ntlpcapi.h
|
|
// from the documentation. The CONNECTION_INFO message is part
|
|
// of the message body following PORT_MESSAGE, just like
|
|
// NtReplyWaitReceivePort().
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WSTAPI: Calling AcceptConnectPort, Accept %d\n", Accept));
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: pContext %p, ConnectionRequest %p, info %p\n",
|
|
pContext, ConnectionRequest, info));
|
|
|
|
if (!Accept) {
|
|
pClientView = NULL;
|
|
pServerView = NULL;
|
|
}
|
|
|
|
st = NtAcceptConnectPort(
|
|
&CommunicationPort,
|
|
(PVOID)pContext,
|
|
ConnectionRequest,
|
|
Accept,
|
|
pServerView,
|
|
pClientView
|
|
);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
if (PortSection != NULL) {
|
|
NtClose(PortSection);
|
|
}
|
|
if (pContext != NULL) {
|
|
MemFree( pContext );
|
|
}
|
|
return st;
|
|
}
|
|
|
|
// Close the PortSection (LPC will hold the reference now)
|
|
if ( pServerView )
|
|
NtClose(PortSection);
|
|
|
|
// Insert the context before completing the connect because as soon
|
|
// as the complete is done, the client thread can send a request and
|
|
// if this request is serviced by another LPC thread then the context
|
|
// won't be found (WinStationBrokenConnection case, by instance).
|
|
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
|
|
pLpcContextEntry = MemAlloc(sizeof(TERMSRVLPCCONTEXT));
|
|
if (pLpcContextEntry != NULL) {
|
|
pLpcContextEntry->pContext = pContext;
|
|
InsertTailList( &gTermsrvLpcListHead, &pLpcContextEntry->Links );
|
|
}
|
|
|
|
// Don't leave the critical section before you complete connection. Because if context switch occurs, there
|
|
// are chances that communication port might get distroyed and we might end up working on invalid handle.
|
|
|
|
if ( Accept ) {
|
|
|
|
pContext->ClientViewBase = ClientView.ViewBase;
|
|
pContext->ClientViewBounds = (PVOID)((ULONG_PTR)ClientView.ViewBase + ClientView.ViewSize);
|
|
if ( pServerView ) {
|
|
pContext->ViewBase = ServerView.ViewBase;
|
|
pContext->ViewSize = ServerView.ViewSize;
|
|
pContext->ViewRemoteBase = ServerView.ViewRemoteBase;
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: ViewBase %p, ViewSize 0x%x, ViewRemoteBase %p\n",
|
|
pContext->ViewBase, pContext->ViewSize, pContext->ViewRemoteBase));
|
|
} else {
|
|
pContext->ViewBase = NULL;
|
|
pContext->ViewSize = 0;
|
|
pContext->ViewRemoteBase = NULL;
|
|
}
|
|
|
|
pContext->ClientLogonId = ClientLogonId;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WSTAPI: Calling CompleteConnect port %p\n",CommunicationPort));
|
|
pContext->CommunicationPort = CommunicationPort;
|
|
st = NtCompleteConnectPort(CommunicationPort);
|
|
|
|
}
|
|
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1,"TERMSRV: WinStation LPC Connection %sAccepted, Logonid %d pContext %p Status 0x%x\n",
|
|
(Accept?"":"Not "), pContext->ClientLogonId, pContext, st));
|
|
|
|
return( st );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationLpcClientHasTerminated
|
|
*
|
|
* Cleanup after an LPC communications channel has been closed.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connnection
|
|
*
|
|
* ClientId (input)
|
|
* Pointer to the NT LPC CLIENT_ID structure that describes the
|
|
* unique process and thread.
|
|
*
|
|
* EXIT:
|
|
* VOID
|
|
*
|
|
****************************************************************************/
|
|
|
|
VOID
|
|
WinStationLpcClientHasTerminated(
|
|
IN PLPC_CLIENT_CONTEXT pContext
|
|
)
|
|
{
|
|
PWINSTATION pWinStation;
|
|
NTSTATUS Status;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationLpcClientHasTerminated called, pContext %p\n",
|
|
pContext));
|
|
|
|
//
|
|
// We can be called here with a NULL pContext if the allocation failed
|
|
// in WinStationLpcHandleConnectionRequest()
|
|
//
|
|
if (!pContext) {
|
|
return;
|
|
}
|
|
|
|
RemoveLpcContext(pContext);
|
|
|
|
// Hack for #241885
|
|
// This bug is due to client diying in the window beetween
|
|
// server doing NtAcceptConnectPort() and NtCompleteConnectPort().
|
|
// This is an LPC problem (we should not reveive LPC_PORT_CLOSED in such a window).
|
|
// or possibly to the way termsrv uses undocumented LPC features to avoid
|
|
// using a dedicated thread to do NtListenPort(). This is a temporary workaround
|
|
// to avoid stress break.
|
|
//
|
|
// Close the communication port handle
|
|
|
|
try {
|
|
if (pContext->CommunicationPort == NULL) {
|
|
return;
|
|
}
|
|
Status = NtClose( pContext->CommunicationPort );
|
|
if (!NT_SUCCESS(Status)) {
|
|
return;
|
|
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Flush the Win32 command queue.
|
|
* If the Win32 command list is not empty, then loop through each
|
|
* entry on the list and unlink it and trigger the wait event.
|
|
*/
|
|
pWinStation = FindWinStationById( pContext->ClientLogonId, FALSE );
|
|
if ( pWinStation != NULL ) {
|
|
if ( pContext == pWinStation->pWin32Context ) {
|
|
while ( !IsListEmpty( &pWinStation->Win32CommandHead ) ) {
|
|
PLIST_ENTRY Head;
|
|
PCOMMAND_ENTRY pCommand;
|
|
|
|
Head = pWinStation->Win32CommandHead.Flink;
|
|
pCommand = CONTAINING_RECORD( Head, COMMAND_ENTRY, Links );
|
|
RemoveEntryList( &pCommand->Links );
|
|
if ( !pCommand->pMsg->WaitForReply ) {
|
|
ASSERT( pCommand->Event == NULL );
|
|
MemFree( pCommand );
|
|
} else {
|
|
pCommand->Links.Flink = NULL;
|
|
pCommand->pMsg->ReturnedStatus = STATUS_CTX_CLOSE_PENDING;
|
|
NtSetEvent( pCommand->Event, NULL );
|
|
}
|
|
}
|
|
pWinStation->pWin32Context = NULL;
|
|
}
|
|
ReleaseWinStation( pWinStation );
|
|
}
|
|
|
|
// Free the context struct passed in by the LPC
|
|
MemFree( pContext );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationInternalCreate
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationInternalCreate( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
WINSTATIONCREATEMSG *m = &pMsg->u.Create;
|
|
|
|
/*
|
|
* Call the create worker
|
|
*/
|
|
if ( m->WinStationName[0] ) {
|
|
pMsg->ReturnedStatus = WinStationCreateWorker( m->WinStationName,
|
|
&m->LogonId,
|
|
TRUE );
|
|
} else {
|
|
pMsg->ReturnedStatus = WinStationCreateWorker( NULL,
|
|
&m->LogonId,
|
|
TRUE );
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationCreate, Status=0x%x\n", pMsg->ReturnedStatus ));
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationInternalReset
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationInternalReset( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
WINSTATIONRESETMSG *m = &pMsg->u.Reset;
|
|
|
|
/*
|
|
* Call the reset worker
|
|
*/
|
|
pMsg->ReturnedStatus = WinStationResetWorker( m->LogonId, FALSE, FALSE, TRUE );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationReset, Status=0x%x\n", pMsg->ReturnedStatus ));
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationInternalDisconnect
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationInternalDisconnect( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
WINSTATIONDISCONNECTMSG *m = &pMsg->u.Disconnect;
|
|
|
|
/*
|
|
* Call the disconnect worker
|
|
*/
|
|
pMsg->ReturnedStatus = WinStationDisconnectWorker( m->LogonId, FALSE, FALSE );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationDisconnect, Status=0x%x\n", pMsg->ReturnedStatus ));
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationWCharLog
|
|
*
|
|
* Message parameter unmarshalling function for WinStation API.
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationWCharLog( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
extern WCHAR gpszServiceName[];
|
|
WINSTATIONWCHARLOG *m= &pMsg->u.WCharLog;
|
|
PWCHAR ModulePath = m->Buffer;
|
|
HANDLE h;
|
|
|
|
h = RegisterEventSource(NULL, gpszServiceName);
|
|
if (h != NULL)
|
|
{
|
|
ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, EVENT_STACK_LOAD_FAILED, NULL, 1, 0, &ModulePath, NULL);
|
|
DeregisterEventSource(h);
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationGetSMCommand
|
|
*
|
|
* This is the API that the Winstations call in order to get
|
|
* work to do. We send Winstations commands from SendWinStationCommand()
|
|
* once they have called this API.
|
|
*
|
|
* NOTE: Only WinStations may call this command!
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationGetSMCommand( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
PLIST_ENTRY Head;
|
|
PWINSTATION pWinStation;
|
|
PCOMMAND_ENTRY pCommand;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand, LogonId=%d\n",
|
|
pContext->ClientLogonId ));
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
|
|
pWinStation = FindWinStationById( pContext->ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand LogonId=%d not found\n",
|
|
pContext->ClientLogonId ));
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* Ensure this is the Win32 subsystem calling
|
|
*/
|
|
if ( pWinStation->WindowsSubSysProcessId &&
|
|
pMsg->h.ClientId.UniqueProcess != pWinStation->WindowsSubSysProcessId ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand LogonId=%d wrong process id %d != %d\n",
|
|
pContext->ClientLogonId,
|
|
pMsg->h.ClientId.UniqueProcess,
|
|
pWinStation->WindowsSubSysProcessId ));
|
|
#if DBG
|
|
DbgBreakPoint();
|
|
#endif
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* If the LPC context pointer has not been saved yet, do it now
|
|
*/
|
|
if ( pWinStation->pWin32Context == NULL )
|
|
pWinStation->pWin32Context = pContext;
|
|
|
|
/*
|
|
* If this message is a reply to a previous Win32 command,
|
|
* then verify the reply is for the message on the head of the
|
|
* Win32 command queue and complete the command processing.
|
|
*/
|
|
if ( pMsg->WaitForReply ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand wait for reply\n"));
|
|
|
|
if ( !IsListEmpty( &pWinStation->Win32CommandHead ) ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand list entry\n"));
|
|
|
|
Head = pWinStation->Win32CommandHead.Flink;
|
|
pCommand = CONTAINING_RECORD( Head, COMMAND_ENTRY, Links );
|
|
if ( pCommand->pMsg->MessageId == pMsg->MessageId ) {
|
|
WINSTATION_APINUMBER ApiNumber;
|
|
|
|
/*
|
|
* Copy reply msg back to command entry
|
|
* (make sure we preserve original API number)
|
|
*/
|
|
ApiNumber = pCommand->pMsg->ApiNumber;
|
|
*pCommand->pMsg = *pMsg;
|
|
pCommand->pMsg->ApiNumber = ApiNumber;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand, LogonId=%d, Reply for Cmd %s, Status=0x%x\n",
|
|
pContext->ClientLogonId,
|
|
WinStationLpcName[pCommand->pMsg->ApiNumber],
|
|
pMsg->ReturnedStatus ));
|
|
|
|
/*
|
|
* Unlink this command entry and
|
|
* trigger event to wakeup the waiter.
|
|
*/
|
|
RemoveEntryList( &pCommand->Links );
|
|
pCommand->Links.Flink = NULL;
|
|
NtSetEvent( pCommand->Event, NULL );
|
|
}
|
|
else {
|
|
DBGPRINT(("TERMSRV: WinStationGetSMCommand, no cmd entry for MessageId 0x%x\n", pMsg->MessageId ));
|
|
}
|
|
}
|
|
else {
|
|
DBGPRINT(( "TERMSRV: WinStationGetSMCommand, cmd queue empty for MessageId 0x%x\n", pMsg->MessageId ));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the head of the Win32 command queue is non-empty,
|
|
* then send the first command in the queue to Win32.
|
|
*/
|
|
if ( !IsListEmpty( &pWinStation->Win32CommandHead ) ) {
|
|
|
|
Head = pWinStation->Win32CommandHead.Flink;
|
|
pCommand = CONTAINING_RECORD( Head, COMMAND_ENTRY, Links );
|
|
|
|
/*
|
|
* Send the msg contained in the command entry, but be sure to use
|
|
* the LPC PORT_MESSAGE fields from the original msg we received
|
|
* since we are sending the command as an LPC reply message.
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand, LogonId=%d, sending next cmd\n",
|
|
pWinStation->LogonId ));
|
|
|
|
#ifdef notdef // no longer needed - but good example of using view memory
|
|
/*
|
|
* Do connect needs to copy data to the view
|
|
*/
|
|
if ( pCommand->pMsg->ApiNumber == SMWinStationDoConnect ) {
|
|
|
|
pCommand->pMsg->u.DoConnect.VDInfoLength =
|
|
min ( pCommand->pMsg->u.DoConnect.VDInfoLength,
|
|
pContext->ViewSize );
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "SMSS: WinStationGetSMCommand, Copying VD Info data %d\n", pCommand->pMsg->u.DoConnect.VDInfoLength ));
|
|
RtlCopyMemory( pContext->ViewBase,
|
|
pCommand->pMsg->u.DoConnect.VDInfo,
|
|
pCommand->pMsg->u.DoConnect.VDInfoLength );
|
|
pCommand->pMsg->u.DoConnect.VDInfo = pContext->ViewRemoteBase;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* On DoMessage API copy to client view and free temp memory
|
|
*/
|
|
if ( pCommand->pMsg->ApiNumber == SMWinStationDoMessage ) {
|
|
|
|
PVOID pTitle;
|
|
PVOID pMessage;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: pulled SMWinStationDoMessage, copy to client view\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
|
|
pTitle = pContext->ViewBase;
|
|
pMessage = (PVOID)((ULONG_PTR)pTitle + pCommand->pMsg->u.SendMessage.TitleLength);
|
|
|
|
// Copy out the pTitle and pMessage strings to client view
|
|
|
|
RtlMoveMemory( pTitle, pCommand->pMsg->u.SendMessage.pTitle,
|
|
pCommand->pMsg->u.SendMessage.TitleLength );
|
|
RtlMoveMemory( pMessage, pCommand->pMsg->u.SendMessage.pMessage,
|
|
pCommand->pMsg->u.SendMessage.MessageLength );
|
|
|
|
MemFree( pCommand->pMsg->u.SendMessage.pTitle );
|
|
MemFree( pCommand->pMsg->u.SendMessage.pMessage );
|
|
|
|
pCommand->pMsg->u.SendMessage.pTitle =
|
|
(PVOID)(pContext->ViewRemoteBase);
|
|
pCommand->pMsg->u.SendMessage.pMessage =
|
|
(PVOID) ((ULONG_PTR)pContext->ViewRemoteBase + pCommand->pMsg->u.SendMessage.TitleLength);
|
|
|
|
} else if ( pCommand->pMsg->ApiNumber == SMWinStationDoLoadStringNMessage ) {
|
|
|
|
PVOID pDomain;
|
|
PVOID pUserName;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: pulled SMWinStationDoLoadStringNMessage, copy to client view\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
|
|
pDomain = pContext->ViewBase;
|
|
pUserName = (PVOID)((ULONG_PTR)pDomain + pCommand->pMsg->u.LoadStringMessage.DomainSize);
|
|
|
|
// Copy out the pTitle and pMessage strings to client view
|
|
|
|
RtlMoveMemory( pDomain, pCommand->pMsg->u.LoadStringMessage.pDomain,
|
|
pCommand->pMsg->u.LoadStringMessage.DomainSize );
|
|
RtlMoveMemory( pUserName, pCommand->pMsg->u.LoadStringMessage.pUserName,
|
|
pCommand->pMsg->u.LoadStringMessage.UserNameSize );
|
|
|
|
MemFree( pCommand->pMsg->u.LoadStringMessage.pDomain );
|
|
MemFree( pCommand->pMsg->u.LoadStringMessage.pUserName );
|
|
|
|
pCommand->pMsg->u.LoadStringMessage.pDomain =
|
|
(PVOID)(pContext->ViewRemoteBase);
|
|
pCommand->pMsg->u.LoadStringMessage.pUserName =
|
|
(PVOID) ((ULONG_PTR)pContext->ViewRemoteBase + pCommand->pMsg->u.LoadStringMessage.DomainSize);
|
|
|
|
} else if ( pCommand->pMsg->ApiNumber == SMWinStationShadowStart ||
|
|
pCommand->pMsg->ApiNumber == SMWinStationShadowCleanup ) {
|
|
|
|
PVOID pData;
|
|
|
|
// Get pointers to client view of memory
|
|
|
|
pData = pContext->ViewBase;
|
|
|
|
// Copy out the Thinwire data to client view
|
|
|
|
RtlMoveMemory( pData, pCommand->pMsg->u.ShadowStart.pThinwireData,
|
|
pCommand->pMsg->u.ShadowStart.ThinwireDataLength );
|
|
|
|
MemFree( pCommand->pMsg->u.ShadowStart.pThinwireData );
|
|
|
|
pCommand->pMsg->u.ShadowStart.pThinwireData =
|
|
(PVOID)(pContext->ViewRemoteBase);
|
|
|
|
} else if ( pCommand->pMsg->ApiNumber == SMWinStationSendWindowMessage) {
|
|
|
|
PVOID pView;
|
|
|
|
// Get pointers to client view of memory
|
|
pView = pContext->ViewBase;
|
|
|
|
RtlMoveMemory( pView, pCommand->pMsg->u.sMsg.dataBuffer,
|
|
pCommand->pMsg->u.sMsg.bufferSize );
|
|
|
|
MemFree( pCommand->pMsg->u.sMsg.dataBuffer );
|
|
|
|
// Update msg
|
|
pCommand->pMsg->u.sMsg.dataBuffer =
|
|
(PVOID)pContext->ViewRemoteBase;
|
|
|
|
|
|
} else if ( pCommand->pMsg->ApiNumber == SMWinStationBroadcastSystemMessage) {
|
|
|
|
PVOID pView;
|
|
|
|
// Get pointers to client view of memory
|
|
pView = pContext->ViewBase;
|
|
|
|
RtlMoveMemory( pView, pCommand->pMsg->u.bMsg.dataBuffer,
|
|
pCommand->pMsg->u.bMsg.bufferSize );
|
|
|
|
MemFree( pCommand->pMsg->u.bMsg.dataBuffer );
|
|
|
|
// Update msg
|
|
pCommand->pMsg->u.bMsg.dataBuffer =
|
|
(PVOID)pContext->ViewRemoteBase;
|
|
|
|
}
|
|
|
|
pCommand->pMsg->h = pMsg->h;
|
|
NtReplyPort( pContext->CommunicationPort,
|
|
(PPORT_MESSAGE)pCommand->pMsg );
|
|
|
|
/*
|
|
* If no reply is expected, then unlink/free this command entry.
|
|
*/
|
|
if ( !pCommand->pMsg->WaitForReply ) {
|
|
RemoveEntryList( &pCommand->Links );
|
|
ASSERT( pCommand->Event == NULL );
|
|
MemFree( pCommand );
|
|
}
|
|
|
|
/*
|
|
* The Win32 command queue is empty. Save the port handle and port
|
|
* message in the WinStation. The next time a command is to be
|
|
* sent to this WinStation, these will be used to send it.
|
|
*/
|
|
} else {
|
|
ASSERT( pWinStation->Win32CommandPort == NULL );
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationGetSMCommand queue empty port %p\n",
|
|
pContext->CommunicationPort));
|
|
pWinStation->Win32CommandPort = pContext->CommunicationPort;
|
|
pWinStation->Win32CommandPortMsg = pMsg->h;
|
|
}
|
|
|
|
/*
|
|
* Release WinStation
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
/*
|
|
* We ALWAYS return STATUS_PENDING so the msg dispatch routine
|
|
* does not send a reply message now. ALL replies to this message
|
|
* are handled above or in the SendWinStationCommand() routine.
|
|
*/
|
|
return( STATUS_PENDING );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationBrokenConnection
|
|
*
|
|
* API called from Winstation requesting a broken connection
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationBrokenConnection( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
WINSTATIONBROKENCONNECTIONMSG *m = &pMsg->u.Broken;
|
|
BROKENCLASS Reason = (BROKENCLASS) m->Reason;
|
|
PWINSTATION pWinStation;
|
|
ULONG SessionId;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationBrokenConnection, LogonId=%d, Reason=%u\n",
|
|
pContext->ClientLogonId, Reason ));
|
|
|
|
/*
|
|
* Indicate A reply will be returned to client
|
|
*/
|
|
pMsg->WaitForReply = TRUE;
|
|
|
|
|
|
/*
|
|
* Make sure the context is still active and get session Id from it.
|
|
*/
|
|
|
|
|
|
if (!GetSessionIdFromLpcContext(pContext, &SessionId)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( SessionId, FALSE );
|
|
if ( pWinStation == NULL )
|
|
return( STATUS_SUCCESS );
|
|
|
|
/*
|
|
* Ensure this is the Win32 subsystem calling
|
|
*/
|
|
if ( pWinStation->WindowsSubSysProcessId &&
|
|
pMsg->h.ClientId.UniqueProcess != pWinStation->WindowsSubSysProcessId ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* If WinStation is already disconnected, then we're done
|
|
*/
|
|
if ( !pWinStation->WinStationName[0] ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* If busy with something already, don't do this
|
|
*/
|
|
if ( pWinStation->Flags ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_CTX_WINSTATION_BUSY );
|
|
}
|
|
|
|
/*
|
|
* Save reason/source for this broken connection
|
|
*/
|
|
pWinStation->BrokenReason = Reason;
|
|
pWinStation->BrokenSource = m->Source;
|
|
|
|
if ( pWinStation->NeverConnected ) {
|
|
pWinStation->StateFlags |= WSF_ST_BROKEN_CONNECTION;
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*
|
|
* if any of the following is TRUE;
|
|
* - the session is a Salem 'help assistant' session.
|
|
* - no user is logged on (logon time is 0)
|
|
* - reset is requested
|
|
* - unexpected broken connection and current user is
|
|
* setup to reset on broken connection
|
|
* then queue a reset request
|
|
*/
|
|
if (RtlLargeIntegerEqualToZero( pWinStation->LogonTime ) ||
|
|
(Reason == Broken_Terminate) ||
|
|
((Reason == Broken_Unexpected) && pWinStation->Config.Config.User.fResetBroken) ||
|
|
TSIsSessionHelpSession(pWinStation, NULL)) {
|
|
|
|
QueueWinStationReset( pWinStation->LogonId);
|
|
|
|
/*
|
|
* Otherwise, disconnect the WinStation
|
|
*/
|
|
} else {
|
|
|
|
QueueWinStationDisconnect( pWinStation->LogonId );
|
|
}
|
|
|
|
/*
|
|
* Release WinStation
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationIcaReplyMessage
|
|
*
|
|
* API called from Winstation for user response to message box
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationIcaReplyMessage( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
PWINSTATION pWinStation;
|
|
|
|
DBGPRINT(("TERMSRV: WinStationIcaReplyMessage, LogonId=%d\n",
|
|
pContext->ClientLogonId ));
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationIcaReplyMessage, LogonId=%d\n",
|
|
pContext->ClientLogonId ));
|
|
|
|
/*
|
|
* Indicate A reply will be returned to client
|
|
*/
|
|
pMsg->WaitForReply = TRUE;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( pContext->ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL )
|
|
return( STATUS_SUCCESS );
|
|
|
|
/*
|
|
* Ensure this is the Win32 subsystem calling
|
|
*/
|
|
if ( pWinStation->WindowsSubSysProcessId &&
|
|
pMsg->h.ClientId.UniqueProcess != pWinStation->WindowsSubSysProcessId ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* Fill in response
|
|
*/
|
|
*pMsg->u.ReplyMessage.pResponse = pMsg->u.ReplyMessage.Response;
|
|
|
|
|
|
/*
|
|
* Fill in the status of message delievery
|
|
*/
|
|
ASSERT(pMsg->u.ReplyMessage.pStatus);
|
|
|
|
*pMsg->u.ReplyMessage.pStatus = pMsg->u.ReplyMessage.Status;
|
|
|
|
|
|
/*
|
|
* Release RPC thread
|
|
*/
|
|
NtSetEvent( pMsg->u.ReplyMessage.hEvent, NULL );
|
|
|
|
/*
|
|
* Release WinStation
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* WinStationIcaShadowHotkey
|
|
*
|
|
* API called from Winstation that has received a shadow hotkey
|
|
*
|
|
* ENTRY:
|
|
* pContext (input)
|
|
* Pointer to our context structure describing the connection.
|
|
*
|
|
* pMsg (input/output)
|
|
* Pointer to the API message, a superset of NT LPC PORT_MESSAGE.
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - no error
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
WinStationIcaShadowHotkey( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
PWINSTATION pWinStation;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: WinStationIcaShadowHotkey, LogonId=%d\n",
|
|
pContext->ClientLogonId ));
|
|
|
|
/*
|
|
* Indicate A reply will be returned to client
|
|
*/
|
|
pMsg->WaitForReply = TRUE;
|
|
|
|
/*
|
|
* Find and lock client WinStation
|
|
*/
|
|
pWinStation = FindWinStationById( pContext->ClientLogonId, FALSE );
|
|
if ( pWinStation == NULL )
|
|
return( STATUS_SUCCESS );
|
|
|
|
/*
|
|
* Ensure this is the Win32 subsystem calling
|
|
*/
|
|
if ( pWinStation->WindowsSubSysProcessId &&
|
|
pMsg->h.ClientId.UniqueProcess != pWinStation->WindowsSubSysProcessId ) {
|
|
ReleaseWinStation( pWinStation );
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
/*
|
|
* Process the shadow hotkey.
|
|
*
|
|
* If the shadow client is still waiting for the target
|
|
* to connect, then terminate the passthru stack now to break
|
|
* out of the connection wait. Also, set the shadow
|
|
* broken event if it is non-NULL.
|
|
*/
|
|
if ( pWinStation->hPassthruStack &&
|
|
pWinStation->ShadowConnectionWait ) {
|
|
IcaStackClose( pWinStation->hPassthruStack );
|
|
pWinStation->hPassthruStack = NULL;
|
|
}
|
|
if ( pWinStation->ShadowBrokenEvent ) {
|
|
NtSetEvent( pWinStation->ShadowBrokenEvent, NULL );
|
|
}
|
|
|
|
/*
|
|
* Release WinStation
|
|
*/
|
|
ReleaseWinStation( pWinStation );
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SendWinStationCommand
|
|
*
|
|
* Send a command to a WinStation and optionally wait for a reply.
|
|
*
|
|
* NOTE: This works using a reverse LPC in which the WINSTATION must
|
|
* have sent a "request" to us for work to do. This prevents
|
|
* blocking the ICASRV while waiting on a WINSTATION that
|
|
* could be hung.
|
|
*
|
|
* ENTRY:
|
|
* pWinStation (input)
|
|
* Pointer to WinStation to send command to
|
|
* pMsg (input/output)
|
|
* Pointer to message to send
|
|
* WaitTime (input)
|
|
* Time in seconds to wait for a reply message
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - if successful
|
|
*
|
|
******************************************************************************/
|
|
|
|
NTSTATUS
|
|
SendWinStationCommand( PWINSTATION pWinStation,
|
|
PWINSTATION_APIMSG pMsg,
|
|
ULONG WaitTime )
|
|
{
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
COMMAND_ENTRY Command;
|
|
PCOMMAND_ENTRY pCommand;
|
|
NTSTATUS Status;
|
|
BOOLEAN bFreeCommand = FALSE;
|
|
BOOLEAN bTitlenMessageAllocated = FALSE;
|
|
BOOLEAN bDomainUserNameAllocated = FALSE;
|
|
|
|
//
|
|
// These are only used by the SendWindowMessage and the
|
|
// BroadcastSystemMessage APIs.
|
|
//
|
|
PVOID pdataBuffer;
|
|
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand, LogonId=%d, Cmd=%s, Timeout=%d\n",
|
|
pWinStation->LogonId,
|
|
WinStationLpcName[pMsg->ApiNumber],
|
|
WaitTime ));
|
|
|
|
ASSERT( IsWinStationLockedByCaller( pWinStation ) );
|
|
|
|
// Do not send message to listener.
|
|
if (pWinStation->Flags & WSF_LISTEN)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
/*
|
|
* Initialize the message id for this message
|
|
*/
|
|
pMsg->MessageId = InterlockedIncrement(&MessageId);
|
|
pMsg->ReturnedStatus = 0;
|
|
pMsg->WaitForReply = (WaitTime != 0) ? TRUE : FALSE;
|
|
|
|
/*
|
|
* If we will wait for a reply, then create an event to wait on.
|
|
* Since we will wait for a response, its OK to use the static
|
|
* COMMAND entry above.
|
|
*/
|
|
if ( pMsg->WaitForReply ) {
|
|
pCommand = &Command;
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
Status = NtCreateEvent( &pCommand->Event, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE );
|
|
if ( !NT_SUCCESS(Status) )
|
|
return( Status );
|
|
pCommand->pMsg = pMsg;
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand pCommand %p pCommand->pMsg %p\n", pCommand, pCommand->pMsg ));
|
|
|
|
|
|
/*
|
|
* We will not wait for a reply, but the WinStation is currently busy
|
|
* processing a command. Allocate a dynamic COMMAND entry which will
|
|
* be linked into the the command list and sent when it reaches the
|
|
* head of the list.
|
|
*/
|
|
} else if ( pWinStation->Win32CommandPort == NULL ) {
|
|
pCommand = MemAlloc( sizeof(*pCommand) + sizeof(*pMsg) );
|
|
|
|
/* makarp; check for MemAlloc failures. #182622 */
|
|
if (!pCommand) {
|
|
return (STATUS_NO_MEMORY);
|
|
}
|
|
pCommand->Event = NULL;
|
|
pCommand->pMsg = (PWINSTATION_APIMSG)(pCommand + 1);
|
|
*pCommand->pMsg = *pMsg;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/*
|
|
* We will not wait for a reply and the WinStation is NOT busy
|
|
* with a command, so there is no need for a COMMAND entry.
|
|
* The current message will be sent below.
|
|
*/
|
|
} else {
|
|
pCommand = NULL;
|
|
}
|
|
|
|
/*
|
|
* On DoMessage API either copy message to client view or strdup strings.
|
|
*/
|
|
if ( pMsg->ApiNumber == SMWinStationDoMessage ) {
|
|
|
|
PVOID pTitle;
|
|
PVOID pMessage;
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
|
|
//
|
|
// assert some parameters
|
|
//
|
|
ASSERT(pMsg->u.SendMessage.DoNotWait == (pMsg->u.SendMessage.pStatus == NULL));
|
|
ASSERT(pMsg->u.SendMessage.DoNotWait == (pMsg->u.SendMessage.hEvent == NULL));
|
|
|
|
|
|
// get winstation context
|
|
if ( (pContext = pWinStation->pWin32Context) == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR WinStationContext not valid\n" ));
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// validate size of parameters
|
|
if ((pMsg->u.SendMessage.TitleLength + pMsg->u.SendMessage.MessageLength) > pContext->ViewSize ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR Message or Title too long\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// busy? then strdup string else copy to client view
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC IDLE, process now\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
pTitle = pContext->ViewBase;
|
|
pMessage = (PVOID)((ULONG_PTR)pTitle + pMsg->u.SendMessage.TitleLength);
|
|
|
|
// Copy out the pTitle and pMessage strings to client view
|
|
RtlMoveMemory( pTitle, pMsg->u.SendMessage.pTitle, pMsg->u.SendMessage.TitleLength );
|
|
RtlMoveMemory( pMessage, pMsg->u.SendMessage.pMessage, pMsg->u.SendMessage.MessageLength );
|
|
|
|
// Update msg
|
|
pMsg->u.SendMessage.pTitle =
|
|
(PVOID)(pContext->ViewRemoteBase);
|
|
pMsg->u.SendMessage.pMessage =
|
|
(PVOID) ((ULONG_PTR)pContext->ViewRemoteBase + pMsg->u.SendMessage.TitleLength);
|
|
}
|
|
else if ( pCommand ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC BUSY, queue for later processing\n" ));
|
|
|
|
// Get pointers to temporary memory
|
|
pTitle = MemAlloc( pMsg->u.SendMessage.TitleLength );
|
|
if (pTitle == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
pMessage = MemAlloc( pMsg->u.SendMessage.MessageLength );
|
|
if (pMessage == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
MemFree( pTitle );
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
bTitlenMessageAllocated = TRUE;
|
|
|
|
// Copy out the pTitle and pMessage strings to temp memory
|
|
RtlMoveMemory( pTitle, pMsg->u.SendMessage.pTitle, pMsg->u.SendMessage.TitleLength );
|
|
RtlMoveMemory( pMessage, pMsg->u.SendMessage.pMessage, pMsg->u.SendMessage.MessageLength );
|
|
|
|
// Update msg
|
|
pCommand->pMsg->u.SendMessage.pTitle = pTitle;
|
|
pCommand->pMsg->u.SendMessage.pMessage = pMessage;
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SMWinStationDoMessage pTitle %S\n", pTitle ));
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SMWinStationDoMessage pMessage %S\n", pMessage ));
|
|
|
|
} else if ( pMsg->ApiNumber == SMWinStationDoLoadStringNMessage) {
|
|
|
|
PVOID pDomain;
|
|
PVOID pUserName;
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
|
|
//
|
|
// assert some parameters
|
|
//
|
|
ASSERT(pMsg->u.LoadStringMessage.DoNotWait == (pMsg->u.LoadStringMessage.pStatus == NULL));
|
|
ASSERT(pMsg->u.LoadStringMessage.DoNotWait == (pMsg->u.LoadStringMessage.hEvent == NULL));
|
|
|
|
|
|
// get winstation context
|
|
if ( (pContext = pWinStation->pWin32Context) == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR WinStationContext not valid\n" ));
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// validate size of parameters
|
|
if ((pMsg->u.LoadStringMessage.DomainSize + pMsg->u.LoadStringMessage.UserNameSize) > pContext->ViewSize ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR Message or Title too long\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// busy? then strdup string else copy to client view
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC IDLE, process now\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
pDomain = pContext->ViewBase;
|
|
pUserName = (PVOID)((ULONG_PTR)pDomain + pMsg->u.LoadStringMessage.DomainSize);
|
|
|
|
// Copy out the pTitle and pMessage strings to client view
|
|
RtlMoveMemory( pDomain, pMsg->u.LoadStringMessage.pDomain, pMsg->u.LoadStringMessage.DomainSize );
|
|
RtlMoveMemory( pUserName, pMsg->u.LoadStringMessage.pUserName, pMsg->u.LoadStringMessage.UserNameSize );
|
|
|
|
// Update msg
|
|
pMsg->u.LoadStringMessage.pDomain =
|
|
(PVOID)(pContext->ViewRemoteBase);
|
|
pMsg->u.LoadStringMessage.pUserName =
|
|
(PVOID) ((ULONG_PTR)pContext->ViewRemoteBase + pMsg->u.LoadStringMessage.DomainSize);
|
|
}
|
|
else if ( pCommand ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC BUSY, queue for later processing\n" ));
|
|
|
|
// Get pointers to temporary memory
|
|
pDomain = MemAlloc( pMsg->u.LoadStringMessage.DomainSize );
|
|
if (pDomain == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
pUserName = MemAlloc( pMsg->u.LoadStringMessage.UserNameSize );
|
|
if (pUserName == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
MemFree( pDomain );
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
bDomainUserNameAllocated = TRUE;
|
|
|
|
// Copy out the pTitle and pMessage strings to temp memory
|
|
RtlMoveMemory( pDomain, pMsg->u.LoadStringMessage.pDomain, pMsg->u.LoadStringMessage.DomainSize );
|
|
RtlMoveMemory( pUserName, pMsg->u.LoadStringMessage.pUserName, pMsg->u.LoadStringMessage.UserNameSize );
|
|
|
|
// Update msg
|
|
pCommand->pMsg->u.LoadStringMessage.pDomain = pDomain;
|
|
pCommand->pMsg->u.LoadStringMessage.pUserName = pUserName;
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SMWinStationDoLoadStringNMessage pDomain %S\n", pDomain ));
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SMWinStationDoLoadStringNMessage pUserName %S\n", pUserName ));
|
|
|
|
} else if ( pMsg->ApiNumber == SMWinStationShadowStart ||
|
|
pMsg->ApiNumber == SMWinStationShadowCleanup ) {
|
|
|
|
PVOID pData;
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
|
|
// get winstation contect
|
|
if ( (pContext = pWinStation->pWin32Context) == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR WinStationContext not valid\n" ));
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
|
|
|
|
// validate size of parameters
|
|
if (( pMsg->u.ShadowStart.ThinwireDataLength) > pContext->ViewSize ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR Message or Title too long\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
|
|
|
|
// busy? then strdup string else copy to client view
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
|
|
// Get pointers to client view of memory
|
|
pData = pContext->ViewBase;
|
|
|
|
// Copy out the ThinwireData to client view
|
|
RtlCopyMemory( pData, pMsg->u.ShadowStart.pThinwireData,
|
|
pMsg->u.ShadowStart.ThinwireDataLength );
|
|
|
|
// Update msg
|
|
pMsg->u.ShadowStart.pThinwireData =
|
|
(PVOID) (pContext->ViewRemoteBase);
|
|
}
|
|
else if ( pCommand ) {
|
|
|
|
// Get pointers to temporary memory
|
|
pData = MemAlloc( pMsg->u.ShadowStart.ThinwireDataLength );
|
|
if (pData == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
bFreeCommand = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
// Copy out the ThinwireData to temp memory
|
|
RtlCopyMemory( pData, pMsg->u.ShadowStart.pThinwireData,
|
|
pMsg->u.ShadowStart.ThinwireDataLength );
|
|
|
|
// Update msg
|
|
pCommand->pMsg->u.ShadowStart.pThinwireData = pData;
|
|
}
|
|
}
|
|
else if ( pMsg->ApiNumber == SMWinStationSendWindowMessage )// This msg always has WaitForReply=TRUE
|
|
{
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
PVOID pView;
|
|
|
|
// get winstation context
|
|
if ( (pContext = pWinStation->pWin32Context) == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR WinStationContext not valid\n" ));
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
// @@@
|
|
// Do i need this? : bFreeCommand = TRUE;
|
|
// Since we are waiting for a reply, then we have not allocated memory for pCommand,
|
|
// hence, we don't need to set this flag.
|
|
goto done;
|
|
}
|
|
|
|
// validate size of parameters
|
|
if ((pMsg->u.sMsg.bufferSize ) > pContext->ViewSize ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR Message or Title too long\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
// @@@
|
|
// Do i need this? : bFreeCommand = TRUE;
|
|
// Since we are waiting for a reply, then we have not allocated memory for pCommand,
|
|
// hence, we don't need to set this flag.
|
|
goto done;
|
|
}
|
|
|
|
// if not busy? then copy to client view
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC IDLE, process now\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
pView = pContext->ViewBase;
|
|
|
|
RtlMoveMemory( pView, pMsg->u.sMsg.dataBuffer, pMsg->u.sMsg.bufferSize );
|
|
|
|
// Update msg
|
|
pMsg->u.sMsg.dataBuffer = (PVOID)pContext->ViewRemoteBase;
|
|
|
|
}
|
|
else if ( pCommand ) // this is on the stack, since this msg always has WaitForReply=TRUE
|
|
{
|
|
pdataBuffer = MemAlloc(pMsg->u.sMsg.bufferSize );
|
|
if ( pdataBuffer == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// copy into tmp memory
|
|
RtlMoveMemory(pdataBuffer, pMsg->u.sMsg.dataBuffer, pMsg->u.sMsg.bufferSize );
|
|
|
|
pCommand->pMsg->u.sMsg.dataBuffer = pdataBuffer;
|
|
}
|
|
|
|
}
|
|
else if ( pMsg->ApiNumber == SMWinStationBroadcastSystemMessage )// this msg always has WaitForReply=TRUE
|
|
{
|
|
PLPC_CLIENT_CONTEXT pContext;
|
|
PVOID pView;
|
|
|
|
// get winstation context
|
|
if ( (pContext = pWinStation->pWin32Context) == NULL ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR WinStationContext not valid\n" ));
|
|
Status = STATUS_CTX_WINSTATION_NOT_FOUND;
|
|
// @@@
|
|
// Do i need this? : bFreeCommand = TRUE;
|
|
// Since we are waiting for a reply, then we have not allocated memory for pCommand,
|
|
// hence, we don't need to set this flag.
|
|
goto done;
|
|
}
|
|
|
|
// validate size of parameters
|
|
if ((pMsg->u.bMsg.bufferSize ) > pContext->ViewSize ) {
|
|
TRACE((hTrace,TC_ICASRV,TT_ERROR, "TERMSRV: SendWinStationCommand, ERROR Message or Title too long\n" ));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
// @@@
|
|
// Do i need this? : bFreeCommand = TRUE;
|
|
// Since we are waiting for a reply, then we have not allocated memory for pCommand,
|
|
// hence, we don't need to set this flag.
|
|
goto done;
|
|
}
|
|
|
|
// if not busy? then copy to client view
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand - WinStation LPC IDLE, process now\n" ));
|
|
|
|
// Get pointers to client view of memory
|
|
pView = pContext->ViewBase;
|
|
|
|
RtlMoveMemory( pView, pMsg->u.bMsg.dataBuffer, pMsg->u.bMsg.bufferSize );
|
|
|
|
// Update msg
|
|
pMsg->u.bMsg.dataBuffer = (PVOID)pContext->ViewRemoteBase;
|
|
|
|
}
|
|
else if ( pCommand ) // this is on the stack, since this msg always has WaitForReply=TRUE
|
|
{
|
|
pdataBuffer = MemAlloc(pMsg->u.bMsg.bufferSize );
|
|
if ( pdataBuffer == NULL )
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// copy into tmp memory
|
|
RtlMoveMemory(pdataBuffer, pMsg->u.bMsg.dataBuffer, pMsg->u.bMsg.bufferSize );
|
|
|
|
pCommand->pMsg->u.bMsg.dataBuffer = pdataBuffer;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the WinStation is not currently busy processing a command,
|
|
* then send this command now.
|
|
*/
|
|
if ( pWinStation->Win32CommandPort ) {
|
|
ASSERT( IsListEmpty( &pWinStation->Win32CommandHead ) );
|
|
|
|
/*
|
|
* Send the command msg, but be sure to use the LPC PORT_MESSAGE
|
|
* fields saved from the original msg we received since we are
|
|
* sending the command as an LPC reply message.
|
|
*/
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand, LogonId=%d, sending cmd\n",
|
|
pWinStation->LogonId ));
|
|
|
|
pMsg->h = pWinStation->Win32CommandPortMsg;
|
|
Status = NtReplyPort( pWinStation->Win32CommandPort,
|
|
(PPORT_MESSAGE) pMsg );
|
|
pWinStation->Win32CommandPort = NULL;
|
|
if ( !NT_SUCCESS( Status ) )
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* If we have a command entry, add it to the command list.
|
|
*/
|
|
if ( pCommand )
|
|
InsertTailList( &pWinStation->Win32CommandHead, &pCommand->Links );
|
|
|
|
/*
|
|
* If we need to wait for a reply, then do it now.
|
|
*/
|
|
if ( pMsg->WaitForReply ) {
|
|
ULONG mSecs;
|
|
LARGE_INTEGER Timeout;
|
|
|
|
#if DBG
|
|
// if ( (WaitTime != (ULONG)(-1)) && WaitTime < 120 ) // give plenty of time on debug builds
|
|
// WaitTime = 120;
|
|
#endif
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand, LogonId=%d, waiting for response\n",
|
|
pWinStation->LogonId ));
|
|
|
|
WaitAgain :
|
|
|
|
if ( WaitTime != (ULONG)(-1) ) {
|
|
mSecs = WaitTime * 1000;
|
|
Timeout = RtlEnlargedIntegerMultiply( mSecs, -10000 );
|
|
}
|
|
UnlockWinStation( pWinStation );
|
|
if ( WaitTime != (ULONG)(-1) ) {
|
|
|
|
Status = NtWaitForSingleObject( pCommand->Event, FALSE, &Timeout );
|
|
|
|
}
|
|
else {
|
|
DBGPRINT(("Waiting for command with no timeout\n"));
|
|
Status = NtWaitForSingleObject( pCommand->Event, FALSE, NULL );
|
|
}
|
|
|
|
if ( !RelockWinStation( pWinStation ) )
|
|
Status = STATUS_CTX_CLOSE_PENDING;
|
|
|
|
|
|
if ( Status == STATUS_SUCCESS )
|
|
Status = pMsg->ReturnedStatus;
|
|
else if ( Status == STATUS_TIMEOUT ) {
|
|
|
|
BOOLEAN DesiredOperation = FALSE;
|
|
Status = STATUS_CTX_WINSTATION_BUSY;
|
|
|
|
// If this is a Connect/Reconnect/Disconnect operation and we timed out (for session owning the console, then we break here)
|
|
|
|
DesiredOperation = ( ((pMsg->ApiNumber == SMWinStationDoConnect) || (pMsg->ApiNumber == SMWinStationDoDisconnect)) && (pWinStation->fOwnsConsoleTerminal) ) ||
|
|
( (pMsg->ApiNumber == SMWinStationDoReconnect) && (pWinStation->fReconnectingToConsole) );
|
|
|
|
if (DesiredOperation) {
|
|
//#if DBG
|
|
// if (IsKernelDebuggerAttached()) {
|
|
// DbgPrint("SendWinStationCommand : LPC to Win32k timed out (Conn/Recon/Discon) for Session owning the Console. pMsg->ApiNumber = %d \n", pMsg->ApiNumber);
|
|
// DebugBreak();
|
|
// }
|
|
//#endif
|
|
goto WaitAgain;
|
|
}
|
|
|
|
}
|
|
|
|
if ( pCommand->Links.Flink != NULL )
|
|
RemoveEntryList( &pCommand->Links );
|
|
|
|
|
|
}
|
|
|
|
done:
|
|
if ( pCommand ) {
|
|
if ( pCommand->Event ) {
|
|
NtClose( pCommand->Event );
|
|
}
|
|
|
|
if ( !pMsg->WaitForReply && bFreeCommand ) {
|
|
|
|
// makarp:182622
|
|
if (bTitlenMessageAllocated)
|
|
{
|
|
ASSERT(pCommand->pMsg->u.SendMessage.pTitle);
|
|
ASSERT(pCommand->pMsg->u.SendMessage.pMessage);
|
|
|
|
MemFree(pCommand->pMsg->u.SendMessage.pTitle);
|
|
MemFree(pCommand->pMsg->u.SendMessage.pMessage);
|
|
}
|
|
|
|
if (bDomainUserNameAllocated)
|
|
{
|
|
ASSERT(pCommand->pMsg->u.LoadStringMessage.pDomain);
|
|
ASSERT(pCommand->pMsg->u.LoadStringMessage.pUserName);
|
|
|
|
MemFree(pCommand->pMsg->u.LoadStringMessage.pDomain);
|
|
MemFree(pCommand->pMsg->u.LoadStringMessage.pUserName);
|
|
}
|
|
|
|
MemFree( pCommand );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TRACE((hTrace,TC_ICASRV,TT_API1, "TERMSRV: SendWinStationCommand, LogonId=%d, Cmd=%s, Status=0x%x\n",
|
|
pWinStation->LogonId,
|
|
WinStationLpcName[pMsg->ApiNumber],
|
|
Status ));
|
|
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS RemoveBadHwnd(ULONG hWnd, ULONG SessionId);
|
|
|
|
NTSTATUS
|
|
WinStationWindowInvalid( PLPC_CLIENT_CONTEXT pContext,
|
|
PWINSTATION_APIMSG pMsg )
|
|
{
|
|
ASSERT(pMsg);
|
|
ASSERT(pMsg->ApiNumber == SMWinStationWindowInvalid);
|
|
ASSERT(pMsg->u.WindowInvalid.hWnd);
|
|
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
|
|
return RemoveBadHwnd(pMsg->u.WindowInvalid.hWnd, pMsg->u.WindowInvalid.SessionId);
|
|
}
|
|
|
|
|
|
VOID RemoveLpcContext(PVOID pContext)
|
|
{
|
|
PTERMSRVLPCCONTEXT pLpcContextEntry ;
|
|
PLIST_ENTRY Head, Next;
|
|
BOOL bFoundContext = FALSE;
|
|
|
|
|
|
Head = &gTermsrvLpcListHead;
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
|
|
/*
|
|
* Search the list for a the same context .
|
|
*/
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pLpcContextEntry = CONTAINING_RECORD( Next, TERMSRVLPCCONTEXT, Links );
|
|
if ( pLpcContextEntry->pContext == pContext ) {
|
|
RemoveEntryList(&pLpcContextEntry->Links);
|
|
bFoundContext = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
if (bFoundContext) {
|
|
MemFree(pLpcContextEntry);
|
|
}
|
|
|
|
}
|
|
|
|
BOOL GetSessionIdFromLpcContext(PLPC_CLIENT_CONTEXT pContext,
|
|
PULONG pSessionId)
|
|
{
|
|
PTERMSRVLPCCONTEXT pLpcContextEntry ;
|
|
PLIST_ENTRY Head, Next;
|
|
BOOL bFoundContext = FALSE;
|
|
|
|
|
|
Head = &gTermsrvLpcListHead;
|
|
RtlEnterCriticalSection( &ApiThreadLock );
|
|
|
|
/*
|
|
* Search the list for a the same context .
|
|
*/
|
|
for ( Next = Head->Flink; Next != Head; Next = Next->Flink ) {
|
|
pLpcContextEntry = CONTAINING_RECORD( Next, TERMSRVLPCCONTEXT, Links );
|
|
if ( pLpcContextEntry->pContext == pContext ) {
|
|
*pSessionId = pContext->ClientLogonId;
|
|
bFoundContext = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection( &ApiThreadLock );
|
|
return bFoundContext;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|