reactos/subsystems/win32/csrsrv/api/wapi.c

1151 lines
37 KiB
C
Raw Normal View History

/*
* subsystems/win32/csrss/csrsrv/api/wapi.c
*
* "\windows\ApiPort" port process management functions
* CSRSS port message processing
*
* ReactOS Operating System
*
*/
/* INCLUDES ******************************************************************/
#include <srv.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
static unsigned ApiDefinitionsCount = 0;
static PCSRSS_API_DEFINITION ApiDefinitions = NULL;
UNICODE_STRING CsrApiPortName;
volatile LONG CsrpStaticThreadCount;
volatile LONG CsrpDynamicThreadTotal;
extern ULONG CsrMaxApiRequestThreads;
/* FUNCTIONS *****************************************************************/
#if 0
NTSTATUS FASTCALL
CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions)
{
unsigned NewCount;
PCSRSS_API_DEFINITION Scan;
PCSRSS_API_DEFINITION New;
DPRINT("CSR: %s called\n", __FUNCTION__);
NewCount = 0;
for (Scan = NewDefinitions; 0 != Scan->Handler; Scan++)
{
NewCount++;
}
New = RtlAllocateHeap(CsrHeap, 0,
(ApiDefinitionsCount + NewCount)
* sizeof(CSRSS_API_DEFINITION));
if (NULL == New)
{
DPRINT1("Unable to allocate memory\n");
return STATUS_NO_MEMORY;
}
if (0 != ApiDefinitionsCount)
{
RtlCopyMemory(New, ApiDefinitions,
ApiDefinitionsCount * sizeof(CSRSS_API_DEFINITION));
RtlFreeHeap(CsrHeap, 0, ApiDefinitions);
}
RtlCopyMemory(New + ApiDefinitionsCount, NewDefinitions,
NewCount * sizeof(CSRSS_API_DEFINITION));
ApiDefinitions = New;
ApiDefinitionsCount += NewCount;
return STATUS_SUCCESS;
}
#endif
/*
VOID
FASTCALL
CsrApiCallHandler(PCSR_PROCESS ProcessData,
PCSR_API_MESSAGE Request)
*/
VOID
FASTCALL
CsrApiCallHandler(
IN OUT PCSR_API_MESSAGE ApiMessage,
OUT PULONG Reply
)
{
unsigned DefIndex;
ULONG ApiId;
DPRINT("CSR: Calling handler for ApiNumber: %x.\n", ApiMessage->ApiNumber);
ApiId = CSR_API_NUMBER_TO_API_ID(ApiMessage->ApiNumber);
DPRINT("CSR: ApiID: %x ServerID: %x\n", ApiId, CSR_API_NUMBER_TO_SERVER_ID(ApiMessage->ApiNumber));
/* FIXME: Extract DefIndex instead of looping */
for (DefIndex = 0; DefIndex < ApiDefinitionsCount; DefIndex++)
{
if (ApiDefinitions[DefIndex].ApiID == ApiId)
{
if (ApiMessage->Header.u1.s1.DataLength < ApiDefinitions[DefIndex].MinRequestSize)
{
DPRINT1("Request ApiID %d min request size %d actual %d\n",
ApiId, ApiDefinitions[DefIndex].MinRequestSize,
ApiMessage->Header.u1.s1.DataLength);
ApiMessage->Status = STATUS_INVALID_PARAMETER;
}
else
{
ApiMessage->Status = (ApiDefinitions[DefIndex].Handler)(ApiMessage, Reply);
}
return;
}
}
DPRINT1("CSR: Unknown request ApiNumber 0x%x\n", ApiMessage->ApiNumber);
ApiMessage->Status = STATUS_INVALID_SYSTEM_SERVICE;
}
VOID
CallHardError(IN PCSR_THREAD ThreadData,
IN PHARDERROR_MSG HardErrorMessage);
static
VOID
NTAPI
CsrHandleHardError(IN PCSR_THREAD ThreadData,
IN OUT PHARDERROR_MSG Message)
{
DPRINT1("CSR: received hard error %lx\n", Message->Status);
/* Call the hard error handler in win32csr */
CallHardError(ThreadData, Message);
}
/*++
* @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(PCSR_API_MESSAGE ReceiveMsg,
PCSR_API_MESSAGE ReplyMsg)
{
#if 1 // Real code
ULONG ServerId;
PCSR_SERVER_DLL ServerDll;
ULONG ApiId;
ULONG Reply;
NTSTATUS Status;
/* Get the Server ID */
ServerId = CSR_SERVER_ID_FROM_OPCODE(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 */
ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg->ApiNumber);
/* Normalize it with our Base ID */
ApiId -= 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 and get the result */
/// CsrApiCallHandler(ReplyMsg, /*ProcessData*/ &ReplyCode); ///
Status = (ServerDll->DispatchTable[ApiId])(ReceiveMsg, &Reply);
/* Return the result, no matter what it is */
ReplyMsg->Status = Status;
}
_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;
#else // Hacky reactos code
PCSR_PROCESS ProcessData;
ULONG ReplyCode;
/* Get the Process Data */
CsrLockProcessByClientId(&ReceiveMsg->Header.ClientId.UniqueProcess, &ProcessData);
if (!ProcessData)
{
DPRINT1("Message: Unable to find data for process 0x%x\n",
ReceiveMsg->Header.ClientId.UniqueProcess);
return STATUS_NOT_SUPPORTED;
}
/* Validation complete, start SEH */
_SEH2_TRY
{
/* Call the API and get the result */
CsrApiCallHandler(ReplyMsg, /*ProcessData*/ &ReplyCode);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* If we got an exception, return access violation */
ReplyMsg->Status = STATUS_ACCESS_VIOLATION;
}
_SEH2_END;
/* Release the process reference */
CsrUnlockProcess(ProcessData);
/* Return success */
return STATUS_SUCCESS;
#endif
}
/*++
* @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*/);
/* Create the Port Object */
Status = NtCreatePort(&CsrApiPort,
&ObjectAttributes,
LPC_MAX_DATA_LENGTH, // hack ; sizeof(CSR_CONNECTION_INFO),
LPC_MAX_MESSAGE_LENGTH, // hack ; 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)ClientConnectionThread,//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;
}
PBASE_STATIC_SERVER_DATA BaseStaticServerData;
NTSTATUS
NTAPI
CreateBaseAcls(OUT PACL* Dacl,
OUT PACL* RestrictedDacl)
{
PSID SystemSid, WorldSid, RestrictedSid;
SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
NTSTATUS Status;
UCHAR KeyValueBuffer[0x40];
PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
UNICODE_STRING KeyName;
ULONG ProtectionMode = 0;
ULONG AclLength, ResultLength;
HANDLE hKey;
OBJECT_ATTRIBUTES ObjectAttributes;
/* Open the Session Manager Key */
RtlInitUnicodeString(&KeyName, SM_REG_KEY);
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
if (NT_SUCCESS(Status))
{
/* Read the key value */
RtlInitUnicodeString(&KeyName, L"ProtectionMode");
Status = NtQueryValueKey(hKey,
&KeyName,
KeyValuePartialInformation,
KeyValueBuffer,
sizeof(KeyValueBuffer),
&ResultLength);
/* Make sure it's what we expect it to be */
KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) &&
(*(PULONG)KeyValuePartialInfo->Data))
{
/* Save the Protection Mode */
ProtectionMode = *(PULONG)KeyValuePartialInfo->Data;
}
/* Close the handle */
NtClose(hKey);
}
/* Allocate the System SID */
Status = RtlAllocateAndInitializeSid(&NtAuthority,
1, SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&SystemSid);
ASSERT(NT_SUCCESS(Status));
/* Allocate the World SID */
Status = RtlAllocateAndInitializeSid(&WorldAuthority,
1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&WorldSid);
ASSERT(NT_SUCCESS(Status));
/* Allocate the restricted SID */
Status = RtlAllocateAndInitializeSid(&NtAuthority,
1, SECURITY_RESTRICTED_CODE_RID,
0, 0, 0, 0, 0, 0, 0,
&RestrictedSid);
ASSERT(NT_SUCCESS(Status));
/* Allocate one ACL with 3 ACEs each for one SID */
AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) +
RtlLengthSid(SystemSid) +
RtlLengthSid(RestrictedSid) +
RtlLengthSid(WorldSid);
*Dacl = RtlAllocateHeap(CsrHeap, 0, AclLength);
ASSERT(*Dacl != NULL);
/* Set the correct header fields */
Status = RtlCreateAcl(*Dacl, AclLength, ACL_REVISION2);
ASSERT(NT_SUCCESS(Status));
/* Give the appropriate rights to each SID */
/* FIXME: Should check SessionId/ProtectionMode */
Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid);
ASSERT(NT_SUCCESS(Status));
/* Now allocate the restricted DACL */
*RestrictedDacl = RtlAllocateHeap(CsrHeap, 0, AclLength);
ASSERT(*RestrictedDacl != NULL);
/* Initialize it */
Status = RtlCreateAcl(*RestrictedDacl, AclLength, ACL_REVISION2);
ASSERT(NT_SUCCESS(Status));
/* And add the same ACEs as before */
/* FIXME: Not really fully correct */
Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid);
ASSERT(NT_SUCCESS(Status));
/* The SIDs are captured, can free them now */
RtlFreeHeap(CsrHeap, 0, SystemSid);
RtlFreeHeap(CsrHeap, 0, WorldSid);
RtlFreeHeap(CsrHeap, 0, RestrictedSid);
return Status;
}
NTSTATUS WINAPI
CsrpHandleConnectionRequest(PPORT_MESSAGE Request)
{
NTSTATUS Status;
HANDLE ServerPort = NULL;//, ServerThread = NULL;
PCSR_PROCESS ProcessData = NULL;
REMOTE_PORT_VIEW RemotePortView;
// CLIENT_ID ClientId;
BOOLEAN AllowConnection = FALSE;
PCSR_CONNECTION_INFO ConnectInfo;
ServerPort = NULL;
- Fix SleepEx. - Put volatile statements in EX_RUNDOWN_REF, IRP, DEVICE_OBJECT, ERESOURCE, FILE_OBJECT, IO_REMOVE_LOCK, WORK_QUEUE_ITEM where required (thanks to Microsoft's changes in the WDK to mark the fields properly). - Update FILE_OBJECT definition. - Add some asserts to some I/O functions. - Add stub support for File Objects created by XP+ Drivers which have File Object Extensions. - Add some fixes to IopDeleteFile, including proper reference counting for the DO and VPB, as well as cleanup when the file is closed without a handle. - Fix a bug in IopSecurityFile. - Queue and unqueue IRPs in all I/O functions. - Fully support IRP cancellation now. - Fix critical bugs in NtDeviceIoControlFile and NtDeviceFsControlFile which were causing double queueing of IRPs and freeing of invalid memory, as well as invalid paramter checking for user-mode buffers. - Add exhaustive validation checks to IoCreateFile, add more failure cases, and validate the EA buffer. Also support IO_ATTACH_DEVICE_API flag. - Implement IoCreateStreamFileObjectEx and IoCreateStreamFileObjectLite and fix several bugs in the original implementation of IoCreateStreamFileObject. - Fix a bug in RtlRaiseException. - Update Io*ShareAccess routines to support XP+ style semantics related to special File Object flags which disable their use. - Add validation to all Query/Set routines so that information clasess, lengths, buffers and alignment are properly checked. - Also add an array for the proper acess rights that each query/set operation requires. - Check backup/restore privileges during I/O File operations. - Check traverse access during I/O File Operations. - Check access privileges to the device during I/O file operations. - Rename IopReferenceDeviceObject and also verify if an exclusive DO is trying to be invalidly opened. - Support various extra security checks during I/O File/Device Parse Routine. - Fix a bug during IopCleanupIrp so that we don't dereference the File OBject if this was a create operation. - Fix some bogus asserts in IofCompleteRequest, and save the IRP Flags before signalling it's event, since the driver might've freed it behind our back. - Fix a large bug in ObInsertObject which affected the insert of unnamed objects with forced security options (Such as process/threads). - Fix the creation of the Process/Thread/Job Obejct Types to that security information is forced. - Remove "Fix PS!!!" messages since the bug is now fixed and these objects now get proper security descriptors. - Fix another bug in ObInsertObjet which wasn't properly validating user-mode objects and always assumed kernel mode. - Silence multiple trace/checkpoint messages that have accumulated throughout time for various debugging purposes. svn path=/trunk/; revision=25118
2006-12-10 18:40:30 +00:00
DPRINT("CSR: %s: Handling: %p\n", __FUNCTION__, Request);
ConnectInfo = (PCSR_CONNECTION_INFO)(Request + 1);
/* Save the process ID */
RtlZeroMemory(ConnectInfo, sizeof(CSR_CONNECTION_INFO));
CsrLockProcessByClientId(Request->ClientId.UniqueProcess, &ProcessData);
if (!ProcessData)
{
DPRINT1("CSRSRV: Unknown process: %lx. Will be rejecting connection\n",
Request->ClientId.UniqueProcess);
}
if ((ProcessData) && (ProcessData != CsrRootProcess))
{
/* Attach the Shared Section */
Status = CsrSrvAttachSharedSection(ProcessData, ConnectInfo);
if (NT_SUCCESS(Status))
{
DPRINT("Connection ok\n");
AllowConnection = TRUE;
}
else
{
DPRINT1("Shared section map failed: %lx\n", Status);
}
}
else if (ProcessData == CsrRootProcess)
{
AllowConnection = TRUE;
}
/* Release the process */
if (ProcessData) CsrUnlockProcess(ProcessData);
/* 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;
Status = NtAcceptConnectPort(&ServerPort,
AllowConnection ? UlongToPtr(ProcessData->SequenceNumber) : 0,
Request,
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",
Request->ClientId.UniqueProcess,
Request->ClientId.UniqueThread,
RemotePortView.ViewBase,
RemotePortView.ViewSize);
}
/* Set some Port Data in the Process */
ProcessData->ClientPort = ServerPort;
ProcessData->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase;
ProcessData->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",
Request->ClientId.UniqueProcess,
Request->ClientId.UniqueThread);
}
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)
{
PTEB Teb = NtCurrentTeb();
PCSR_THREAD CsrThread;
#if 0
NTSTATUS Status;
ANSI_STRING DllName;
UNICODE_STRING TempName;
HANDLE hUser32;
STRING StartupName;
/* 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 teh handle, get the Client Thread Startup Entrypoint */
if (NT_SUCCESS(Status))
{
RtlInitAnsiString(&StartupName,"ClientThreadSetup");
Status = LdrGetProcedureAddress(hUser32,
&StartupName,
0,
(PVOID)&CsrClientThreadSetup);
}
}
/* Connect to user32 */
CsrClientThreadSetup();
#endif
/* Save pointer to this thread in TEB */
CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId);
if (CsrThread) Teb->CsrClientThread = CsrThread;
/* Return it */
return CsrThread;
}
/*++
* @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)ClientConnectionThread,//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;
}
VOID
WINAPI
ClientConnectionThread(IN PVOID Parameter)
{
PTEB Teb = NtCurrentTeb();
LARGE_INTEGER TimeOut;
NTSTATUS Status;
BYTE RawRequest[LPC_MAX_DATA_LENGTH];
PCSR_API_MESSAGE Request = (PCSR_API_MESSAGE)RawRequest;
PCSR_API_MESSAGE Reply;
PCSR_PROCESS CsrProcess;
PCSR_THREAD ServerThread, CsrThread;
ULONG MessageType;
HANDLE ReplyPort;
PDBGKM_MSG DebugMessage;
PHARDERROR_MSG HardErrorMsg;
PCLIENT_DIED_MSG ClientDiedMsg;
DPRINT("CSR: %s called\n", __FUNCTION__);
/* Setup LPC loop port and message */
Reply = 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 */
ServerThread = 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",
Request, Reply);
DbgBreakPoint();
}
/* Send the reply and wait for a new request */
DPRINT("Replying to: %lx (%lx)\n", ReplyPort, CsrApiPort);
Status = NtReplyWaitReceivePort(ReplyPort,
0,
&Reply->Header,
&Request->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 */
Reply = NULL;
ReplyPort = CsrApiPort;
DPRINT1("failed: %lx\n", Status);
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 = Request->Header.ClientId;
/* Get the Message Type */
MessageType = Request->Header.u2.s2.Type;
/* Handle connection requests */
if (MessageType == LPC_CONNECTION_REQUEST)
{
/* Handle the Connection Request */
DPRINT("Accepting new connection\n");
CsrpHandleConnectionRequest((PPORT_MESSAGE)Request);
Reply = 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,
&Request->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)
{
DPRINT1("Exception from unknown thread, just continue\n");
Reply = Request;
ReplyPort = CsrApiPort;
Reply->Status = DBG_CONTINUE;
}
else if (MessageType == LPC_PORT_CLOSED ||
MessageType == LPC_CLIENT_DIED)
{
/* The Client or Port are gone, loop again */
DPRINT("Death from unknown thread, just continue\n");
Reply = NULL;
ReplyPort = CsrApiPort;
}
else if (MessageType == LPC_ERROR_EVENT)
{
/* If it's a hard error, handle this too */
DPRINT1("Hard error from unknown thread, call handlers\n");
HandleHardError:
HardErrorMsg = (PHARDERROR_MSG)Request;
/* Default it to unhandled */
HardErrorMsg->Response = ResponseNotHandled;
/* Check if there are free api threads */
CsrpCheckRequestThreads();
if (CsrpStaticThreadCount)
{
CsrHandleHardError(CsrThread, (PHARDERROR_MSG)Request);
}
/* If the response was 0xFFFFFFFF, we'll ignore it */
if (HardErrorMsg->Response == 0xFFFFFFFF)
{
Reply = NULL;
ReplyPort = CsrApiPort;
}
else
{
if (CsrThread) CsrDereferenceThread(CsrThread);
Reply = Request;
ReplyPort = CsrApiPort;
}
}
else if (MessageType == LPC_REQUEST)
{
/* This is an API Message coming from a non-CSR Thread */
DPRINT1("No thread found for request %lx and clientID %lx.%lx\n",
Request->ApiNumber & 0xFFFF,
Request->Header.ClientId.UniqueProcess,
Request->Header.ClientId.UniqueThread);
Reply = Request;
ReplyPort = CsrApiPort;
Reply->Status = STATUS_ILLEGAL_FUNCTION;
}
else if (MessageType == LPC_DATAGRAM)
{
DPRINT1("Kernel datagram: not yet supported\n");
Reply = NULL;
ReplyPort = CsrApiPort;
}
else
{
/* Some other ignored message type */
Reply = 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)Request;
if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart)
{
/* Reference the thread */
CsrLockedReferenceThread(CsrThread);
/* Destroy the thread in the API Message */
CsrDestroyThread(&Request->Header.ClientId);
/* Check if the thread was actually ourselves */
if (CsrProcess->ThreadCount == 1)
{
/* Kill the process manually here */
DPRINT1("Last thread\n");
CsrDestroyProcess(&CsrThread->ClientId, 0);
}
/* Remove our extra reference */
CsrLockedDereferenceThread(CsrThread);
}
/* Release the lock and keep looping */
CsrReleaseProcessLock();
Reply = NULL;
ReplyPort = CsrApiPort;
continue;
}
/* Reference the thread and release the lock */
CsrLockedReferenceThread(CsrThread);
CsrReleaseProcessLock();
/* If this was an exception, handle it */
if (MessageType == LPC_EXCEPTION)
{
/* Kill the process */
DPRINT1("Exception in %lx.%lx. Killing...\n",
Request->Header.ClientId.UniqueProcess,
Request->Header.ClientId.UniqueThread);
NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED);
/* Destroy it from CSR */
CsrDestroyProcess(&Request->Header.ClientId, STATUS_ABANDONED);
/* Return a Debug Message */
DebugMessage = (PDBGKM_MSG)Request;
DebugMessage->ReturnedStatus = DBG_CONTINUE;
Reply = Request;
ReplyPort = CsrApiPort;
/* Remove our extra reference */
CsrDereferenceThread(CsrThread);
}
else if (MessageType == LPC_ERROR_EVENT)
{
DPRINT1("Hard error from known CSR thread... handling\n");
goto HandleHardError;
}
else
{
/* Something else */
DPRINT1("Unhandled message type: %lx\n", MessageType);
CsrDereferenceThread(CsrThread);
Reply = NULL;
}
/* Keep looping */
continue;
}
/* We got an API Request */
CsrLockedReferenceThread(CsrThread);
CsrReleaseProcessLock();
/* Assume success */
Reply = Request;
Request->Status = STATUS_SUCCESS;
/* Now we reply to a particular client */
ReplyPort = CsrThread->Process->ClientPort;
DPRINT("CSR: Got CSR API: %x [Message Origin: %x]\n",
Request->ApiNumber,
Request->Header.ClientId.UniqueThread);
/* Validation complete, start SEH */
_SEH2_TRY
{
ULONG ReplyCode;
/* Make sure we have enough threads */
CsrpCheckRequestThreads();
/* Set the client thread pointer */
NtCurrentTeb()->CsrClientThread = CsrThread;
/* Call the Handler */
CsrApiCallHandler(Request, &ReplyCode);
/* Increase the static thread count */
_InterlockedIncrement(&CsrpStaticThreadCount);
/* Restore the server thread */
NtCurrentTeb()->CsrClientThread = ServerThread;
/* Check if this is a dead client now */
if (Request->ApiNumber == 0xBABE)
{
/* Reply to the death message */
NtReplyPort(ReplyPort, &Reply->Header);
/* Reply back to the API port now */
ReplyPort = CsrApiPort;
Reply = NULL;
/* Drop the reference */
CsrDereferenceThread(CsrThread);
}
else
{
/* Drop the reference */
CsrDereferenceThread(CsrThread);
}
}
_SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
{
Reply = NULL;
ReplyPort = CsrApiPort;
}
_SEH2_END;
}
/* Close the port and exit the thread */
// NtClose(ServerPort);
DPRINT1("CSR: %s done\n", __FUNCTION__);
/* We're out of the loop for some reason, terminate! */
NtTerminateThread(NtCurrentThread(), Status);
//return Status;
}
/*++
* @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;
ULONG_PTR **PointerOffsets, *CurrentPointer;
/* Get the capture buffers */
RemoteCaptureBuffer = ApiMessage->CsrCaptureData;
LocalCaptureBuffer = RemoteCaptureBuffer->PreviousCaptureBuffer;
/* Free the previous one */
RemoteCaptureBuffer->PreviousCaptureBuffer = NULL;
/* Find out the difference between the two buffers */
BufferDistance = (ULONG_PTR)LocalCaptureBuffer - (ULONG_PTR)RemoteCaptureBuffer;
/* Save the pointer count and offset pointer */
PointerCount = RemoteCaptureBuffer->PointerCount;
PointerOffsets = (ULONG_PTR**)(RemoteCaptureBuffer + 1);
/* Start the loop */
while (PointerCount)
{
/* Get the current pointer */
CurrentPointer = *PointerOffsets++;
if (CurrentPointer)
{
/* Add it to the CSR Message structure */
CurrentPointer += (ULONG_PTR)ApiMessage;
/* Modify the pointer to take into account its new position */
*CurrentPointer += BufferDistance;
}
/* Move to the next Pointer */
PointerCount--;
}
/* Copy the data back */
RtlMoveMemory(LocalCaptureBuffer, RemoteCaptureBuffer, RemoteCaptureBuffer->Size);
/* Free our allocated buffer */
RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer);
}
/* EOF */