[CSRSS/CSRSRV]
[reactos.git] / subsystems / win32 / csrsrv / api / wapi.c
1 /*
2 * subsystems/win32/csrss/csrsrv/api/wapi.c
3 *
4 * CSRSS port message processing
5 *
6 * ReactOS Operating System
7 *
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <srv.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 static unsigned ApiDefinitionsCount = 0;
20 static PCSRSS_API_DEFINITION ApiDefinitions = NULL;
21 UNICODE_STRING CsrApiPortName;
22 volatile LONG CsrpStaticThreadCount;
23 volatile LONG CsrpDynamicThreadTotal;
24 extern ULONG CsrMaxApiRequestThreads;
25
26 /* FUNCTIONS *****************************************************************/
27
28 NTSTATUS FASTCALL
29 CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions)
30 {
31 unsigned NewCount;
32 PCSRSS_API_DEFINITION Scan;
33 PCSRSS_API_DEFINITION New;
34
35 DPRINT("CSR: %s called\n", __FUNCTION__);
36
37 NewCount = 0;
38 for (Scan = NewDefinitions; 0 != Scan->Handler; Scan++)
39 {
40 NewCount++;
41 }
42
43 New = RtlAllocateHeap(CsrHeap, 0,
44 (ApiDefinitionsCount + NewCount)
45 * sizeof(CSRSS_API_DEFINITION));
46 if (NULL == New)
47 {
48 DPRINT1("Unable to allocate memory\n");
49 return STATUS_NO_MEMORY;
50 }
51 if (0 != ApiDefinitionsCount)
52 {
53 RtlCopyMemory(New, ApiDefinitions,
54 ApiDefinitionsCount * sizeof(CSRSS_API_DEFINITION));
55 RtlFreeHeap(CsrHeap, 0, ApiDefinitions);
56 }
57 RtlCopyMemory(New + ApiDefinitionsCount, NewDefinitions,
58 NewCount * sizeof(CSRSS_API_DEFINITION));
59 ApiDefinitions = New;
60 ApiDefinitionsCount += NewCount;
61
62 return STATUS_SUCCESS;
63 }
64
65 /*
66 VOID
67 FASTCALL
68 CsrApiCallHandler(PCSR_PROCESS ProcessData,
69 PCSR_API_MESSAGE Request)
70 */
71 VOID
72 FASTCALL
73 CsrApiCallHandler(
74 IN OUT PCSR_API_MESSAGE ApiMessage,
75 OUT PULONG Reply
76 )
77 {
78 unsigned DefIndex;
79 ULONG ApiId;
80
81 DPRINT("CSR: Calling handler for ApiNumber: %x.\n", ApiMessage->ApiNumber);
82 ApiId = CSR_API_NUMBER_TO_API_ID(ApiMessage->ApiNumber);
83 DPRINT("CSR: ApiID: %x ServerID: %x\n", ApiId, CSR_API_NUMBER_TO_SERVER_ID(ApiMessage->ApiNumber));
84
85 /* FIXME: Extract DefIndex instead of looping */
86 for (DefIndex = 0; DefIndex < ApiDefinitionsCount; DefIndex++)
87 {
88 if (ApiDefinitions[DefIndex].ApiID == ApiId)
89 {
90 if (ApiMessage->Header.u1.s1.DataLength < ApiDefinitions[DefIndex].MinRequestSize)
91 {
92 DPRINT1("Request ApiID %d min request size %d actual %d\n",
93 ApiId, ApiDefinitions[DefIndex].MinRequestSize,
94 ApiMessage->Header.u1.s1.DataLength);
95 ApiMessage->Status = STATUS_INVALID_PARAMETER;
96 }
97 else
98 {
99 ApiMessage->Status = (ApiDefinitions[DefIndex].Handler)(ApiMessage, Reply);
100 }
101 return;
102 }
103 }
104 DPRINT1("CSR: Unknown request ApiNumber 0x%x\n", ApiMessage->ApiNumber);
105 ApiMessage->Status = STATUS_INVALID_SYSTEM_SERVICE;
106 }
107
108 VOID
109 CallHardError(IN PCSR_THREAD ThreadData,
110 IN PHARDERROR_MSG HardErrorMessage);
111
112 static
113 VOID
114 NTAPI
115 CsrHandleHardError(IN PCSR_THREAD ThreadData,
116 IN OUT PHARDERROR_MSG Message)
117 {
118 DPRINT1("CSR: received hard error %lx\n", Message->Status);
119
120 /* Call the hard error handler in win32csr */
121 CallHardError(ThreadData, Message);
122 }
123
124 /*++
125 * @name CsrCallServerFromServer
126 * @implemented NT4
127 *
128 * The CsrCallServerFromServer routine calls a CSR API from within a server.
129 * It avoids using LPC messages since the request isn't coming from a client.
130 *
131 * @param ReceiveMsg
132 * Pointer to the CSR API Message to send to the server.
133 *
134 * @param ReplyMsg
135 * Pointer to the CSR API Message to receive from the server.
136 *
137 * @return STATUS_SUCCESS in case of success, STATUS_ILLEGAL_FUNCTION
138 * if the ApiNumber is invalid, or STATUS_ACCESS_VIOLATION if there
139 * was a problem executing the API.
140 *
141 * @remarks None.
142 *
143 *--*/
144 NTSTATUS
145 NTAPI
146 CsrCallServerFromServer(PCSR_API_MESSAGE ReceiveMsg,
147 PCSR_API_MESSAGE ReplyMsg)
148 {
149 #if 0 // real code
150 ULONG ServerId;
151 PCSR_SERVER_DLL ServerDll;
152 ULONG ApiId;
153 ULONG Reply;
154 NTSTATUS Status;
155
156 /* Get the Server ID */
157 ServerId = CSR_SERVER_ID_FROM_OPCODE(ReceiveMsg->ApiNumber);
158
159 /* Make sure that the ID is within limits, and the Server DLL loaded */
160 if ((ServerId >= CSR_SERVER_DLL_MAX) ||
161 (!(ServerDll = CsrLoadedServerDll[ServerId])))
162 {
163 /* We are beyond the Maximum Server ID */
164 DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", ServerId, ServerDll);
165 ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION;
166 return STATUS_ILLEGAL_FUNCTION;
167 }
168 else
169 {
170 /* Get the API ID */
171 ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg->ApiNumber);
172
173 /* Normalize it with our Base ID */
174 ApiId -= ServerDll->ApiBase;
175
176 /* Make sure that the ID is within limits, and the entry exists */
177 if ((ApiId >= ServerDll->HighestApiSupported) ||
178 ((ServerDll->ValidTable) && !(ServerDll->ValidTable[ApiId])))
179 {
180 /* We are beyond the Maximum API ID, or it doesn't exist */
181 DPRINT1("CSRSS: %lx (%s) is invalid ApiTableIndex for %Z or is an "
182 "invalid API to call from the server.\n",
183 ServerDll->ValidTable[ApiId],
184 ((ServerDll->NameTable) && (ServerDll->NameTable[ApiId])) ?
185 ServerDll->NameTable[ApiId] : "*** UNKNOWN ***", &ServerDll->Name);
186 DbgBreakPoint();
187 ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION;
188 return STATUS_ILLEGAL_FUNCTION;
189 }
190 }
191
192 if (CsrDebug & 2)
193 {
194 DPRINT1("CSRSS: %s Api Request received from server process\n",
195 ServerDll->NameTable[ApiId]);
196 }
197
198 /* Validation complete, start SEH */
199 _SEH2_TRY
200 {
201 /* Call the API and get the result */
202 Status = (ServerDll->DispatchTable[ApiId])(ReceiveMsg, &Reply);
203
204 /* Return the result, no matter what it is */
205 ReplyMsg->Status = Status;
206 }
207 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
208 {
209 /* If we got an exception, return access violation */
210 ReplyMsg->Status = STATUS_ACCESS_VIOLATION;
211 }
212 _SEH2_END;
213
214 /* Return success */
215 return STATUS_SUCCESS;
216
217 #else // Hacky reactos code
218
219 PCSR_PROCESS ProcessData;
220 ULONG ReplyCode;
221
222 /* Get the Process Data */
223 CsrLockProcessByClientId(&ReceiveMsg->Header.ClientId.UniqueProcess, &ProcessData);
224 if (!ProcessData)
225 {
226 DPRINT1("Message: Unable to find data for process 0x%x\n",
227 ReceiveMsg->Header.ClientId.UniqueProcess);
228 return STATUS_NOT_SUPPORTED;
229 }
230
231 /* Validation complete, start SEH */
232 _SEH2_TRY
233 {
234 /* Call the API and get the result */
235 CsrApiCallHandler(ReplyMsg, /*ProcessData*/ &ReplyCode);
236 }
237 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
238 {
239 /* If we got an exception, return access violation */
240 ReplyMsg->Status = STATUS_ACCESS_VIOLATION;
241 }
242 _SEH2_END;
243
244 /* Release the process reference */
245 CsrUnlockProcess(ProcessData);
246
247 /* Return success */
248 return STATUS_SUCCESS;
249 #endif
250 }
251
252 /*++
253 * @name CsrApiPortInitialize
254 *
255 * The CsrApiPortInitialize routine initializes the LPC Port used for
256 * communications with the Client/Server Runtime (CSR) and initializes the
257 * static thread that will handle connection requests and APIs.
258 *
259 * @param None
260 *
261 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
262 * otherwise.
263 *
264 * @remarks None.
265 *
266 *--*/
267 NTSTATUS
268 NTAPI
269 CsrApiPortInitialize(VOID)
270 {
271 ULONG Size;
272 OBJECT_ATTRIBUTES ObjectAttributes;
273 NTSTATUS Status;
274 HANDLE hRequestEvent, hThread;
275 CLIENT_ID ClientId;
276 PLIST_ENTRY ListHead, NextEntry;
277 PCSR_THREAD ServerThread;
278
279 /* Calculate how much space we'll need for the Port Name */
280 Size = CsrDirectoryName.Length + sizeof(CSR_PORT_NAME) + sizeof(WCHAR);
281
282 /* Create the buffer for it */
283 CsrApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size);
284 if (!CsrApiPortName.Buffer) return STATUS_NO_MEMORY;
285
286 /* Setup the rest of the empty string */
287 CsrApiPortName.Length = 0;
288 CsrApiPortName.MaximumLength = (USHORT)Size;
289 RtlAppendUnicodeStringToString(&CsrApiPortName, &CsrDirectoryName);
290 RtlAppendUnicodeToString(&CsrApiPortName, UNICODE_PATH_SEP);
291 RtlAppendUnicodeToString(&CsrApiPortName, CSR_PORT_NAME);
292 if (CsrDebug & 1)
293 {
294 DPRINT1("CSRSS: Creating %wZ port and associated threads\n", &CsrApiPortName);
295 DPRINT1("CSRSS: sizeof( CONNECTINFO ) == %ld sizeof( API_MSG ) == %ld\n",
296 sizeof(CSR_CONNECTION_INFO), sizeof(CSR_API_MESSAGE));
297 }
298
299 /* FIXME: Create a Security Descriptor */
300
301 /* Initialize the Attributes */
302 InitializeObjectAttributes(&ObjectAttributes,
303 &CsrApiPortName,
304 0,
305 NULL,
306 NULL /* FIXME*/);
307
308 /* Create the Port Object */
309 Status = NtCreatePort(&CsrApiPort,
310 &ObjectAttributes,
311 LPC_MAX_DATA_LENGTH, // hack ; sizeof(CSR_CONNECTION_INFO),
312 LPC_MAX_MESSAGE_LENGTH, // hack ; sizeof(CSR_API_MESSAGE),
313 16 * PAGE_SIZE);
314 if (NT_SUCCESS(Status))
315 {
316 /* Create the event the Port Thread will use */
317 Status = NtCreateEvent(&hRequestEvent,
318 EVENT_ALL_ACCESS,
319 NULL,
320 SynchronizationEvent,
321 FALSE);
322 if (NT_SUCCESS(Status))
323 {
324 /* Create the Request Thread */
325 Status = RtlCreateUserThread(NtCurrentProcess(),
326 NULL,
327 TRUE,
328 0,
329 0,
330 0,
331 (PVOID)ClientConnectionThread,//CsrApiRequestThread,
332 (PVOID)hRequestEvent,
333 &hThread,
334 &ClientId);
335 if (NT_SUCCESS(Status))
336 {
337 /* Add this as a static thread to CSRSRV */
338 CsrAddStaticServerThread(hThread, &ClientId, CsrThreadIsServerThread);
339
340 /* Get the Thread List Pointers */
341 ListHead = &CsrRootProcess->ThreadList;
342 NextEntry = ListHead->Flink;
343
344 /* Start looping the list */
345 while (NextEntry != ListHead)
346 {
347 /* Get the Thread */
348 ServerThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
349
350 /* Start it up */
351 Status = NtResumeThread(ServerThread->ThreadHandle, NULL);
352
353 /* Is this a Server Thread? */
354 if (ServerThread->Flags & CsrThreadIsServerThread)
355 {
356 /* If so, then wait for it to initialize */
357 Status = NtWaitForSingleObject(hRequestEvent, FALSE, NULL);
358 ASSERT(NT_SUCCESS(Status));
359 }
360
361 /* Next thread */
362 NextEntry = NextEntry->Flink;
363 }
364
365 /* We don't need this anymore */
366 NtClose(hRequestEvent);
367 }
368 }
369 }
370
371 /* Return */
372 return Status;
373 }
374
375 PBASE_STATIC_SERVER_DATA BaseStaticServerData;
376
377 NTSTATUS
378 NTAPI
379 CreateBaseAcls(OUT PACL* Dacl,
380 OUT PACL* RestrictedDacl)
381 {
382 PSID SystemSid, WorldSid, RestrictedSid;
383 SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
384 SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY};
385 NTSTATUS Status;
386 UCHAR KeyValueBuffer[0x40];
387 PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
388 UNICODE_STRING KeyName;
389 ULONG ProtectionMode = 0;
390 ULONG AclLength, ResultLength;
391 HANDLE hKey;
392 OBJECT_ATTRIBUTES ObjectAttributes;
393
394 /* Open the Session Manager Key */
395 RtlInitUnicodeString(&KeyName, SM_REG_KEY);
396 InitializeObjectAttributes(&ObjectAttributes,
397 &KeyName,
398 OBJ_CASE_INSENSITIVE,
399 NULL,
400 NULL);
401 Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
402 if (NT_SUCCESS(Status))
403 {
404 /* Read the key value */
405 RtlInitUnicodeString(&KeyName, L"ProtectionMode");
406 Status = NtQueryValueKey(hKey,
407 &KeyName,
408 KeyValuePartialInformation,
409 KeyValueBuffer,
410 sizeof(KeyValueBuffer),
411 &ResultLength);
412
413 /* Make sure it's what we expect it to be */
414 KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
415 if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) &&
416 (*(PULONG)KeyValuePartialInfo->Data))
417 {
418 /* Save the Protection Mode */
419 ProtectionMode = *(PULONG)KeyValuePartialInfo->Data;
420 }
421
422 /* Close the handle */
423 NtClose(hKey);
424 }
425
426 /* Allocate the System SID */
427 Status = RtlAllocateAndInitializeSid(&NtAuthority,
428 1, SECURITY_LOCAL_SYSTEM_RID,
429 0, 0, 0, 0, 0, 0, 0,
430 &SystemSid);
431 ASSERT(NT_SUCCESS(Status));
432
433 /* Allocate the World SID */
434 Status = RtlAllocateAndInitializeSid(&WorldAuthority,
435 1, SECURITY_WORLD_RID,
436 0, 0, 0, 0, 0, 0, 0,
437 &WorldSid);
438 ASSERT(NT_SUCCESS(Status));
439
440 /* Allocate the restricted SID */
441 Status = RtlAllocateAndInitializeSid(&NtAuthority,
442 1, SECURITY_RESTRICTED_CODE_RID,
443 0, 0, 0, 0, 0, 0, 0,
444 &RestrictedSid);
445 ASSERT(NT_SUCCESS(Status));
446
447 /* Allocate one ACL with 3 ACEs each for one SID */
448 AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) +
449 RtlLengthSid(SystemSid) +
450 RtlLengthSid(RestrictedSid) +
451 RtlLengthSid(WorldSid);
452 *Dacl = RtlAllocateHeap(CsrHeap, 0, AclLength);
453 ASSERT(*Dacl != NULL);
454
455 /* Set the correct header fields */
456 Status = RtlCreateAcl(*Dacl, AclLength, ACL_REVISION2);
457 ASSERT(NT_SUCCESS(Status));
458
459 /* Give the appropriate rights to each SID */
460 /* FIXME: Should check SessionId/ProtectionMode */
461 Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid);
462 ASSERT(NT_SUCCESS(Status));
463 Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid);
464 ASSERT(NT_SUCCESS(Status));
465 Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid);
466 ASSERT(NT_SUCCESS(Status));
467
468 /* Now allocate the restricted DACL */
469 *RestrictedDacl = RtlAllocateHeap(CsrHeap, 0, AclLength);
470 ASSERT(*RestrictedDacl != NULL);
471
472 /* Initialize it */
473 Status = RtlCreateAcl(*RestrictedDacl, AclLength, ACL_REVISION2);
474 ASSERT(NT_SUCCESS(Status));
475
476 /* And add the same ACEs as before */
477 /* FIXME: Not really fully correct */
478 Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid);
479 ASSERT(NT_SUCCESS(Status));
480 Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid);
481 ASSERT(NT_SUCCESS(Status));
482 Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid);
483 ASSERT(NT_SUCCESS(Status));
484
485 /* The SIDs are captured, can free them now */
486 RtlFreeHeap(CsrHeap, 0, SystemSid);
487 RtlFreeHeap(CsrHeap, 0, WorldSid);
488 RtlFreeHeap(CsrHeap, 0, RestrictedSid);
489 return Status;
490 }
491
492 NTSTATUS WINAPI
493 CsrpHandleConnectionRequest(PPORT_MESSAGE Request)
494 {
495 NTSTATUS Status;
496 HANDLE ServerPort = NULL;//, ServerThread = NULL;
497 PCSR_PROCESS ProcessData = NULL;
498 REMOTE_PORT_VIEW RemotePortView;
499 // CLIENT_ID ClientId;
500 BOOLEAN AllowConnection = FALSE;
501 PCSR_CONNECTION_INFO ConnectInfo;
502 ServerPort = NULL;
503
504 DPRINT("CSR: %s: Handling: %p\n", __FUNCTION__, Request);
505
506 ConnectInfo = (PCSR_CONNECTION_INFO)(Request + 1);
507
508 /* Save the process ID */
509 RtlZeroMemory(ConnectInfo, sizeof(CSR_CONNECTION_INFO));
510
511 CsrLockProcessByClientId(Request->ClientId.UniqueProcess, &ProcessData);
512 if (!ProcessData)
513 {
514 DPRINT1("CSRSRV: Unknown process: %lx. Will be rejecting connection\n",
515 Request->ClientId.UniqueProcess);
516 }
517
518 if ((ProcessData) && (ProcessData != CsrRootProcess))
519 {
520 /* Attach the Shared Section */
521 Status = CsrSrvAttachSharedSection(ProcessData, ConnectInfo);
522 if (NT_SUCCESS(Status))
523 {
524 DPRINT("Connection ok\n");
525 AllowConnection = TRUE;
526 }
527 else
528 {
529 DPRINT1("Shared section map failed: %lx\n", Status);
530 }
531 }
532 else if (ProcessData == CsrRootProcess)
533 {
534 AllowConnection = TRUE;
535 }
536
537 /* Release the process */
538 if (ProcessData) CsrUnlockProcess(ProcessData);
539
540 /* Setup the Port View Structure */
541 RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
542 RemotePortView.ViewSize = 0;
543 RemotePortView.ViewBase = NULL;
544
545 /* Save the Process ID */
546 ConnectInfo->ProcessId = NtCurrentTeb()->ClientId.UniqueProcess;
547
548 Status = NtAcceptConnectPort(&ServerPort,
549 AllowConnection ? UlongToPtr(ProcessData->SequenceNumber) : 0,
550 Request,
551 AllowConnection,
552 NULL,
553 &RemotePortView);
554 if (!NT_SUCCESS(Status))
555 {
556 DPRINT1("CSRSS: NtAcceptConnectPort - failed. Status == %X\n", Status);
557 }
558 else if (AllowConnection)
559 {
560 if (CsrDebug & 2)
561 {
562 DPRINT1("CSRSS: ClientId: %lx.%lx has ClientView: Base=%p, Size=%lx\n",
563 Request->ClientId.UniqueProcess,
564 Request->ClientId.UniqueThread,
565 RemotePortView.ViewBase,
566 RemotePortView.ViewSize);
567 }
568
569 /* Set some Port Data in the Process */
570 ProcessData->ClientPort = ServerPort;
571 ProcessData->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase;
572 ProcessData->ClientViewBounds = (ULONG_PTR)((ULONG_PTR)RemotePortView.ViewBase +
573 (ULONG_PTR)RemotePortView.ViewSize);
574
575 /* Complete the connection */
576 Status = NtCompleteConnectPort(ServerPort);
577 if (!NT_SUCCESS(Status))
578 {
579 DPRINT1("CSRSS: NtCompleteConnectPort - failed. Status == %X\n", Status);
580 }
581 }
582 else
583 {
584 DPRINT1("CSRSS: Rejecting Connection Request from ClientId: %lx.%lx\n",
585 Request->ClientId.UniqueProcess,
586 Request->ClientId.UniqueThread);
587 }
588
589 return Status;
590 }
591
592 /*++
593 * @name CsrConnectToUser
594 * @implemented NT4
595 *
596 * The CsrConnectToUser connects to the User subsystem.
597 *
598 * @param None
599 *
600 * @return A pointer to the CSR Thread
601 *
602 * @remarks None.
603 *
604 *--*/
605 PCSR_THREAD
606 NTAPI
607 CsrConnectToUser(VOID)
608 {
609 PTEB Teb = NtCurrentTeb();
610 PCSR_THREAD CsrThread;
611 #if 0
612 NTSTATUS Status;
613 ANSI_STRING DllName;
614 UNICODE_STRING TempName;
615 HANDLE hUser32;
616 STRING StartupName;
617
618 /* Check if we didn't already find it */
619 if (!CsrClientThreadSetup)
620 {
621 /* Get the DLL Handle for user32.dll */
622 RtlInitAnsiString(&DllName, "user32");
623 RtlAnsiStringToUnicodeString(&TempName, &DllName, TRUE);
624 Status = LdrGetDllHandle(NULL,
625 NULL,
626 &TempName,
627 &hUser32);
628 RtlFreeUnicodeString(&TempName);
629
630 /* If we got teh handle, get the Client Thread Startup Entrypoint */
631 if (NT_SUCCESS(Status))
632 {
633 RtlInitAnsiString(&StartupName,"ClientThreadSetup");
634 Status = LdrGetProcedureAddress(hUser32,
635 &StartupName,
636 0,
637 (PVOID)&CsrClientThreadSetup);
638 }
639 }
640
641 /* Connect to user32 */
642 CsrClientThreadSetup();
643 #endif
644 /* Save pointer to this thread in TEB */
645 CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId);
646 if (CsrThread) Teb->CsrClientThread = CsrThread;
647
648 /* Return it */
649 return CsrThread;
650 }
651
652 /*++
653 * @name CsrpCheckRequestThreads
654 *
655 * The CsrpCheckRequestThreads routine checks if there are no more threads
656 * to handle CSR API Requests, and creates a new thread if possible, to
657 * avoid starvation.
658 *
659 * @param None.
660 *
661 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
662 * if a new thread couldn't be created.
663 *
664 * @remarks None.
665 *
666 *--*/
667 NTSTATUS
668 NTAPI
669 CsrpCheckRequestThreads(VOID)
670 {
671 HANDLE hThread;
672 CLIENT_ID ClientId;
673 NTSTATUS Status;
674
675 /* Decrease the count, and see if we're out */
676 if (!(_InterlockedDecrement(&CsrpStaticThreadCount)))
677 {
678 /* Check if we've still got space for a Dynamic Thread */
679 if (CsrpDynamicThreadTotal < CsrMaxApiRequestThreads)
680 {
681 /* Create a new dynamic thread */
682 Status = RtlCreateUserThread(NtCurrentProcess(),
683 NULL,
684 TRUE,
685 0,
686 0,
687 0,
688 (PVOID)ClientConnectionThread,//CsrApiRequestThread,
689 NULL,
690 &hThread,
691 &ClientId);
692 /* Check success */
693 if (NT_SUCCESS(Status))
694 {
695 /* Increase the thread counts */
696 _InterlockedIncrement(&CsrpStaticThreadCount);
697 _InterlockedIncrement(&CsrpDynamicThreadTotal);
698
699 /* Add a new server thread */
700 if (CsrAddStaticServerThread(hThread,
701 &ClientId,
702 CsrThreadIsServerThread))
703 {
704 /* Activate it */
705 NtResumeThread(hThread, NULL);
706 }
707 else
708 {
709 /* Failed to create a new static thread */
710 _InterlockedDecrement(&CsrpStaticThreadCount);
711 _InterlockedDecrement(&CsrpDynamicThreadTotal);
712
713 /* Terminate it */
714 DPRINT1("Failing\n");
715 NtTerminateThread(hThread, 0);
716 NtClose(hThread);
717
718 /* Return */
719 return STATUS_UNSUCCESSFUL;
720 }
721 }
722 }
723 }
724
725 /* Success */
726 return STATUS_SUCCESS;
727 }
728
729 VOID
730 WINAPI
731 ClientConnectionThread(IN PVOID Parameter)
732 {
733 PTEB Teb = NtCurrentTeb();
734 LARGE_INTEGER TimeOut;
735 NTSTATUS Status;
736 BYTE RawRequest[LPC_MAX_DATA_LENGTH];
737 PCSR_API_MESSAGE Request = (PCSR_API_MESSAGE)RawRequest;
738 PCSR_API_MESSAGE Reply;
739 PCSR_PROCESS CsrProcess;
740 PCSR_THREAD ServerThread, CsrThread;
741 ULONG MessageType;
742 HANDLE ReplyPort;
743 PDBGKM_MSG DebugMessage;
744 PHARDERROR_MSG HardErrorMsg;
745 PCLIENT_DIED_MSG ClientDiedMsg;
746 DPRINT("CSR: %s called\n", __FUNCTION__);
747
748 /* Setup LPC loop port and message */
749 Reply = NULL;
750 ReplyPort = CsrApiPort;
751
752 /* Connect to user32 */
753 while (!CsrConnectToUser())
754 {
755 /* Set up the timeout for the connect (30 seconds) */
756 TimeOut.QuadPart = -30 * 1000 * 1000 * 10;
757
758 /* Keep trying until we get a response */
759 Teb->Win32ClientInfo[0] = 0;
760 NtDelayExecution(FALSE, &TimeOut);
761 }
762
763 /* Get our thread */
764 ServerThread = Teb->CsrClientThread;
765
766 /* If we got an event... */
767 if (Parameter)
768 {
769 /* Set it, to let stuff waiting on us load */
770 Status = NtSetEvent((HANDLE)Parameter, NULL);
771 ASSERT(NT_SUCCESS(Status));
772
773 /* Increase the Thread Counts */
774 _InterlockedIncrement(&CsrpStaticThreadCount);
775 _InterlockedIncrement(&CsrpDynamicThreadTotal);
776 }
777
778 /* Now start the loop */
779 while (TRUE)
780 {
781 /* Make sure the real CID is set */
782 Teb->RealClientId = Teb->ClientId;
783
784 /* Debug check */
785 if (Teb->CountOfOwnedCriticalSections)
786 {
787 DPRINT1("CSRSRV: FATAL ERROR. CsrThread is Idle while holding %lu critical sections\n",
788 Teb->CountOfOwnedCriticalSections);
789 DPRINT1("CSRSRV: Last Receive Message %lx ReplyMessage %lx\n",
790 Request, Reply);
791 DbgBreakPoint();
792 }
793
794 /* Send the reply and wait for a new request */
795 DPRINT("Replying to: %lx (%lx)\n", ReplyPort, CsrApiPort);
796 Status = NtReplyWaitReceivePort(ReplyPort,
797 0,
798 &Reply->Header,
799 &Request->Header);
800 /* Check if we didn't get success */
801 if (Status != STATUS_SUCCESS)
802 {
803 /* Was it a failure or another success code? */
804 if (!NT_SUCCESS(Status))
805 {
806 /* Check for specific status cases */
807 if ((Status != STATUS_INVALID_CID) &&
808 (Status != STATUS_UNSUCCESSFUL) &&
809 ((Status == STATUS_INVALID_HANDLE) || (ReplyPort == CsrApiPort)))
810 {
811 /* Notify the debugger */
812 DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
813 DPRINT1("CSRSS: ReplyPortHandle %lx CsrApiPort %lx\n", ReplyPort, CsrApiPort);
814 }
815
816 /* We failed big time, so start out fresh */
817 Reply = NULL;
818 ReplyPort = CsrApiPort;
819 DPRINT1("failed: %lx\n", Status);
820 continue;
821 }
822 else
823 {
824 /* A bizare "success" code, just try again */
825 DPRINT1("NtReplyWaitReceivePort returned \"success\" status 0x%x\n", Status);
826 continue;
827 }
828 }
829
830 /* Use whatever Client ID we got */
831 Teb->RealClientId = Request->Header.ClientId;
832
833 /* Get the Message Type */
834 MessageType = Request->Header.u2.s2.Type;
835
836 /* Handle connection requests */
837 if (MessageType == LPC_CONNECTION_REQUEST)
838 {
839 /* Handle the Connection Request */
840 DPRINT("Accepting new connection\n");
841 CsrpHandleConnectionRequest((PPORT_MESSAGE)Request);
842 Reply = NULL;
843 ReplyPort = CsrApiPort;
844 continue;
845 }
846
847 /* It's some other kind of request. Get the lock for the lookup */
848 CsrAcquireProcessLock();
849
850 /* Now do the lookup to get the CSR_THREAD */
851 CsrThread = CsrLocateThreadByClientId(&CsrProcess,
852 &Request->Header.ClientId);
853
854 /* Did we find a thread? */
855 if (!CsrThread)
856 {
857 /* This wasn't a CSR Thread, release lock */
858 CsrReleaseProcessLock();
859
860 /* If this was an exception, handle it */
861 if (MessageType == LPC_EXCEPTION)
862 {
863 DPRINT1("Exception from unknown thread, just continue\n");
864 Reply = Request;
865 ReplyPort = CsrApiPort;
866 Reply->Status = DBG_CONTINUE;
867 }
868 else if (MessageType == LPC_PORT_CLOSED ||
869 MessageType == LPC_CLIENT_DIED)
870 {
871 /* The Client or Port are gone, loop again */
872 DPRINT("Death from unknown thread, just continue\n");
873 Reply = NULL;
874 ReplyPort = CsrApiPort;
875 }
876 else if (MessageType == LPC_ERROR_EVENT)
877 {
878 /* If it's a hard error, handle this too */
879 DPRINT1("Hard error from unknown thread, call handlers\n");
880 HandleHardError:
881 HardErrorMsg = (PHARDERROR_MSG)Request;
882
883 /* Default it to unhandled */
884 HardErrorMsg->Response = ResponseNotHandled;
885
886 /* Check if there are free api threads */
887 CsrpCheckRequestThreads();
888 if (CsrpStaticThreadCount)
889 {
890 CsrHandleHardError(CsrThread, (PHARDERROR_MSG)Request);
891 }
892
893 /* If the response was 0xFFFFFFFF, we'll ignore it */
894 if (HardErrorMsg->Response == 0xFFFFFFFF)
895 {
896 Reply = NULL;
897 ReplyPort = CsrApiPort;
898 }
899 else
900 {
901 if (CsrThread) CsrDereferenceThread(CsrThread);
902 Reply = Request;
903 ReplyPort = CsrApiPort;
904 }
905 }
906 else if (MessageType == LPC_REQUEST)
907 {
908 /* This is an API Message coming from a non-CSR Thread */
909 DPRINT1("No thread found for request %lx and clientID %lx.%lx\n",
910 Request->ApiNumber & 0xFFFF,
911 Request->Header.ClientId.UniqueProcess,
912 Request->Header.ClientId.UniqueThread);
913 Reply = Request;
914 ReplyPort = CsrApiPort;
915 Reply->Status = STATUS_ILLEGAL_FUNCTION;
916 }
917 else if (MessageType == LPC_DATAGRAM)
918 {
919 DPRINT1("Kernel datagram: not yet supported\n");
920 Reply = NULL;
921 ReplyPort = CsrApiPort;
922 }
923 else
924 {
925 /* Some other ignored message type */
926 Reply = NULL;
927 ReplyPort = CsrApiPort;
928 }
929
930 /* Keep going */
931 continue;
932 }
933
934 /* We have a valid thread, was this an LPC Request? */
935 if (MessageType != LPC_REQUEST)
936 {
937 /* It's not an API, check if the client died */
938 if (MessageType == LPC_CLIENT_DIED)
939 {
940 /* Get the information and check if it matches our thread */
941 ClientDiedMsg = (PCLIENT_DIED_MSG)Request;
942 if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart)
943 {
944 /* Reference the thread */
945 CsrLockedReferenceThread(CsrThread);
946
947 /* Destroy the thread in the API Message */
948 CsrDestroyThread(&Request->Header.ClientId);
949
950 /* Check if the thread was actually ourselves */
951 if (CsrProcess->ThreadCount == 1)
952 {
953 /* Kill the process manually here */
954 DPRINT1("Last thread\n");
955 CsrDestroyProcess(&CsrThread->ClientId, 0);
956 }
957
958 /* Remove our extra reference */
959 CsrLockedDereferenceThread(CsrThread);
960 }
961
962 /* Release the lock and keep looping */
963 CsrReleaseProcessLock();
964 Reply = NULL;
965 ReplyPort = CsrApiPort;
966 continue;
967 }
968
969 /* Reference the thread and release the lock */
970 CsrLockedReferenceThread(CsrThread);
971 CsrReleaseProcessLock();
972
973 /* If this was an exception, handle it */
974 if (MessageType == LPC_EXCEPTION)
975 {
976 /* Kill the process */
977 DPRINT1("Exception in %lx.%lx. Killing...\n",
978 Request->Header.ClientId.UniqueProcess,
979 Request->Header.ClientId.UniqueThread);
980 NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED);
981
982 /* Destroy it from CSR */
983 CsrDestroyProcess(&Request->Header.ClientId, STATUS_ABANDONED);
984
985 /* Return a Debug Message */
986 DebugMessage = (PDBGKM_MSG)Request;
987 DebugMessage->ReturnedStatus = DBG_CONTINUE;
988 Reply = Request;
989 ReplyPort = CsrApiPort;
990
991 /* Remove our extra reference */
992 CsrDereferenceThread(CsrThread);
993 }
994 else if (MessageType == LPC_ERROR_EVENT)
995 {
996 DPRINT1("Hard error from known CSR thread... handling\n");
997 goto HandleHardError;
998 }
999 else
1000 {
1001 /* Something else */
1002 DPRINT1("Unhandled message type: %lx\n", MessageType);
1003 CsrDereferenceThread(CsrThread);
1004 Reply = NULL;
1005 }
1006
1007 /* Keep looping */
1008 continue;
1009 }
1010
1011 /* We got an API Request */
1012 CsrLockedReferenceThread(CsrThread);
1013 CsrReleaseProcessLock();
1014
1015 /* Assume success */
1016 Reply = Request;
1017 Request->Status = STATUS_SUCCESS;
1018
1019 /* Now we reply to a particular client */
1020 ReplyPort = CsrThread->Process->ClientPort;
1021
1022 DPRINT("CSR: Got CSR API: %x [Message Origin: %x]\n",
1023 Request->ApiNumber,
1024 Request->Header.ClientId.UniqueThread);
1025
1026 /* Validation complete, start SEH */
1027 _SEH2_TRY
1028 {
1029 ULONG ReplyCode;
1030
1031 /* Make sure we have enough threads */
1032 CsrpCheckRequestThreads();
1033
1034 /* Set the client thread pointer */
1035 NtCurrentTeb()->CsrClientThread = CsrThread;
1036
1037 /* Call the Handler */
1038 CsrApiCallHandler(Request, &ReplyCode);
1039
1040 /* Increase the static thread count */
1041 _InterlockedIncrement(&CsrpStaticThreadCount);
1042
1043 /* Restore the server thread */
1044 NtCurrentTeb()->CsrClientThread = ServerThread;
1045
1046 /* Check if this is a dead client now */
1047 if (Request->ApiNumber == 0xBABE)
1048 {
1049 /* Reply to the death message */
1050 NtReplyPort(ReplyPort, &Reply->Header);
1051
1052 /* Reply back to the API port now */
1053 ReplyPort = CsrApiPort;
1054 Reply = NULL;
1055
1056 /* Drop the reference */
1057 CsrDereferenceThread(CsrThread);
1058 }
1059 else
1060 {
1061 /* Drop the reference */
1062 CsrDereferenceThread(CsrThread);
1063 }
1064 }
1065 _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
1066 {
1067 Reply = NULL;
1068 ReplyPort = CsrApiPort;
1069 }
1070 _SEH2_END;
1071 }
1072
1073 /* Close the port and exit the thread */
1074 // NtClose(ServerPort);
1075
1076 DPRINT1("CSR: %s done\n", __FUNCTION__);
1077 /* We're out of the loop for some reason, terminate! */
1078 NtTerminateThread(NtCurrentThread(), Status);
1079 //return Status;
1080 }
1081
1082 /*++
1083 * @name CsrReleaseCapturedArguments
1084 * @implemented NT5.1
1085 *
1086 * The CsrReleaseCapturedArguments routine releases a Capture Buffer
1087 * that was previously captured with CsrCaptureArguments.
1088 *
1089 * @param ApiMessage
1090 * Pointer to the CSR API Message containing the Capture Buffer
1091 * that needs to be released.
1092 *
1093 * @return None.
1094 *
1095 * @remarks None.
1096 *
1097 *--*/
1098 VOID
1099 NTAPI
1100 CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage)
1101 {
1102 PCSR_CAPTURE_BUFFER RemoteCaptureBuffer, LocalCaptureBuffer;
1103 SIZE_T BufferDistance;
1104 ULONG PointerCount;
1105 ULONG_PTR **PointerOffsets, *CurrentPointer;
1106
1107 /* Get the capture buffers */
1108 RemoteCaptureBuffer = ApiMessage->CsrCaptureData;
1109 LocalCaptureBuffer = RemoteCaptureBuffer->PreviousCaptureBuffer;
1110
1111 /* Free the previous one */
1112 RemoteCaptureBuffer->PreviousCaptureBuffer = NULL;
1113
1114 /* Find out the difference between the two buffers */
1115 BufferDistance = (ULONG_PTR)LocalCaptureBuffer - (ULONG_PTR)RemoteCaptureBuffer;
1116
1117 /* Save the pointer count and offset pointer */
1118 PointerCount = RemoteCaptureBuffer->PointerCount;
1119 PointerOffsets = (ULONG_PTR**)(RemoteCaptureBuffer + 1);
1120
1121 /* Start the loop */
1122 while (PointerCount)
1123 {
1124 /* Get the current pointer */
1125 CurrentPointer = *PointerOffsets++;
1126 if (CurrentPointer)
1127 {
1128 /* Add it to the CSR Message structure */
1129 CurrentPointer += (ULONG_PTR)ApiMessage;
1130
1131 /* Modify the pointer to take into account its new position */
1132 *CurrentPointer += BufferDistance;
1133 }
1134
1135 /* Move to the next Pointer */
1136 PointerCount--;
1137 }
1138
1139 /* Copy the data back */
1140 RtlMoveMemory(LocalCaptureBuffer, RemoteCaptureBuffer, RemoteCaptureBuffer->Size);
1141
1142 /* Free our allocated buffer */
1143 RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer);
1144 }
1145
1146 /* EOF */