reactos/subsystems/win32/csrsrv/api.c
Hermès Bélusca-Maïto b75ee7fd14 [CSRSRV]
- Zero-out some allocated memory.
- During my investigations preceding the implementation of AttachConsole (r58166), I wanted (in a first attempt; finally I've found a better way to achieve what I wanted to do) to retrieve the CSR_PROCESS structure of the parent of a given process. I've found the 'Parent' member in the CSR_PROCESS structure, however this member was always initialized to NULL when new processes were created via CsrCreateProcess (and via the call to CsrInsertProcess). After looking at some informating here (http://svn.reactos.org/svn/reactos/trunk/reactos/include/subsys/csr/server.h?r1=17363&r2=17362&pathrev=17363) and there (http://forum.sysinternals.com/csrwalker-processes-detection-from-user-mode_topic15457.html), I became convinced that the 'Parent' member was unexistent starting from Windows Server 2003. Also, after much more investigation, I've found that the CsrInsertProcess function was called with only two parameters starting from Windows Server 2003 (and still continues in Windows 7), the always-NULL paramater being removed.
Therefore, I remove that unneeded parameter from CsrInsertProcess and the corresponding 'Parent' member from CSR_PROCESS.

svn path=/branches/ros-csrss/; revision=58232
2013-01-26 21:23:10 +00:00

1474 lines
48 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Client/Server Runtime SubSystem
* FILE: subsystems/win32/csrsrv/api.c
* PURPOSE: CSR Server DLL API LPC Implementation
* "\windows\ApiPort" port process management functions
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES *******************************************************************/
#include "srv.h"
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
BOOLEAN (*CsrClientThreadSetup)(VOID) = NULL;
UNICODE_STRING CsrApiPortName;
volatile LONG CsrpStaticThreadCount;
volatile LONG CsrpDynamicThreadTotal;
extern ULONG CsrMaxApiRequestThreads;
/* FUNCTIONS ******************************************************************/
/*++
* @name CsrCallServerFromServer
* @implemented NT4
*
* The CsrCallServerFromServer routine calls a CSR API from within a server.
* It avoids using LPC messages since the request isn't coming from a client.
*
* @param ReceiveMsg
* Pointer to the CSR API Message to send to the server.
*
* @param ReplyMsg
* Pointer to the CSR API Message to receive from the server.
*
* @return STATUS_SUCCESS in case of success, STATUS_ILLEGAL_FUNCTION
* if the ApiNumber is invalid, or STATUS_ACCESS_VIOLATION if there
* was a problem executing the API.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrCallServerFromServer(IN PCSR_API_MESSAGE ReceiveMsg,
IN OUT PCSR_API_MESSAGE ReplyMsg)
{
ULONG ServerId;
PCSR_SERVER_DLL ServerDll;
ULONG ApiId;
CSR_REPLY_CODE ReplyCode = CsrReplyImmediately;
/* Get the Server ID */
ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg->ApiNumber);
/* Make sure that the ID is within limits, and the Server DLL loaded */
if ((ServerId >= CSR_SERVER_DLL_MAX) ||
(!(ServerDll = CsrLoadedServerDll[ServerId])))
{
/* We are beyond the Maximum Server ID */
DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", ServerId, ServerDll);
ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION;
return STATUS_ILLEGAL_FUNCTION;
}
else
{
/* Get the API ID, normalized with our Base ID */
ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg->ApiNumber) - ServerDll->ApiBase;
/* Make sure that the ID is within limits, and the entry exists */
if ((ApiId >= ServerDll->HighestApiSupported) ||
((ServerDll->ValidTable) && !(ServerDll->ValidTable[ApiId])))
{
/* We are beyond the Maximum API ID, or it doesn't exist */
DPRINT1("CSRSS: %lx (%s) is invalid ApiTableIndex for %Z or is an "
"invalid API to call from the server.\n",
ServerDll->ValidTable[ApiId],
((ServerDll->NameTable) && (ServerDll->NameTable[ApiId])) ?
ServerDll->NameTable[ApiId] : "*** UNKNOWN ***", &ServerDll->Name);
DbgBreakPoint();
ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION;
return STATUS_ILLEGAL_FUNCTION;
}
}
if (CsrDebug & 2)
{
DPRINT1("CSRSS: %s Api Request received from server process\n",
ServerDll->NameTable[ApiId]);
}
/* Validation complete, start SEH */
_SEH2_TRY
{
/* Call the API, get the reply code and return the result */
ReplyMsg->Status = ServerDll->DispatchTable[ApiId](ReceiveMsg, &ReplyCode);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* If we got an exception, return access violation */
ReplyMsg->Status = STATUS_ACCESS_VIOLATION;
}
_SEH2_END;
/* Return success */
return STATUS_SUCCESS;
}
/*++
* @name CsrApiHandleConnectionRequest
*
* The CsrApiHandleConnectionRequest routine handles and accepts a new
* connection request to the CSR API LPC Port.
*
* @param ApiMessage
* Pointer to the incoming CSR API Message which contains the
* connection request.
*
* @return STATUS_SUCCESS in case of success, or status code which caused
* the routine to error.
*
* @remarks This routine is responsible for attaching the Shared Section to
* new clients connecting to CSR.
*
*--*/
NTSTATUS
NTAPI
CsrApiHandleConnectionRequest(IN PCSR_API_MESSAGE ApiMessage)
{
PCSR_THREAD CsrThread = NULL;
PCSR_PROCESS CsrProcess = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PCSR_CONNECTION_INFO ConnectInfo = &ApiMessage->ConnectionInfo;
BOOLEAN AllowConnection = FALSE;
REMOTE_PORT_VIEW RemotePortView;
HANDLE ServerPort;
/* Acquire the Process Lock */
CsrAcquireProcessLock();
/* Lookup the CSR Thread */
CsrThread = CsrLocateThreadByClientId(NULL, &ApiMessage->Header.ClientId);
/* Check if we have a thread */
if (CsrThread)
{
/* Get the Process */
CsrProcess = CsrThread->Process;
/* Make sure we have a Process as well */
if (CsrProcess)
{
/* Reference the Process */
CsrLockedReferenceProcess(CsrThread->Process);
/* Release the lock */
CsrReleaseProcessLock();
/* Duplicate the Object Directory */
Status = NtDuplicateObject(NtCurrentProcess(),
CsrObjectDirectory,
CsrProcess->ProcessHandle,
&ConnectInfo->ObjectDirectory,
0,
0,
DUPLICATE_SAME_ACCESS |
DUPLICATE_SAME_ATTRIBUTES);
/* Acquire the lock */
CsrAcquireProcessLock();
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Attach the Shared Section */
Status = CsrSrvAttachSharedSection(CsrProcess, ConnectInfo);
/* Check how this went */
if (NT_SUCCESS(Status)) AllowConnection = TRUE;
}
/* Dereference the project */
CsrLockedDereferenceProcess(CsrProcess);
}
}
/* Release the lock */
CsrReleaseProcessLock();
/* Setup the Port View Structure */
RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
RemotePortView.ViewSize = 0;
RemotePortView.ViewBase = NULL;
/* Save the Process ID */
ConnectInfo->ProcessId = NtCurrentTeb()->ClientId.UniqueProcess;
/* Accept the Connection */
Status = NtAcceptConnectPort(&ServerPort,
AllowConnection ? UlongToPtr(CsrProcess->SequenceNumber) : 0,
&ApiMessage->Header,
AllowConnection,
NULL,
&RemotePortView);
if (!NT_SUCCESS(Status))
{
DPRINT1("CSRSS: NtAcceptConnectPort - failed. Status == %X\n", Status);
}
else if (AllowConnection)
{
if (CsrDebug & 2)
{
DPRINT1("CSRSS: ClientId: %lx.%lx has ClientView: Base=%p, Size=%lx\n",
ApiMessage->Header.ClientId.UniqueProcess,
ApiMessage->Header.ClientId.UniqueThread,
RemotePortView.ViewBase,
RemotePortView.ViewSize);
}
/* Set some Port Data in the Process */
CsrProcess->ClientPort = ServerPort;
CsrProcess->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase;
CsrProcess->ClientViewBounds = (ULONG_PTR)((ULONG_PTR)RemotePortView.ViewBase +
(ULONG_PTR)RemotePortView.ViewSize);
/* Complete the connection */
Status = NtCompleteConnectPort(ServerPort);
if (!NT_SUCCESS(Status))
{
DPRINT1("CSRSS: NtCompleteConnectPort - failed. Status == %X\n", Status);
}
}
else
{
DPRINT1("CSRSS: Rejecting Connection Request from ClientId: %lx.%lx\n",
ApiMessage->Header.ClientId.UniqueProcess,
ApiMessage->Header.ClientId.UniqueThread);
}
/* Return status to caller */
return Status;
}
/*++
* @name CsrpCheckRequestThreads
*
* The CsrpCheckRequestThreads routine checks if there are no more threads
* to handle CSR API Requests, and creates a new thread if possible, to
* avoid starvation.
*
* @param None.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
* if a new thread couldn't be created.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrpCheckRequestThreads(VOID)
{
HANDLE hThread;
CLIENT_ID ClientId;
NTSTATUS Status;
/* Decrease the count, and see if we're out */
if (!(_InterlockedDecrement(&CsrpStaticThreadCount)))
{
/* Check if we've still got space for a Dynamic Thread */
if (CsrpDynamicThreadTotal < CsrMaxApiRequestThreads)
{
/* Create a new dynamic thread */
Status = RtlCreateUserThread(NtCurrentProcess(),
NULL,
TRUE,
0,
0,
0,
(PVOID)CsrApiRequestThread,
NULL,
&hThread,
&ClientId);
/* Check success */
if (NT_SUCCESS(Status))
{
/* Increase the thread counts */
_InterlockedIncrement(&CsrpStaticThreadCount);
_InterlockedIncrement(&CsrpDynamicThreadTotal);
/* Add a new server thread */
if (CsrAddStaticServerThread(hThread,
&ClientId,
CsrThreadIsServerThread))
{
/* Activate it */
NtResumeThread(hThread, NULL);
}
else
{
/* Failed to create a new static thread */
_InterlockedDecrement(&CsrpStaticThreadCount);
_InterlockedDecrement(&CsrpDynamicThreadTotal);
/* Terminate it */
DPRINT1("Failing\n");
NtTerminateThread(hThread, 0);
NtClose(hThread);
/* Return */
return STATUS_UNSUCCESSFUL;
}
}
}
}
/* Success */
return STATUS_SUCCESS;
}
/*++
* @name CsrApiRequestThread
*
* The CsrApiRequestThread routine handles incoming messages or connection
* requests on the CSR API LPC Port.
*
* @param Parameter
* System-default user-defined parameter. Unused.
*
* @return The thread exit code, if the thread is terminated.
*
* @remarks Before listening on the port, the routine will first attempt
* to connect to the user subsystem.
*
*--*/
NTSTATUS
NTAPI
CsrApiRequestThread(IN PVOID Parameter)
{
PTEB Teb = NtCurrentTeb();
LARGE_INTEGER TimeOut;
PCSR_THREAD CurrentThread, CsrThread;
NTSTATUS Status;
CSR_REPLY_CODE ReplyCode;
PCSR_API_MESSAGE ReplyMsg;
CSR_API_MESSAGE ReceiveMsg;
PCSR_PROCESS CsrProcess;
PHARDERROR_MSG HardErrorMsg;
PVOID PortContext;
PCSR_SERVER_DLL ServerDll;
PCLIENT_DIED_MSG ClientDiedMsg;
PDBGKM_MSG DebugMessage;
ULONG ServerId, ApiId, MessageType, i;
HANDLE ReplyPort;
/* Setup LPC loop port and message */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
/* Connect to user32 */
while (!CsrConnectToUser())
{
/* Set up the timeout for the connect (30 seconds) */
TimeOut.QuadPart = -30 * 1000 * 1000 * 10;
/* Keep trying until we get a response */
Teb->Win32ClientInfo[0] = 0;
NtDelayExecution(FALSE, &TimeOut);
}
/* Get our thread */
CurrentThread = Teb->CsrClientThread;
/* If we got an event... */
if (Parameter)
{
/* Set it, to let stuff waiting on us load */
Status = NtSetEvent((HANDLE)Parameter, NULL);
ASSERT(NT_SUCCESS(Status));
/* Increase the Thread Counts */
_InterlockedIncrement(&CsrpStaticThreadCount);
_InterlockedIncrement(&CsrpDynamicThreadTotal);
}
/* Now start the loop */
while (TRUE)
{
/* Make sure the real CID is set */
Teb->RealClientId = Teb->ClientId;
/* Debug check */
if (Teb->CountOfOwnedCriticalSections)
{
DPRINT1("CSRSRV: FATAL ERROR. CsrThread is Idle while holding %lu critical sections\n",
Teb->CountOfOwnedCriticalSections);
DPRINT1("CSRSRV: Last Receive Message %lx ReplyMessage %lx\n",
&ReceiveMsg, ReplyMsg);
DbgBreakPoint();
}
/* Wait for a message to come through */
Status = NtReplyWaitReceivePort(ReplyPort,
&PortContext,
&ReplyMsg->Header,
&ReceiveMsg.Header);
/* Check if we didn't get success */
if (Status != STATUS_SUCCESS)
{
/* Was it a failure or another success code? */
if (!NT_SUCCESS(Status))
{
/* Check for specific status cases */
if ((Status != STATUS_INVALID_CID) &&
(Status != STATUS_UNSUCCESSFUL) &&
((Status == STATUS_INVALID_HANDLE) || (ReplyPort == CsrApiPort)))
{
/* Notify the debugger */
DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
DPRINT1("CSRSS: ReplyPortHandle %lx CsrApiPort %lx\n", ReplyPort, CsrApiPort);
}
/* We failed big time, so start out fresh */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
continue;
}
else
{
/* A bizare "success" code, just try again */
DPRINT1("NtReplyWaitReceivePort returned \"success\" status 0x%x\n", Status);
continue;
}
}
/* Use whatever Client ID we got */
Teb->RealClientId = ReceiveMsg.Header.ClientId;
/* Get the Message Type */
MessageType = ReceiveMsg.Header.u2.s2.Type;
/* Handle connection requests */
if (MessageType == LPC_CONNECTION_REQUEST)
{
/* Handle the Connection Request */
CsrApiHandleConnectionRequest(&ReceiveMsg);
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
continue;
}
/* It's some other kind of request. Get the lock for the lookup */
CsrAcquireProcessLock();
/* Now do the lookup to get the CSR_THREAD */
CsrThread = CsrLocateThreadByClientId(&CsrProcess,
&ReceiveMsg.Header.ClientId);
/* Did we find a thread? */
if (!CsrThread)
{
/* This wasn't a CSR Thread, release lock */
CsrReleaseProcessLock();
/* If this was an exception, handle it */
if (MessageType == LPC_EXCEPTION)
{
ReplyMsg = &ReceiveMsg;
ReplyPort = CsrApiPort;
ReplyMsg->Status = DBG_CONTINUE;
}
else if (MessageType == LPC_PORT_CLOSED ||
MessageType == LPC_CLIENT_DIED)
{
/* The Client or Port are gone, loop again */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
else if (MessageType == LPC_ERROR_EVENT)
{
/* If it's a hard error, handle this too */
HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg;
/* Default it to unhandled */
HardErrorMsg->Response = ResponseNotHandled;
/* Check if there are free api threads */
CsrpCheckRequestThreads();
if (CsrpStaticThreadCount)
{
/* Loop every Server DLL */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the Server DLL */
ServerDll = CsrLoadedServerDll[i];
/* Check if it's valid and if it has a Hard Error Callback */
if ((ServerDll) && (ServerDll->HardErrorCallback))
{
/* Call it */
ServerDll->HardErrorCallback(NULL /* == CsrThread */, HardErrorMsg);
/* If it's handled, get out of here */
if (HardErrorMsg->Response != ResponseNotHandled) break;
}
}
}
/* Increase the thread count */
_InterlockedIncrement(&CsrpStaticThreadCount);
/* If the response was 0xFFFFFFFF, we'll ignore it */
if (HardErrorMsg->Response == 0xFFFFFFFF)
{
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
else
{
ReplyMsg = &ReceiveMsg;
ReplyPort = CsrApiPort;
}
}
else if (MessageType == LPC_REQUEST)
{
/* This is an API Message coming from a non-CSR Thread */
ReplyMsg = &ReceiveMsg;
ReplyPort = CsrApiPort;
ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
}
else if (MessageType == LPC_DATAGRAM)
{
/* This is an API call, get the Server ID */
ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg.ApiNumber);
/* Make sure that the ID is within limits, and the Server DLL loaded */
ServerDll = NULL;
if ((ServerId >= CSR_SERVER_DLL_MAX) ||
(!(ServerDll = CsrLoadedServerDll[ServerId])))
{
/* We are beyond the Maximum Server ID */
DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n",
ServerId, ServerDll);
DbgBreakPoint();
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
continue;
}
/* Get the API ID, normalized with our Base ID */
ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber) - ServerDll->ApiBase;
/* Make sure that the ID is within limits, and the entry exists */
if (ApiId >= ServerDll->HighestApiSupported)
{
/* We are beyond the Maximum API ID, or it doesn't exist */
DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n",
CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber),
&ServerDll->Name);
ReplyPort = CsrApiPort;
ReplyMsg = NULL;
continue;
}
if (CsrDebug & 2)
{
DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x\n",
Teb->ClientId.UniqueThread,
ReceiveMsg.Header.ClientId.UniqueProcess,
ReceiveMsg.Header.ClientId.UniqueThread,
ServerDll->NameTable[ApiId],
NULL);
}
/* Assume success */
ReceiveMsg.Status = STATUS_SUCCESS;
/* Validation complete, start SEH */
_SEH2_TRY
{
/* Make sure we have enough threads */
CsrpCheckRequestThreads();
/* Call the API and get the reply code */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
ServerDll->DispatchTable[ApiId](&ReceiveMsg, &ReplyCode);
/* Increase the static thread count */
_InterlockedIncrement(&CsrpStaticThreadCount);
}
_SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
{
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
_SEH2_END;
}
else
{
/* Some other ignored message type */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
/* Keep going */
continue;
}
/* We have a valid thread, was this an LPC Request? */
if (MessageType != LPC_REQUEST)
{
/* It's not an API, check if the client died */
if (MessageType == LPC_CLIENT_DIED)
{
/* Get the information and check if it matches our thread */
ClientDiedMsg = (PCLIENT_DIED_MSG)&ReceiveMsg;
if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart)
{
/* Now we reply to the dying client */
ReplyPort = CsrThread->Process->ClientPort;
/* Reference the thread */
CsrLockedReferenceThread(CsrThread);
/* Destroy the thread in the API Message */
CsrDestroyThread(&ReceiveMsg.Header.ClientId);
/* Check if the thread was actually ourselves */
if (CsrProcess->ThreadCount == 1)
{
/* Kill the process manually here */
CsrDestroyProcess(&CsrThread->ClientId, 0);
}
/* Remove our extra reference */
CsrLockedDereferenceThread(CsrThread);
}
/* Release the lock and keep looping */
CsrReleaseProcessLock();
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
continue;
}
/* Reference the thread and release the lock */
CsrLockedReferenceThread(CsrThread);
CsrReleaseProcessLock();
/* Check if this was an exception */
if (MessageType == LPC_EXCEPTION)
{
/* Kill the process */
NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED);
/* Destroy it from CSR */
CsrDestroyProcess(&ReceiveMsg.Header.ClientId, STATUS_ABANDONED);
/* Return a Debug Message */
DebugMessage = (PDBGKM_MSG)&ReceiveMsg;
DebugMessage->ReturnedStatus = DBG_CONTINUE;
ReplyMsg = &ReceiveMsg;
ReplyPort = CsrApiPort;
/* Remove our extra reference */
CsrDereferenceThread(CsrThread);
}
else if (MessageType == LPC_ERROR_EVENT)
{
/* If it's a hard error, handle this too */
HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg;
/* Default it to unhandled */
HardErrorMsg->Response = ResponseNotHandled;
/* Check if there are free api threads */
CsrpCheckRequestThreads();
if (CsrpStaticThreadCount)
{
/* Loop every Server DLL */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the Server DLL */
ServerDll = CsrLoadedServerDll[i];
/* Check if it's valid and if it has a Hard Error Callback */
if ((ServerDll) && (ServerDll->HardErrorCallback))
{
/* Call it */
ServerDll->HardErrorCallback(CsrThread, HardErrorMsg);
/* If it's handled, get out of here */
if (HardErrorMsg->Response != ResponseNotHandled) break;
}
}
}
/* Increase the thread count */
_InterlockedIncrement(&CsrpStaticThreadCount);
/* If the response was 0xFFFFFFFF, we'll ignore it */
if (HardErrorMsg->Response == 0xFFFFFFFF)
{
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
else
{
CsrDereferenceThread(CsrThread);
ReplyMsg = &ReceiveMsg;
ReplyPort = CsrApiPort;
}
}
else
{
/* Something else */
CsrDereferenceThread(CsrThread);
ReplyMsg = NULL;
}
/* Keep looping */
continue;
}
/* We got an API Request */
CsrLockedReferenceThread(CsrThread);
CsrReleaseProcessLock();
/* This is an API call, get the Server ID */
ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg.ApiNumber);
/* Make sure that the ID is within limits, and the Server DLL loaded */
ServerDll = NULL;
if ((ServerId >= CSR_SERVER_DLL_MAX) ||
(!(ServerDll = CsrLoadedServerDll[ServerId])))
{
/* We are beyond the Maximum Server ID */
DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n",
ServerId, ServerDll);
DbgBreakPoint();
ReplyPort = CsrApiPort;
ReplyMsg = &ReceiveMsg;
ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
CsrDereferenceThread(CsrThread);
continue;
}
/* Get the API ID, normalized with our Base ID */
ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber) - ServerDll->ApiBase;
/* Make sure that the ID is within limits, and the entry exists */
if (ApiId >= ServerDll->HighestApiSupported)
{
/* We are beyond the Maximum API ID, or it doesn't exist */
DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n",
CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber),
&ServerDll->Name);
ReplyPort = CsrApiPort;
ReplyMsg = &ReceiveMsg;
ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
CsrDereferenceThread(CsrThread);
continue;
}
if (CsrDebug & 2)
{
DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x, Process %08x - %08x\n",
Teb->ClientId.UniqueThread,
ReceiveMsg.Header.ClientId.UniqueProcess,
ReceiveMsg.Header.ClientId.UniqueThread,
ServerDll->NameTable[ApiId],
CsrThread,
CsrThread->Process,
CsrProcess);
}
/* Assume success */
ReplyMsg = &ReceiveMsg;
ReceiveMsg.Status = STATUS_SUCCESS;
/* Now we reply to a particular client */
ReplyPort = CsrThread->Process->ClientPort;
/* Check if there's a capture buffer */
if (ReceiveMsg.CsrCaptureData)
{
/* Capture the arguments */
if (!CsrCaptureArguments(CsrThread, &ReceiveMsg))
{
/* Ignore this message if we failed to get the arguments */
CsrDereferenceThread(CsrThread);
continue;
}
}
/* Validation complete, start SEH */
_SEH2_TRY
{
/* Make sure we have enough threads */
CsrpCheckRequestThreads();
Teb->CsrClientThread = CsrThread;
/* Call the API, get the reply code and return the result */
ReplyCode = CsrReplyImmediately;
ReplyMsg->Status = ServerDll->DispatchTable[ApiId](&ReceiveMsg, &ReplyCode);
/* Increase the static thread count */
_InterlockedIncrement(&CsrpStaticThreadCount);
Teb->CsrClientThread = CurrentThread;
if (ReplyCode == CsrReplyAlreadySent)
{
if (ReceiveMsg.CsrCaptureData)
{
CsrReleaseCapturedArguments(&ReceiveMsg);
}
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
CsrDereferenceThread(CsrThread);
}
else if (ReplyCode == CsrReplyDeadClient)
{
/* Reply to the death message */
NtReplyPort(ReplyPort, &ReplyMsg->Header);
/* Reply back to the API port now */
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
CsrDereferenceThread(CsrThread);
}
else if (ReplyCode == CsrReplyPending)
{
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
else
{
if (ReceiveMsg.CsrCaptureData)
{
CsrReleaseCapturedArguments(&ReceiveMsg);
}
CsrDereferenceThread(CsrThread);
}
}
_SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
{
ReplyMsg = NULL;
ReplyPort = CsrApiPort;
}
_SEH2_END;
}
/* We're out of the loop for some reason, terminate! */
NtTerminateThread(NtCurrentThread(), Status);
return Status;
}
/*++
* @name CsrApiPortInitialize
*
* The CsrApiPortInitialize routine initializes the LPC Port used for
* communications with the Client/Server Runtime (CSR) and initializes the
* static thread that will handle connection requests and APIs.
*
* @param None
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
* otherwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrApiPortInitialize(VOID)
{
ULONG Size;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE hRequestEvent, hThread;
CLIENT_ID ClientId;
PLIST_ENTRY ListHead, NextEntry;
PCSR_THREAD ServerThread;
/* Calculate how much space we'll need for the Port Name */
Size = CsrDirectoryName.Length + sizeof(CSR_PORT_NAME) + sizeof(WCHAR);
/* Create the buffer for it */
CsrApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size);
if (!CsrApiPortName.Buffer) return STATUS_NO_MEMORY;
/* Setup the rest of the empty string */
CsrApiPortName.Length = 0;
CsrApiPortName.MaximumLength = (USHORT)Size;
RtlAppendUnicodeStringToString(&CsrApiPortName, &CsrDirectoryName);
RtlAppendUnicodeToString(&CsrApiPortName, UNICODE_PATH_SEP);
RtlAppendUnicodeToString(&CsrApiPortName, CSR_PORT_NAME);
if (CsrDebug & 1)
{
DPRINT1("CSRSS: Creating %wZ port and associated threads\n", &CsrApiPortName);
DPRINT1("CSRSS: sizeof( CONNECTINFO ) == %ld sizeof( API_MSG ) == %ld\n",
sizeof(CSR_CONNECTION_INFO), sizeof(CSR_API_MESSAGE));
}
/* FIXME: Create a Security Descriptor */
/* Initialize the Attributes */
InitializeObjectAttributes(&ObjectAttributes,
&CsrApiPortName,
0,
NULL,
NULL /* FIXME: Use the Security Descriptor */);
/* Create the Port Object */
Status = NtCreatePort(&CsrApiPort,
&ObjectAttributes,
sizeof(CSR_CONNECTION_INFO),
sizeof(CSR_API_MESSAGE),
16 * PAGE_SIZE);
if (NT_SUCCESS(Status))
{
/* Create the event the Port Thread will use */
Status = NtCreateEvent(&hRequestEvent,
EVENT_ALL_ACCESS,
NULL,
SynchronizationEvent,
FALSE);
if (NT_SUCCESS(Status))
{
/* Create the Request Thread */
Status = RtlCreateUserThread(NtCurrentProcess(),
NULL,
TRUE,
0,
0,
0,
(PVOID)CsrApiRequestThread,
(PVOID)hRequestEvent,
&hThread,
&ClientId);
if (NT_SUCCESS(Status))
{
/* Add this as a static thread to CSRSRV */
CsrAddStaticServerThread(hThread, &ClientId, CsrThreadIsServerThread);
/* Get the Thread List Pointers */
ListHead = &CsrRootProcess->ThreadList;
NextEntry = ListHead->Flink;
/* Start looping the list */
while (NextEntry != ListHead)
{
/* Get the Thread */
ServerThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
/* Start it up */
Status = NtResumeThread(ServerThread->ThreadHandle, NULL);
/* Is this a Server Thread? */
if (ServerThread->Flags & CsrThreadIsServerThread)
{
/* If so, then wait for it to initialize */
Status = NtWaitForSingleObject(hRequestEvent, FALSE, NULL);
ASSERT(NT_SUCCESS(Status));
}
/* Next thread */
NextEntry = NextEntry->Flink;
}
/* We don't need this anymore */
NtClose(hRequestEvent);
}
}
}
/* Return */
return Status;
}
/*++
* @name CsrConnectToUser
* @implemented NT4
*
* The CsrConnectToUser connects to the User subsystem.
*
* @param None
*
* @return A pointer to the CSR Thread
*
* @remarks None.
*
*--*/
PCSR_THREAD
NTAPI
CsrConnectToUser(VOID)
{
#if 0 // This code is OK, however it is ClientThreadSetup which sucks.
NTSTATUS Status;
ANSI_STRING DllName;
UNICODE_STRING TempName;
HANDLE hUser32;
STRING StartupName;
PTEB Teb = NtCurrentTeb();
PCSR_THREAD CsrThread;
BOOLEAN Connected;
/* Check if we didn't already find it */
if (!CsrClientThreadSetup)
{
/* Get the DLL Handle for user32.dll */
RtlInitAnsiString(&DllName, "user32");
RtlAnsiStringToUnicodeString(&TempName, &DllName, TRUE);
Status = LdrGetDllHandle(NULL,
NULL,
&TempName,
&hUser32);
RtlFreeUnicodeString(&TempName);
/* If we got the handle, get the Client Thread Startup Entrypoint */
if (NT_SUCCESS(Status))
{
RtlInitAnsiString(&StartupName,"ClientThreadSetup");
Status = LdrGetProcedureAddress(hUser32,
&StartupName,
0,
(PVOID)&CsrClientThreadSetup);
}
}
/* Connect to user32 */
_SEH2_TRY
{
Connected = CsrClientThreadSetup();
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Connected = FALSE;
} _SEH2_END;
if (!Connected)
{
DPRINT1("CSRSS: CsrConnectToUser failed\n");
return NULL;
}
/* Save pointer to this thread in TEB */
CsrAcquireProcessLock();
CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId);
CsrReleaseProcessLock();
if (CsrThread) Teb->CsrClientThread = CsrThread;
/* Return it */
return CsrThread;
#else
PTEB Teb = NtCurrentTeb();
PCSR_THREAD CsrThread;
/* Save pointer to this thread in TEB */
CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId);
if (CsrThread) Teb->CsrClientThread = CsrThread;
/* Return it */
return CsrThread;
#endif
}
/*++
* @name CsrQueryApiPort
* @implemented NT4
*
* The CsrQueryApiPort routine returns a handle to the CSR API LPC port.
*
* @param None.
*
* @return A handle to the port.
*
* @remarks None.
*
*--*/
HANDLE
NTAPI
CsrQueryApiPort(VOID)
{
DPRINT("CSRSRV: %s called\n", __FUNCTION__);
return CsrApiPort;
}
/*++
* @name CsrCaptureArguments
* @implemented NT5.1
*
* The CsrCaptureArguments routine validates a CSR Capture Buffer and
* re-captures it into a server CSR Capture Buffer.
*
* @param CsrThread
* Pointer to the CSR Thread performing the validation.
*
* @param ApiMessage
* Pointer to the CSR API Message containing the Capture Buffer
* that needs to be validated.
*
* @return TRUE if validation succeeded, FALSE otherwise.
*
* @remarks None.
*
*--*/
BOOLEAN
NTAPI
CsrCaptureArguments(IN PCSR_THREAD CsrThread,
IN PCSR_API_MESSAGE ApiMessage)
{
PCSR_CAPTURE_BUFFER LocalCaptureBuffer = NULL, RemoteCaptureBuffer = NULL;
SIZE_T BufferDistance;
ULONG Length = 0;
ULONG PointerCount;
PULONG_PTR OffsetPointer;
ULONG_PTR CurrentOffset;
/* Use SEH to make sure this is valid */
_SEH2_TRY
{
/* Get the buffer we got from whoever called NTDLL */
LocalCaptureBuffer = ApiMessage->CsrCaptureData;
Length = LocalCaptureBuffer->Size;
/* Now check if the buffer is inside our mapped section */
if (((ULONG_PTR)LocalCaptureBuffer < CsrThread->Process->ClientViewBase) ||
(((ULONG_PTR)LocalCaptureBuffer + Length) >= CsrThread->Process->ClientViewBounds))
{
/* Return failure */
DPRINT1("*** CSRSS: CaptureBuffer outside of ClientView\n");
ApiMessage->Status = STATUS_INVALID_PARAMETER;
_SEH2_YIELD(return FALSE);
}
/* Check if the Length is valid */
if ((FIELD_OFFSET(CSR_CAPTURE_BUFFER, PointerOffsetsArray) +
(LocalCaptureBuffer->PointerCount * sizeof(PVOID)) > Length) ||
(Length > MAXWORD))
{
/* Return failure */
DPRINT1("*** CSRSS: CaptureBuffer %p has bad length\n", LocalCaptureBuffer);
DbgBreakPoint();
ApiMessage->Status = STATUS_INVALID_PARAMETER;
_SEH2_YIELD(return FALSE);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Return failure */
ApiMessage->Status = STATUS_INVALID_PARAMETER;
_SEH2_YIELD(return FALSE);
} _SEH2_END;
/* We validated the incoming buffer, now allocate the remote one */
RemoteCaptureBuffer = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Length);
if (!RemoteCaptureBuffer)
{
/* We're out of memory */
ApiMessage->Status = STATUS_NO_MEMORY;
return FALSE;
}
/* Copy the client's buffer */
RtlMoveMemory(RemoteCaptureBuffer, LocalCaptureBuffer, Length);
/* Calculate the difference between our buffer and the client's */
BufferDistance = (ULONG_PTR)RemoteCaptureBuffer - (ULONG_PTR)LocalCaptureBuffer;
/*
* All the pointer offsets correspond to pointers which point
* to the remote data buffer instead of the local one.
*/
PointerCount = RemoteCaptureBuffer->PointerCount;
OffsetPointer = RemoteCaptureBuffer->PointerOffsetsArray;
while (PointerCount--)
{
CurrentOffset = *OffsetPointer;
if (CurrentOffset != 0)
{
/* Get the pointer corresponding to the offset */
CurrentOffset += (ULONG_PTR)ApiMessage;
/* Validate the bounds of the current pointed pointer */
if ((*(PULONG_PTR)CurrentOffset >= CsrThread->Process->ClientViewBase) &&
(*(PULONG_PTR)CurrentOffset < CsrThread->Process->ClientViewBounds))
{
/* Modify the pointed pointer to take into account its new position */
*(PULONG_PTR)CurrentOffset += BufferDistance;
}
else
{
/* Invalid pointer, fail */
DPRINT1("*** CSRSS: CaptureBuffer MessagePointer outside of ClientView\n");
DbgBreakPoint();
ApiMessage->Status = STATUS_INVALID_PARAMETER;
}
}
++OffsetPointer;
}
/* Check if we got success */
if (ApiMessage->Status != STATUS_SUCCESS)
{
/* Failure. Free the buffer and return */
RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer);
return FALSE;
}
else
{
/* Success, save the previous buffer and use the remote capture buffer */
RemoteCaptureBuffer->PreviousCaptureBuffer = LocalCaptureBuffer;
ApiMessage->CsrCaptureData = RemoteCaptureBuffer;
}
/* Success */
return TRUE;
}
/*++
* @name CsrReleaseCapturedArguments
* @implemented NT5.1
*
* The CsrReleaseCapturedArguments routine releases a Capture Buffer
* that was previously captured with CsrCaptureArguments.
*
* @param ApiMessage
* Pointer to the CSR API Message containing the Capture Buffer
* that needs to be released.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage)
{
PCSR_CAPTURE_BUFFER RemoteCaptureBuffer, LocalCaptureBuffer;
SIZE_T BufferDistance;
ULONG PointerCount;
PULONG_PTR OffsetPointer;
ULONG_PTR CurrentOffset;
/* Get the remote capture buffer */
RemoteCaptureBuffer = ApiMessage->CsrCaptureData;
/* Do not continue if there is no captured buffer */
if (!RemoteCaptureBuffer) return;
/* If there is one, get the corresponding local capture buffer */
LocalCaptureBuffer = RemoteCaptureBuffer->PreviousCaptureBuffer;
/* Free the previous one and use again the local capture buffer */
RemoteCaptureBuffer->PreviousCaptureBuffer = NULL;
ApiMessage->CsrCaptureData = LocalCaptureBuffer;
/* Calculate the difference between our buffer and the client's */
BufferDistance = (ULONG_PTR)RemoteCaptureBuffer - (ULONG_PTR)LocalCaptureBuffer;
/*
* All the pointer offsets correspond to pointers which point
* to the local data buffer instead of the remote one (revert
* the logic of CsrCaptureArguments).
*/
PointerCount = RemoteCaptureBuffer->PointerCount;
OffsetPointer = RemoteCaptureBuffer->PointerOffsetsArray;
while (PointerCount--)
{
CurrentOffset = *OffsetPointer;
if (CurrentOffset != 0)
{
/* Get the pointer corresponding to the offset */
CurrentOffset += (ULONG_PTR)ApiMessage;
/* Modify the pointed pointer to take into account its new position */
*(PULONG_PTR)CurrentOffset -= BufferDistance;
}
++OffsetPointer;
}
/* Copy the data back */
RtlMoveMemory(LocalCaptureBuffer, RemoteCaptureBuffer, RemoteCaptureBuffer->Size);
/* Free our allocated buffer */
RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer);
}
/*++
* @name CsrValidateMessageBuffer
* @implemented NT5.1
*
* The CsrValidateMessageBuffer routine validates a captured message buffer
* present in the CSR Api Message
*
* @param ApiMessage
* Pointer to the CSR API Message containing the CSR Capture Buffer.
*
* @param Buffer
* Pointer to the message buffer to validate.
*
* @param ElementCount
* Number of elements contained in the message buffer.
*
* @param ElementSize
* Size of each element.
*
* @return TRUE if validation succeeded, FALSE otherwise.
*
* @remarks None.
*
*--*/
BOOLEAN
NTAPI
CsrValidateMessageBuffer(IN PCSR_API_MESSAGE ApiMessage,
IN PVOID *Buffer,
IN ULONG ElementCount,
IN ULONG ElementSize)
{
PCSR_CAPTURE_BUFFER CaptureBuffer = ApiMessage->CsrCaptureData;
SIZE_T BufferDistance = (ULONG_PTR)Buffer - (ULONG_PTR)ApiMessage;
ULONG PointerCount;
PULONG_PTR OffsetPointer;
/*
* Check whether we have a valid buffer pointer, elements
* of non-trivial size and that we don't overflow.
*/
if (!Buffer || ElementSize == 0 ||
(ULONGLONG)ElementCount * ElementSize > (ULONGLONG)0xFFFFFFFF)
{
return FALSE;
}
/* Check if didn't get a buffer and there aren't any arguments to check */
// if (!*Buffer && (ElementCount * ElementSize == 0))
if (!*Buffer && ElementCount == 0) // Here ElementSize != 0 therefore only ElementCount can be == 0
return TRUE;
/* Check if we have no capture buffer */
if (!CaptureBuffer)
{
/*
* In this case, check only the Process ID
* and if there is a match, we succeed.
*/
if (NtCurrentTeb()->ClientId.UniqueProcess ==
ApiMessage->Header.ClientId.UniqueProcess)
{
return TRUE;
}
}
else
{
/* Make sure that there is still space left in the buffer */
if ((CaptureBuffer->Size - (ULONG_PTR)*Buffer + (ULONG_PTR)CaptureBuffer) >=
(ElementCount * ElementSize))
{
/* Perform the validation test */
PointerCount = CaptureBuffer->PointerCount;
OffsetPointer = CaptureBuffer->PointerOffsetsArray;
while (PointerCount--)
{
/*
* The pointer offset must be equal to the delta between
* the addresses of the buffer and of the API message.
*/
if (*OffsetPointer == BufferDistance)
{
return TRUE;
}
++OffsetPointer;
}
}
}
/* Failure */
DPRINT1("CSRSRV: Bad message buffer %p\n", ApiMessage);
DbgBreakPoint();
return FALSE;
}
/*** This is what we have in consrv/server.c ***
/\* Ensure that a captured buffer is safe to access *\/
BOOL FASTCALL
Win32CsrValidateBuffer(PCSR_PROCESS ProcessData, PVOID Buffer,
SIZE_T NumElements, SIZE_T ElementSize)
{
/\* Check that the following conditions are true:
* 1. The start of the buffer is somewhere within the process's
* shared memory section view.
* 2. The remaining space in the view is at least as large as the buffer.
* (NB: Please don't try to "optimize" this by using multiplication
* instead of division; remember that 2147483648 * 2 = 0.)
* 3. The buffer is DWORD-aligned.
*\/
ULONG_PTR Offset = (BYTE *)Buffer - (BYTE *)ProcessData->ClientViewBase;
if (Offset >= ProcessData->ClientViewBounds
|| NumElements > (ProcessData->ClientViewBounds - Offset) / ElementSize
|| (Offset & (sizeof(DWORD) - 1)) != 0)
{
DPRINT1("Invalid buffer %p(%u*%u); section view is %p(%u)\n",
Buffer, NumElements, ElementSize,
ProcessData->ClientViewBase, ProcessData->ClientViewBounds);
return FALSE;
}
return TRUE;
}
***********************************************/
/*++
* @name CsrValidateMessageString
* @implemented NT5.1
*
* The CsrValidateMessageString validates a captured Wide-Character String
* present in a CSR API Message.
*
* @param ApiMessage
* Pointer to the CSR API Message containing the CSR Capture Buffer.
*
* @param MessageString
* Pointer to the buffer containing the string to validate.
*
* @return TRUE if validation succeeded, FALSE otherwise.
*
* @remarks None.
*
*--*/
BOOLEAN
NTAPI
CsrValidateMessageString(IN PCSR_API_MESSAGE ApiMessage,
IN LPWSTR *MessageString)
{
if (MessageString)
{
return CsrValidateMessageBuffer(ApiMessage,
(PVOID*)MessageString,
wcslen(*MessageString) + 1,
sizeof(WCHAR));
}
else
{
return FALSE;
}
}
/* EOF */