|
|
/*************************************************************************
* * 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; }
|