xref: /reactos/subsystems/csr/csrsrv/api.c (revision d2aeaba5)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Client/Server Runtime SubSystem
4  * FILE:            subsystems/win32/csrsrv/api.c
5  * PURPOSE:         CSR Server DLL API LPC Implementation
6  *                  "\Windows\ApiPort" port process management functions
7  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "srv.h"
13 
14 #include <ndk/kefuncs.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 BOOLEAN (*CsrClientThreadSetup)(VOID) = NULL;
22 UNICODE_STRING CsrApiPortName;
23 volatile ULONG CsrpStaticThreadCount;
24 volatile ULONG CsrpDynamicThreadTotal;
25 extern ULONG CsrMaxApiRequestThreads;
26 
27 /* FUNCTIONS ******************************************************************/
28 
29 /*++
30  * @name CsrCallServerFromServer
31  * @implemented NT4
32  *
33  * The CsrCallServerFromServer routine calls a CSR API from within a server.
34  * It avoids using LPC messages since the request isn't coming from a client.
35  *
36  * @param ReceiveMsg
37  *        Pointer to the CSR API Message to send to the server.
38  *
39  * @param ReplyMsg
40  *        Pointer to the CSR API Message to receive from the server.
41  *
42  * @return STATUS_SUCCESS in case of success, STATUS_ILLEGAL_FUNCTION
43  *         if the ApiNumber is invalid, or STATUS_ACCESS_VIOLATION if there
44  *         was a problem executing the API.
45  *
46  * @remarks None.
47  *
48  *--*/
49 NTSTATUS
50 NTAPI
CsrCallServerFromServer(IN PCSR_API_MESSAGE ReceiveMsg,IN OUT PCSR_API_MESSAGE ReplyMsg)51 CsrCallServerFromServer(IN PCSR_API_MESSAGE ReceiveMsg,
52                         IN OUT PCSR_API_MESSAGE ReplyMsg)
53 {
54     ULONG ServerId;
55     PCSR_SERVER_DLL ServerDll = NULL;
56     ULONG ApiId;
57     CSR_REPLY_CODE ReplyCode = CsrReplyImmediately;
58 
59     /* Get the Server ID */
60     ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg->ApiNumber);
61 
62     /* Make sure that the ID is within limits, and the Server DLL loaded */
63     if ((ServerId >= CSR_SERVER_DLL_MAX) ||
64         (!(ServerDll = CsrLoadedServerDll[ServerId])))
65     {
66         /* We are beyond the Maximum Server ID */
67 #ifdef CSR_DBG
68         DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n",
69                 ServerId, ServerDll);
70         if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
71 #endif
72         ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
73         return STATUS_ILLEGAL_FUNCTION;
74     }
75     else
76     {
77         /* Get the API ID, normalized with our Base ID */
78         ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg->ApiNumber) - ServerDll->ApiBase;
79 
80         /* Make sure that the ID is within limits, and the entry exists */
81         if ((ApiId >= ServerDll->HighestApiSupported) ||
82             ((ServerDll->ValidTable) && !(ServerDll->ValidTable[ApiId])))
83         {
84             /* We are beyond the Maximum API ID, or it doesn't exist */
85 #ifdef CSR_DBG
86             DPRINT1("API: %d\n", ApiId);
87             DPRINT1("CSRSS: %lx (%s) is invalid ApiTableIndex for %Z or is an "
88                     "invalid API to call from the server.\n",
89                     ApiId,
90                     ((ServerDll->NameTable) && (ServerDll->NameTable[ApiId])) ?
91                     ServerDll->NameTable[ApiId] : "*** UNKNOWN ***",
92                     &ServerDll->Name);
93             if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
94 #endif
95             ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
96             return STATUS_ILLEGAL_FUNCTION;
97         }
98     }
99 
100 #ifdef CSR_DBG
101     if (CsrDebug & 2)
102     {
103         DPRINT1("CSRSS: %s Api Request received from server process\n",
104                 ServerDll->NameTable[ApiId]);
105     }
106 #endif
107 
108     /* Validation complete, start SEH */
109     _SEH2_TRY
110     {
111         /* Call the API, get the reply code and return the result */
112         ReplyMsg->Status = ServerDll->DispatchTable[ApiId](ReceiveMsg, &ReplyCode);
113     }
114     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
115     {
116         /* If we got an exception, return access violation */
117         ReplyMsg->Status = STATUS_ACCESS_VIOLATION;
118     }
119     _SEH2_END;
120 
121     /* Return success */
122     return STATUS_SUCCESS;
123 }
124 
125 /*++
126  * @name CsrApiHandleConnectionRequest
127  *
128  * The CsrApiHandleConnectionRequest routine handles and accepts a new
129  * connection request to the CSR API LPC Port.
130  *
131  * @param ApiMessage
132  *        Pointer to the incoming CSR API Message which contains the
133  *        connection request.
134  *
135  * @return STATUS_SUCCESS in case of success, or status code which caused
136  *         the routine to error.
137  *
138  * @remarks This routine is responsible for attaching the Shared Section to
139  *          new clients connecting to CSR.
140  *
141  *--*/
142 NTSTATUS
143 NTAPI
CsrApiHandleConnectionRequest(IN PCSR_API_MESSAGE ApiMessage)144 CsrApiHandleConnectionRequest(IN PCSR_API_MESSAGE ApiMessage)
145 {
146     PCSR_THREAD CsrThread = NULL;
147     PCSR_PROCESS CsrProcess = NULL;
148     NTSTATUS Status = STATUS_SUCCESS;
149     PCSR_API_CONNECTINFO ConnectInfo = &ApiMessage->ConnectionInfo;
150     BOOLEAN AllowConnection = FALSE;
151     REMOTE_PORT_VIEW RemotePortView;
152     HANDLE ServerPort;
153 
154     /* Acquire the Process Lock */
155     CsrAcquireProcessLock();
156 
157     /* Lookup the CSR Thread */
158     CsrThread = CsrLocateThreadByClientId(NULL, &ApiMessage->Header.ClientId);
159 
160     /* Check if we have a thread */
161     if (CsrThread)
162     {
163         /* Get the Process and make sure we have it as well */
164         CsrProcess = CsrThread->Process;
165         if (CsrProcess)
166         {
167             /* Reference the Process */
168             CsrLockedReferenceProcess(CsrProcess);
169 
170             /* Attach the Shared Section */
171             Status = CsrSrvAttachSharedSection(CsrProcess, ConnectInfo);
172             if (NT_SUCCESS(Status))
173             {
174                 /* Allow the connection and return debugging flag */
175                 ConnectInfo->DebugFlags = CsrDebug;
176                 AllowConnection = TRUE;
177             }
178 
179             /* Dereference the Process */
180             CsrLockedDereferenceProcess(CsrProcess);
181         }
182     }
183 
184     /* Release the Process Lock */
185     CsrReleaseProcessLock();
186 
187     /* Setup the Port View Structure */
188     RemotePortView.Length = sizeof(REMOTE_PORT_VIEW);
189     RemotePortView.ViewSize = 0;
190     RemotePortView.ViewBase = NULL;
191 
192     /* Save the Process ID */
193     ConnectInfo->ServerProcessId = NtCurrentTeb()->ClientId.UniqueProcess;
194 
195     /* Accept the Connection */
196     ASSERT(!AllowConnection || CsrProcess);
197     Status = NtAcceptConnectPort(&ServerPort,
198                                  AllowConnection ? UlongToPtr(CsrProcess->SequenceNumber) : 0,
199                                  &ApiMessage->Header,
200                                  AllowConnection,
201                                  NULL,
202                                  &RemotePortView);
203     if (!NT_SUCCESS(Status))
204     {
205          DPRINT1("CSRSS: NtAcceptConnectPort - failed.  Status == %X\n", Status);
206     }
207     else if (AllowConnection)
208     {
209         if (CsrDebug & 2)
210         {
211             DPRINT1("CSRSS: ClientId: %lx.%lx has ClientView: Base=%p, Size=%lx\n",
212                     ApiMessage->Header.ClientId.UniqueProcess,
213                     ApiMessage->Header.ClientId.UniqueThread,
214                     RemotePortView.ViewBase,
215                     RemotePortView.ViewSize);
216         }
217 
218         /* Set some Port Data in the Process */
219         CsrProcess->ClientPort = ServerPort;
220         CsrProcess->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase;
221         CsrProcess->ClientViewBounds = (ULONG_PTR)((ULONG_PTR)RemotePortView.ViewBase +
222                                                    (ULONG_PTR)RemotePortView.ViewSize);
223 
224         /* Complete the connection */
225         Status = NtCompleteConnectPort(ServerPort);
226         if (!NT_SUCCESS(Status))
227         {
228             DPRINT1("CSRSS: NtCompleteConnectPort - failed.  Status == %X\n", Status);
229         }
230     }
231     else
232     {
233         DPRINT1("CSRSS: Rejecting Connection Request from ClientId: %lx.%lx\n",
234                 ApiMessage->Header.ClientId.UniqueProcess,
235                 ApiMessage->Header.ClientId.UniqueThread);
236     }
237 
238     /* Return status to caller */
239     return Status;
240 }
241 
242 /*++
243  * @name CsrpCheckRequestThreads
244  *
245  * The CsrpCheckRequestThreads routine checks if there are no more threads
246  * to handle CSR API Requests, and creates a new thread if possible, to
247  * avoid starvation.
248  *
249  * @param None.
250  *
251  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
252  *         if a new thread couldn't be created.
253  *
254  * @remarks None.
255  *
256  *--*/
257 NTSTATUS
258 NTAPI
CsrpCheckRequestThreads(VOID)259 CsrpCheckRequestThreads(VOID)
260 {
261     HANDLE hThread;
262     CLIENT_ID ClientId;
263     NTSTATUS Status;
264 
265     /* Decrease the count, and see if we're out */
266     if (InterlockedDecrementUL(&CsrpStaticThreadCount) == 0)
267     {
268         /* Check if we've still got space for a Dynamic Thread */
269         if (CsrpDynamicThreadTotal < CsrMaxApiRequestThreads)
270         {
271             /* Create a new dynamic thread */
272             Status = RtlCreateUserThread(NtCurrentProcess(),
273                                          NULL,
274                                          TRUE,
275                                          0,
276                                          0,
277                                          0,
278                                          (PVOID)CsrApiRequestThread,
279                                          NULL,
280                                          &hThread,
281                                          &ClientId);
282             /* Check success */
283             if (NT_SUCCESS(Status))
284             {
285                 /* Increase the thread counts */
286                 InterlockedIncrementUL(&CsrpStaticThreadCount);
287                 InterlockedIncrementUL(&CsrpDynamicThreadTotal);
288 
289                 /* Add a new server thread */
290                 if (CsrAddStaticServerThread(hThread,
291                                              &ClientId,
292                                              CsrThreadIsServerThread))
293                 {
294                     /* Activate it */
295                     NtResumeThread(hThread, NULL);
296                 }
297                 else
298                 {
299                     /* Failed to create a new static thread */
300                     InterlockedDecrementUL(&CsrpStaticThreadCount);
301                     InterlockedDecrementUL(&CsrpDynamicThreadTotal);
302 
303                     /* Terminate it */
304                     DPRINT1("Failing\n");
305                     NtTerminateThread(hThread, 0);
306                     NtClose(hThread);
307 
308                     /* Return */
309                     return STATUS_UNSUCCESSFUL;
310                 }
311             }
312         }
313     }
314 
315     /* Success */
316     return STATUS_SUCCESS;
317 }
318 
319 /*++
320  * @name CsrApiRequestThread
321  *
322  * The CsrApiRequestThread routine handles incoming messages or connection
323  * requests on the CSR API LPC Port.
324  *
325  * @param Parameter
326  *        System-default user-defined parameter. Unused.
327  *
328  * @return The thread exit code, if the thread is terminated.
329  *
330  * @remarks Before listening on the port, the routine will first attempt
331  *          to connect to the user subsystem.
332  *
333  *--*/
334 NTSTATUS
335 NTAPI
CsrApiRequestThread(IN PVOID Parameter)336 CsrApiRequestThread(IN PVOID Parameter)
337 {
338     PTEB Teb = NtCurrentTeb();
339     LARGE_INTEGER TimeOut;
340     PCSR_THREAD CurrentThread, CsrThread;
341     NTSTATUS Status;
342     CSR_REPLY_CODE ReplyCode;
343     PCSR_API_MESSAGE ReplyMsg;
344     CSR_API_MESSAGE ReceiveMsg;
345     PCSR_PROCESS CsrProcess;
346     PHARDERROR_MSG HardErrorMsg;
347     PVOID PortContext;
348     PCSR_SERVER_DLL ServerDll;
349     PCLIENT_DIED_MSG ClientDiedMsg;
350     PDBGKM_MSG DebugMessage;
351     ULONG ServerId, ApiId, MessageType, i;
352     HANDLE ReplyPort;
353 
354     /* Setup LPC loop port and message */
355     ReplyMsg = NULL;
356     ReplyPort = CsrApiPort;
357 
358     /* Connect to user32 */
359     while (!CsrConnectToUser())
360     {
361         /* Set up the timeout for the connect (30 seconds) */
362         TimeOut.QuadPart = -30 * 1000 * 1000 * 10;
363 
364         /* Keep trying until we get a response */
365         Teb->Win32ClientInfo[0] = 0;
366         NtDelayExecution(FALSE, &TimeOut);
367     }
368 
369     /* Get our thread */
370     CurrentThread = Teb->CsrClientThread;
371 
372     /* If we got an event... */
373     if (Parameter)
374     {
375         /* Set it, to let stuff waiting on us load */
376         Status = NtSetEvent((HANDLE)Parameter, NULL);
377         ASSERT(NT_SUCCESS(Status));
378 
379         /* Increase the Thread Counts */
380         InterlockedIncrementUL(&CsrpStaticThreadCount);
381         InterlockedIncrementUL(&CsrpDynamicThreadTotal);
382     }
383 
384     /* Now start the loop */
385     while (TRUE)
386     {
387         /* Make sure the real CID is set */
388         Teb->RealClientId = Teb->ClientId;
389 
390 #ifdef CSR_DBG
391         /* Debug check */
392         if (Teb->CountOfOwnedCriticalSections)
393         {
394             DPRINT1("CSRSRV: FATAL ERROR. CsrThread is Idle while holding %lu critical sections\n",
395                     Teb->CountOfOwnedCriticalSections);
396             DPRINT1("CSRSRV: Last Receive Message %lx ReplyMessage %lx\n",
397                     &ReceiveMsg, ReplyMsg);
398             DbgBreakPoint();
399         }
400 #endif
401 
402         /* Wait for a message to come through */
403         Status = NtReplyWaitReceivePort(ReplyPort,
404                                         &PortContext,
405                                         &ReplyMsg->Header,
406                                         &ReceiveMsg.Header);
407 
408         /* Check if we didn't get success */
409         if (Status != STATUS_SUCCESS)
410         {
411             /* Was it a failure or another success code? */
412             if (!NT_SUCCESS(Status))
413             {
414 #ifdef CSR_DBG
415                 /* Check for specific status cases */
416                 if ((Status != STATUS_INVALID_CID) &&
417                     (Status != STATUS_UNSUCCESSFUL) &&
418                     ((Status != STATUS_INVALID_HANDLE) || (ReplyPort == CsrApiPort)))
419                 {
420                     /* Notify the debugger */
421                     DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status);
422                     DPRINT1("CSRSS: ReplyPortHandle %lx CsrApiPort %lx\n", ReplyPort, CsrApiPort);
423                 }
424 #endif
425 
426                 /* We failed big time, so start out fresh */
427                 ReplyMsg = NULL;
428                 ReplyPort = CsrApiPort;
429                 continue;
430             }
431             else
432             {
433                 /* A strange "success" code, just try again */
434                 DPRINT1("NtReplyWaitReceivePort returned \"success\" status 0x%x\n", Status);
435                 continue;
436             }
437         }
438 
439         // ASSERT(ReceiveMsg.Header.u1.s1.TotalLength >= sizeof(PORT_MESSAGE));
440         // ASSERT(ReceiveMsg.Header.u1.s1.TotalLength <  sizeof(ReceiveMsg));
441 
442         /* Use whatever Client ID we got */
443         Teb->RealClientId = ReceiveMsg.Header.ClientId;
444 
445         /* Get the Message Type */
446         MessageType = ReceiveMsg.Header.u2.s2.Type;
447 
448         /* Handle connection requests */
449         if (MessageType == LPC_CONNECTION_REQUEST)
450         {
451             /* Handle the Connection Request */
452             CsrApiHandleConnectionRequest(&ReceiveMsg);
453 
454             ReplyMsg = NULL;
455             ReplyPort = CsrApiPort;
456             continue;
457         }
458 
459         /* It's some other kind of request. Get the lock for the lookup */
460         CsrAcquireProcessLock();
461 
462         /* Now do the lookup to get the CSR_THREAD */
463         CsrThread = CsrLocateThreadByClientId(&CsrProcess,
464                                               &ReceiveMsg.Header.ClientId);
465 
466         /* Did we find a thread? */
467         if (!CsrThread)
468         {
469             /* This wasn't a CSR Thread, release lock */
470             CsrReleaseProcessLock();
471 
472             /* If this was an exception, handle it */
473             if (MessageType == LPC_EXCEPTION)
474             {
475                 ReplyMsg = &ReceiveMsg;
476                 ReplyPort = CsrApiPort;
477                 ReplyMsg->Status = DBG_CONTINUE;
478             }
479             else if (MessageType == LPC_PORT_CLOSED ||
480                      MessageType == LPC_CLIENT_DIED)
481             {
482                 /* The Client or Port are gone, loop again */
483                 ReplyMsg = NULL;
484                 ReplyPort = CsrApiPort;
485             }
486             else if (MessageType == LPC_ERROR_EVENT)
487             {
488                 /* If it's a hard error, handle this too */
489                 HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg;
490 
491                 /* Default it to unhandled */
492                 HardErrorMsg->Response = ResponseNotHandled;
493 
494                 /* Check if there are free api threads */
495                 CsrpCheckRequestThreads();
496                 if (CsrpStaticThreadCount)
497                 {
498                     /* Loop every Server DLL */
499                     for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
500                     {
501                         /* Get the Server DLL */
502                         ServerDll = CsrLoadedServerDll[i];
503 
504                         /* Check if it's valid and if it has a Hard Error Callback */
505                         if ((ServerDll) && (ServerDll->HardErrorCallback))
506                         {
507                             /* Call it */
508                             ServerDll->HardErrorCallback(NULL /* == CsrThread */, HardErrorMsg);
509 
510                             /* If it's handled, get out of here */
511                             if (HardErrorMsg->Response != ResponseNotHandled) break;
512                         }
513                     }
514                 }
515 
516                 /* Increase the thread count */
517                 InterlockedIncrementUL(&CsrpStaticThreadCount);
518 
519                 /* If the response was 0xFFFFFFFF, we'll ignore it */
520                 if (HardErrorMsg->Response == 0xFFFFFFFF)
521                 {
522                     ReplyMsg = NULL;
523                     ReplyPort = CsrApiPort;
524                 }
525                 else
526                 {
527                     ReplyMsg = &ReceiveMsg;
528                     ReplyPort = CsrApiPort;
529                 }
530             }
531             else if (MessageType == LPC_REQUEST)
532             {
533                 /* This is an API Message coming from a non-CSR Thread */
534                 ReplyMsg = &ReceiveMsg;
535                 ReplyPort = CsrApiPort;
536                 ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
537             }
538             else if (MessageType == LPC_DATAGRAM)
539             {
540                 /* This is an API call, get the Server ID */
541                 ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg.ApiNumber);
542 
543                 /* Make sure that the ID is within limits, and the Server DLL loaded */
544                 ServerDll = NULL;
545                 if ((ServerId >= CSR_SERVER_DLL_MAX) ||
546                     (!(ServerDll = CsrLoadedServerDll[ServerId])))
547                 {
548                     /* We are beyond the Maximum Server ID */
549 #ifdef CSR_DBG
550                     DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n",
551                             ServerId, ServerDll);
552                     if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
553 #endif
554                     ReplyMsg = NULL;
555                     ReplyPort = CsrApiPort;
556                     continue;
557                 }
558 
559                 /* Get the API ID, normalized with our Base ID */
560                 ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber) - ServerDll->ApiBase;
561 
562                 /* Make sure that the ID is within limits, and the entry exists */
563                 if (ApiId >= ServerDll->HighestApiSupported)
564                 {
565                     /* We are beyond the Maximum API ID, or it doesn't exist */
566 #ifdef CSR_DBG
567                     DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n",
568                             CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber),
569                             &ServerDll->Name);
570 #endif
571                     ReplyPort = CsrApiPort;
572                     ReplyMsg = NULL;
573                     continue;
574                 }
575 
576 #ifdef CSR_DBG
577                 if (CsrDebug & 2)
578                 {
579                     DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x\n",
580                             Teb->ClientId.UniqueThread,
581                             ReceiveMsg.Header.ClientId.UniqueProcess,
582                             ReceiveMsg.Header.ClientId.UniqueThread,
583                             ServerDll->NameTable[ApiId],
584                             NULL);
585                 }
586 #endif
587 
588                 /* Assume success */
589                 ReceiveMsg.Status = STATUS_SUCCESS;
590 
591                 /* Validation complete, start SEH */
592                 _SEH2_TRY
593                 {
594                     /* Make sure we have enough threads */
595                     CsrpCheckRequestThreads();
596 
597                     /* Call the API and get the reply code */
598                     ReplyMsg = NULL;
599                     ReplyPort = CsrApiPort;
600                     ServerDll->DispatchTable[ApiId](&ReceiveMsg, &ReplyCode);
601 
602                     /* Increase the static thread count */
603                     InterlockedIncrementUL(&CsrpStaticThreadCount);
604                 }
605                 _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
606                 {
607                     ReplyMsg = NULL;
608                     ReplyPort = CsrApiPort;
609                 }
610                 _SEH2_END;
611             }
612             else
613             {
614                 /* Some other ignored message type */
615                 ReplyMsg = NULL;
616                 ReplyPort = CsrApiPort;
617             }
618 
619             /* Keep going */
620             continue;
621         }
622 
623         /* We have a valid thread, was this an LPC Request? */
624         if (MessageType != LPC_REQUEST)
625         {
626             /* It's not an API, check if the client died */
627             if (MessageType == LPC_CLIENT_DIED)
628             {
629                 /* Get the information and check if it matches our thread */
630                 ClientDiedMsg = (PCLIENT_DIED_MSG)&ReceiveMsg;
631                 if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart)
632                 {
633                     /* Now we reply to the dying client */
634                     ReplyPort = CsrThread->Process->ClientPort;
635 
636                     /* Reference the thread */
637                     CsrLockedReferenceThread(CsrThread);
638 
639                     /* Destroy the thread in the API Message */
640                     CsrDestroyThread(&ReceiveMsg.Header.ClientId);
641 
642                     /* Check if the thread was actually ourselves */
643                     if (CsrProcess->ThreadCount == 1)
644                     {
645                         /* Kill the process manually here */
646                         CsrDestroyProcess(&CsrThread->ClientId, 0);
647                     }
648 
649                     /* Remove our extra reference */
650                     CsrLockedDereferenceThread(CsrThread);
651                 }
652 
653                 /* Release the lock and keep looping */
654                 CsrReleaseProcessLock();
655 
656                 ReplyMsg = NULL;
657                 ReplyPort = CsrApiPort;
658                 continue;
659             }
660 
661             /* Reference the thread and release the lock */
662             CsrLockedReferenceThread(CsrThread);
663             CsrReleaseProcessLock();
664 
665             /* Check if this was an exception */
666             if (MessageType == LPC_EXCEPTION)
667             {
668                 /* Kill the process */
669                 NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED);
670 
671                 /* Destroy it from CSR */
672                 CsrDestroyProcess(&ReceiveMsg.Header.ClientId, STATUS_ABANDONED);
673 
674                 /* Return a Debug Message */
675                 DebugMessage = (PDBGKM_MSG)&ReceiveMsg;
676                 DebugMessage->ReturnedStatus = DBG_CONTINUE;
677                 ReplyMsg = &ReceiveMsg;
678                 ReplyPort = CsrApiPort;
679 
680                 /* Remove our extra reference */
681                 CsrDereferenceThread(CsrThread);
682             }
683             else if (MessageType == LPC_ERROR_EVENT)
684             {
685                 /* If it's a hard error, handle this too */
686                 HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg;
687 
688                 /* Default it to unhandled */
689                 HardErrorMsg->Response = ResponseNotHandled;
690 
691                 /* Check if there are free api threads */
692                 CsrpCheckRequestThreads();
693                 if (CsrpStaticThreadCount)
694                 {
695                     /* Loop every Server DLL */
696                     for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
697                     {
698                         /* Get the Server DLL */
699                         ServerDll = CsrLoadedServerDll[i];
700 
701                         /* Check if it's valid and if it has a Hard Error Callback */
702                         if ((ServerDll) && (ServerDll->HardErrorCallback))
703                         {
704                             /* Call it */
705                             ServerDll->HardErrorCallback(CsrThread, HardErrorMsg);
706 
707                             /* If it's handled, get out of here */
708                             if (HardErrorMsg->Response != ResponseNotHandled) break;
709                         }
710                     }
711                 }
712 
713                 /* Increase the thread count */
714                 InterlockedIncrementUL(&CsrpStaticThreadCount);
715 
716                 /* If the response was 0xFFFFFFFF, we'll ignore it */
717                 if (HardErrorMsg->Response == 0xFFFFFFFF)
718                 {
719                     ReplyMsg = NULL;
720                     ReplyPort = CsrApiPort;
721                 }
722                 else
723                 {
724                     CsrDereferenceThread(CsrThread);
725                     ReplyMsg = &ReceiveMsg;
726                     ReplyPort = CsrApiPort;
727                 }
728             }
729             else
730             {
731                 /* Something else */
732                 CsrDereferenceThread(CsrThread);
733                 ReplyMsg = NULL;
734             }
735 
736             /* Keep looping */
737             continue;
738         }
739 
740         /* We got an API Request */
741         CsrLockedReferenceThread(CsrThread);
742         CsrReleaseProcessLock();
743 
744         /* This is an API call, get the Server ID */
745         ServerId = CSR_API_NUMBER_TO_SERVER_ID(ReceiveMsg.ApiNumber);
746 
747         /* Make sure that the ID is within limits, and the Server DLL loaded */
748         ServerDll = NULL;
749         if ((ServerId >= CSR_SERVER_DLL_MAX) ||
750             (!(ServerDll = CsrLoadedServerDll[ServerId])))
751         {
752             /* We are beyond the Maximum Server ID */
753 #ifdef CSR_DBG
754             DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n",
755                     ServerId, ServerDll);
756             if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
757 #endif
758             ReplyPort = CsrApiPort;
759             ReplyMsg = &ReceiveMsg;
760             ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
761             CsrDereferenceThread(CsrThread);
762             continue;
763         }
764 
765         /* Get the API ID, normalized with our Base ID */
766         ApiId = CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber) - ServerDll->ApiBase;
767 
768         /* Make sure that the ID is within limits, and the entry exists */
769         if (ApiId >= ServerDll->HighestApiSupported)
770         {
771 #ifdef CSR_DBG
772             /* We are beyond the Maximum API ID, or it doesn't exist */
773             DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n",
774                     CSR_API_NUMBER_TO_API_ID(ReceiveMsg.ApiNumber),
775                     &ServerDll->Name);
776 #endif
777             ReplyPort = CsrApiPort;
778             ReplyMsg = &ReceiveMsg;
779             ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION;
780             CsrDereferenceThread(CsrThread);
781             continue;
782         }
783 
784 #ifdef CSR_DBG
785         if (CsrDebug & 2)
786         {
787             DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x, Process %08x - %08x\n",
788                     Teb->ClientId.UniqueThread,
789                     ReceiveMsg.Header.ClientId.UniqueProcess,
790                     ReceiveMsg.Header.ClientId.UniqueThread,
791                     ServerDll->NameTable[ApiId],
792                     CsrThread,
793                     CsrThread->Process,
794                     CsrProcess);
795         }
796 #endif
797 
798         /* Assume success */
799         ReplyMsg = &ReceiveMsg;
800         ReceiveMsg.Status = STATUS_SUCCESS;
801 
802         /* Now we reply to a particular client */
803         ReplyPort = CsrThread->Process->ClientPort;
804 
805         /* Check if there's a capture buffer */
806         if (ReceiveMsg.CsrCaptureData)
807         {
808             /* Capture the arguments */
809             if (!CsrCaptureArguments(CsrThread, &ReceiveMsg))
810             {
811                 /* Ignore this message if we failed to get the arguments */
812                 CsrDereferenceThread(CsrThread);
813                 continue;
814             }
815         }
816 
817         /* Validation complete, start SEH */
818         _SEH2_TRY
819         {
820             /* Make sure we have enough threads */
821             CsrpCheckRequestThreads();
822 
823             Teb->CsrClientThread = CsrThread;
824 
825             /* Call the API, get the reply code and return the result */
826             ReplyCode = CsrReplyImmediately;
827             ReplyMsg->Status = ServerDll->DispatchTable[ApiId](&ReceiveMsg, &ReplyCode);
828 
829             /* Increase the static thread count */
830             InterlockedIncrementUL(&CsrpStaticThreadCount);
831 
832             Teb->CsrClientThread = CurrentThread;
833 
834             if (ReplyCode == CsrReplyAlreadySent)
835             {
836                 if (ReceiveMsg.CsrCaptureData)
837                 {
838                     CsrReleaseCapturedArguments(&ReceiveMsg);
839                 }
840                 ReplyMsg = NULL;
841                 ReplyPort = CsrApiPort;
842                 CsrDereferenceThread(CsrThread);
843             }
844             else if (ReplyCode == CsrReplyDeadClient)
845             {
846                 /* Reply to the death message */
847                 NTSTATUS Status2;
848                 Status2 = NtReplyPort(ReplyPort, &ReplyMsg->Header);
849                 if (!NT_SUCCESS(Status2))
850                     DPRINT1("CSRSS: Error while replying to the death message, Status 0x%lx\n", Status2);
851 
852                 /* Reply back to the API port now */
853                 ReplyMsg = NULL;
854                 ReplyPort = CsrApiPort;
855                 CsrDereferenceThread(CsrThread);
856             }
857             else if (ReplyCode == CsrReplyPending)
858             {
859                 ReplyMsg = NULL;
860                 ReplyPort = CsrApiPort;
861             }
862             else
863             {
864                 if (ReceiveMsg.CsrCaptureData)
865                 {
866                     CsrReleaseCapturedArguments(&ReceiveMsg);
867                 }
868                 CsrDereferenceThread(CsrThread);
869             }
870         }
871         _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation()))
872         {
873             ReplyMsg = NULL;
874             ReplyPort = CsrApiPort;
875         }
876         _SEH2_END;
877     }
878 
879     /* We're out of the loop for some reason, terminate! */
880     NtTerminateThread(NtCurrentThread(), Status);
881     return Status;
882 }
883 
884 /*++
885  * @name CsrApiPortInitialize
886  *
887  * The CsrApiPortInitialize routine initializes the LPC Port used for
888  * communications with the Client/Server Runtime (CSR) and initializes the
889  * static thread that will handle connection requests and APIs.
890  *
891  * @param None
892  *
893  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
894  *
895  * @remarks None.
896  *
897  *--*/
898 NTSTATUS
899 NTAPI
CsrApiPortInitialize(VOID)900 CsrApiPortInitialize(VOID)
901 {
902     ULONG Size;
903     OBJECT_ATTRIBUTES ObjectAttributes;
904     NTSTATUS Status;
905     HANDLE hRequestEvent, hThread;
906     CLIENT_ID ClientId;
907     PLIST_ENTRY ListHead, NextEntry;
908     PCSR_THREAD ServerThread;
909 
910     /* Calculate how much space we'll need for the Port Name */
911     Size = CsrDirectoryName.Length + sizeof(CSR_PORT_NAME) + sizeof(WCHAR);
912 
913     /* Create the buffer for it */
914     CsrApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size);
915     if (!CsrApiPortName.Buffer) return STATUS_NO_MEMORY;
916 
917     /* Setup the rest of the empty string */
918     CsrApiPortName.Length = 0;
919     CsrApiPortName.MaximumLength = (USHORT)Size;
920     RtlAppendUnicodeStringToString(&CsrApiPortName, &CsrDirectoryName);
921     RtlAppendUnicodeToString(&CsrApiPortName, UNICODE_PATH_SEP);
922     RtlAppendUnicodeToString(&CsrApiPortName, CSR_PORT_NAME);
923     if (CsrDebug & 1)
924     {
925         DPRINT1("CSRSS: Creating %wZ port and associated threads\n", &CsrApiPortName);
926         DPRINT1("CSRSS: sizeof( CONNECTINFO ) == %ld  sizeof( API_MSG ) == %ld\n",
927                 sizeof(CSR_API_CONNECTINFO), sizeof(CSR_API_MESSAGE));
928     }
929 
930     /* FIXME: Create a Security Descriptor */
931 
932     /* Initialize the Attributes */
933     InitializeObjectAttributes(&ObjectAttributes,
934                                &CsrApiPortName,
935                                0,
936                                NULL,
937                                NULL /* FIXME: Use the Security Descriptor */);
938 
939     /* Create the Port Object */
940     Status = NtCreatePort(&CsrApiPort,
941                           &ObjectAttributes,
942                           sizeof(CSR_API_CONNECTINFO),
943                           sizeof(CSR_API_MESSAGE),
944                           16 * PAGE_SIZE);
945     if (NT_SUCCESS(Status))
946     {
947         /* Create the event the Port Thread will use */
948         Status = NtCreateEvent(&hRequestEvent,
949                                EVENT_ALL_ACCESS,
950                                NULL,
951                                SynchronizationEvent,
952                                FALSE);
953         if (NT_SUCCESS(Status))
954         {
955             /* Create the Request Thread */
956             Status = RtlCreateUserThread(NtCurrentProcess(),
957                                          NULL,
958                                          TRUE,
959                                          0,
960                                          0,
961                                          0,
962                                          (PVOID)CsrApiRequestThread,
963                                          (PVOID)hRequestEvent,
964                                          &hThread,
965                                          &ClientId);
966             if (NT_SUCCESS(Status))
967             {
968                 /* Add this as a static thread to CSRSRV */
969                 CsrAddStaticServerThread(hThread, &ClientId, CsrThreadIsServerThread);
970 
971                 /* Get the Thread List Pointers */
972                 ListHead = &CsrRootProcess->ThreadList;
973                 NextEntry = ListHead->Flink;
974 
975                 /* Start looping the list */
976                 while (NextEntry != ListHead)
977                 {
978                     /* Get the Thread */
979                     ServerThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
980 
981                     /* Start it up */
982                     Status = NtResumeThread(ServerThread->ThreadHandle, NULL);
983 
984                     /* Is this a Server Thread? */
985                     if (ServerThread->Flags & CsrThreadIsServerThread)
986                     {
987                         /* If so, then wait for it to initialize */
988                         Status = NtWaitForSingleObject(hRequestEvent, FALSE, NULL);
989                         ASSERT(NT_SUCCESS(Status));
990                     }
991 
992                     /* Next thread */
993                     NextEntry = NextEntry->Flink;
994                 }
995 
996                 /* We don't need this anymore */
997                 NtClose(hRequestEvent);
998             }
999         }
1000     }
1001 
1002     /* Return */
1003     return Status;
1004 }
1005 
1006 /*++
1007  * @name CsrConnectToUser
1008  * @implemented NT4
1009  *
1010  * The CsrConnectToUser connects to the User subsystem.
1011  *
1012  * @param None
1013  *
1014  * @return A pointer to the CSR Thread
1015  *
1016  * @remarks None.
1017  *
1018  *--*/
1019 PCSR_THREAD
1020 NTAPI
CsrConnectToUser(VOID)1021 CsrConnectToUser(VOID)
1022 {
1023     NTSTATUS Status;
1024     ANSI_STRING DllName;
1025     UNICODE_STRING TempName;
1026     HANDLE hUser32;
1027     STRING StartupName;
1028     PTEB Teb = NtCurrentTeb();
1029     PCSR_THREAD CsrThread;
1030     BOOLEAN Connected;
1031 
1032     /* Check if we didn't already find it */
1033     if (!CsrClientThreadSetup)
1034     {
1035         /* Get the DLL Handle for user32.dll */
1036         RtlInitAnsiString(&DllName, "user32");
1037         RtlAnsiStringToUnicodeString(&TempName, &DllName, TRUE);
1038         Status = LdrGetDllHandle(NULL,
1039                                  NULL,
1040                                  &TempName,
1041                                  &hUser32);
1042         RtlFreeUnicodeString(&TempName);
1043 
1044         /* If we got the handle, get the Client Thread Startup Entrypoint */
1045         if (NT_SUCCESS(Status))
1046         {
1047             RtlInitAnsiString(&StartupName,"ClientThreadSetup");
1048             Status = LdrGetProcedureAddress(hUser32,
1049                                             &StartupName,
1050                                             0,
1051                                             (PVOID)&CsrClientThreadSetup);
1052         }
1053     }
1054 
1055     /* Connect to user32 */
1056     _SEH2_TRY
1057     {
1058         Connected = CsrClientThreadSetup();
1059     }
1060     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1061     {
1062         Connected = FALSE;
1063     }
1064     _SEH2_END;
1065 
1066     if (!Connected)
1067     {
1068         DPRINT1("CSRSS: CsrConnectToUser failed\n");
1069         return NULL;
1070     }
1071 
1072     /* Save pointer to this thread in TEB */
1073     CsrAcquireProcessLock();
1074     CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId);
1075     CsrReleaseProcessLock();
1076     if (CsrThread) Teb->CsrClientThread = CsrThread;
1077 
1078     /* Return it */
1079     return CsrThread;
1080 }
1081 
1082 /*++
1083  * @name CsrQueryApiPort
1084  * @implemented NT4
1085  *
1086  * The CsrQueryApiPort routine returns a handle to the CSR API LPC port.
1087  *
1088  * @param None.
1089  *
1090  * @return A handle to the port.
1091  *
1092  * @remarks None.
1093  *
1094  *--*/
1095 HANDLE
1096 NTAPI
CsrQueryApiPort(VOID)1097 CsrQueryApiPort(VOID)
1098 {
1099     return CsrApiPort;
1100 }
1101 
1102 /*++
1103  * @name CsrCaptureArguments
1104  * @implemented NT5.1
1105  *
1106  * The CsrCaptureArguments routine validates a CSR Capture Buffer and
1107  * re-captures it into a server CSR Capture Buffer.
1108  *
1109  * @param CsrThread
1110  *        Pointer to the CSR Thread performing the validation.
1111  *
1112  * @param ApiMessage
1113  *        Pointer to the CSR API Message containing the Capture Buffer
1114  *        that needs to be validated.
1115  *
1116  * @return TRUE if validation succeeded, FALSE otherwise.
1117  *
1118  * @remarks None.
1119  *
1120  *--*/
1121 BOOLEAN
1122 NTAPI
CsrCaptureArguments(IN PCSR_THREAD CsrThread,IN PCSR_API_MESSAGE ApiMessage)1123 CsrCaptureArguments(IN PCSR_THREAD CsrThread,
1124                     IN PCSR_API_MESSAGE ApiMessage)
1125 {
1126     PCSR_PROCESS CsrProcess = CsrThread->Process;
1127     PCSR_CAPTURE_BUFFER ClientCaptureBuffer, ServerCaptureBuffer = NULL;
1128     ULONG_PTR EndOfClientBuffer;
1129     SIZE_T SizeOfBufferThroughOffsetsArray;
1130     SIZE_T BufferDistance;
1131     ULONG Length;
1132     ULONG PointerCount;
1133     PULONG_PTR OffsetPointer;
1134     ULONG_PTR CurrentOffset;
1135 
1136     /* Get the buffer we got from whoever called NTDLL */
1137     ClientCaptureBuffer = ApiMessage->CsrCaptureData;
1138 
1139     /* Use SEH to validate and capture the client buffer */
1140     _SEH2_TRY
1141     {
1142         /* Check whether at least the buffer's header is inside our mapped section */
1143         if (  ((ULONG_PTR)ClientCaptureBuffer < CsrProcess->ClientViewBase) ||
1144              (((ULONG_PTR)ClientCaptureBuffer + FIELD_OFFSET(CSR_CAPTURE_BUFFER, PointerOffsetsArray))
1145                     >= CsrProcess->ClientViewBounds) )
1146         {
1147 #ifdef CSR_DBG
1148             DPRINT1("*** CSRSS: CaptureBuffer outside of ClientView 1\n");
1149             if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1150 #endif
1151             /* Return failure */
1152             ApiMessage->Status = STATUS_INVALID_PARAMETER;
1153             _SEH2_YIELD(return FALSE);
1154         }
1155 
1156         /* Capture the buffer length */
1157         Length = ((volatile CSR_CAPTURE_BUFFER*)ClientCaptureBuffer)->Size;
1158 
1159         /*
1160          * Now check if the remaining of the buffer is inside our mapped section.
1161          * Take also care for any possible wrap-around of the buffer end-address.
1162          */
1163         EndOfClientBuffer = (ULONG_PTR)ClientCaptureBuffer + Length;
1164         if ( (EndOfClientBuffer < (ULONG_PTR)ClientCaptureBuffer) ||
1165              (EndOfClientBuffer >= CsrProcess->ClientViewBounds) )
1166         {
1167 #ifdef CSR_DBG
1168             DPRINT1("*** CSRSS: CaptureBuffer outside of ClientView 2\n");
1169             if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1170 #endif
1171             /* Return failure */
1172             ApiMessage->Status = STATUS_INVALID_PARAMETER;
1173             _SEH2_YIELD(return FALSE);
1174         }
1175 
1176         /* Capture the pointer count */
1177         PointerCount = ((volatile CSR_CAPTURE_BUFFER*)ClientCaptureBuffer)->PointerCount;
1178 
1179         /*
1180          * Check whether the total buffer size and the pointer count are consistent
1181          * -- the array of offsets must be contained inside the buffer.
1182          */
1183         SizeOfBufferThroughOffsetsArray =
1184             FIELD_OFFSET(CSR_CAPTURE_BUFFER, PointerOffsetsArray) +
1185                 (PointerCount * sizeof(PVOID));
1186         if ( (PointerCount > MAXUSHORT) ||
1187              (SizeOfBufferThroughOffsetsArray > Length) )
1188         {
1189 #ifdef CSR_DBG
1190             DPRINT1("*** CSRSS: CaptureBuffer %p has bad length\n", ClientCaptureBuffer);
1191             if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1192 #endif
1193             /* Return failure */
1194             ApiMessage->Status = STATUS_INVALID_PARAMETER;
1195             _SEH2_YIELD(return FALSE);
1196         }
1197     }
1198     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1199     {
1200 #ifdef CSR_DBG
1201         DPRINT1("*** CSRSS: Took exception during capture %x\n", _SEH2_GetExceptionCode());
1202         if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1203 #endif
1204         /* Return failure */
1205         ApiMessage->Status = STATUS_INVALID_PARAMETER;
1206         _SEH2_YIELD(return FALSE);
1207     }
1208     _SEH2_END;
1209 
1210     /* We validated the client buffer, now allocate the server buffer */
1211     ServerCaptureBuffer = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Length);
1212     if (!ServerCaptureBuffer)
1213     {
1214         /* We're out of memory */
1215         ApiMessage->Status = STATUS_NO_MEMORY;
1216         return FALSE;
1217     }
1218 
1219     /*
1220      * Copy the client's buffer and ensure we use the correct buffer length
1221      * and pointer count we captured and used for validation earlier on.
1222      */
1223     _SEH2_TRY
1224     {
1225         RtlMoveMemory(ServerCaptureBuffer, ClientCaptureBuffer, Length);
1226     }
1227     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1228     {
1229 #ifdef CSR_DBG
1230         DPRINT1("*** CSRSS: Took exception during capture %x\n", _SEH2_GetExceptionCode());
1231         if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1232 #endif
1233         /* Failure, free the buffer and return */
1234         RtlFreeHeap(CsrHeap, 0, ServerCaptureBuffer);
1235         ApiMessage->Status = STATUS_INVALID_PARAMETER;
1236         _SEH2_YIELD(return FALSE);
1237     }
1238     _SEH2_END;
1239 
1240     ServerCaptureBuffer->Size = Length;
1241     ServerCaptureBuffer->PointerCount = PointerCount;
1242 
1243     /* Calculate the difference between our buffer and the client's */
1244     BufferDistance = (ULONG_PTR)ServerCaptureBuffer - (ULONG_PTR)ClientCaptureBuffer;
1245 
1246     /*
1247      * All the pointer offsets correspond to pointers that point
1248      * to the server data buffer instead of the client one.
1249      */
1250     // PointerCount  = ServerCaptureBuffer->PointerCount;
1251     OffsetPointer = ServerCaptureBuffer->PointerOffsetsArray;
1252     while (PointerCount--)
1253     {
1254         CurrentOffset = *OffsetPointer;
1255 
1256         if (CurrentOffset != 0)
1257         {
1258             /*
1259              * Check whether the offset is pointer-aligned and whether
1260              * it points inside CSR_API_MESSAGE::Data.ApiMessageData.
1261              */
1262             if ( ((CurrentOffset & (sizeof(PVOID)-1)) != 0) ||
1263                  (CurrentOffset < FIELD_OFFSET(CSR_API_MESSAGE, Data.ApiMessageData)) ||
1264                  (CurrentOffset >= sizeof(CSR_API_MESSAGE)) )
1265             {
1266 #ifdef CSR_DBG
1267                 DPRINT1("*** CSRSS: CaptureBuffer MessagePointer outside of message\n");
1268                 if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1269 #endif
1270                 /* Invalid pointer, fail */
1271                 ApiMessage->Status = STATUS_INVALID_PARAMETER;
1272                 break;
1273             }
1274 
1275             /* Get the pointer corresponding to the offset */
1276             CurrentOffset += (ULONG_PTR)ApiMessage;
1277 
1278             /* Validate the bounds of the current pointed pointer */
1279             if ( (*(PULONG_PTR)CurrentOffset >= ((ULONG_PTR)ClientCaptureBuffer +
1280                                                  SizeOfBufferThroughOffsetsArray)) &&
1281                  (*(PULONG_PTR)CurrentOffset <= (EndOfClientBuffer - sizeof(PVOID))) )
1282             {
1283                 /* Modify the pointed pointer to take into account its new position */
1284                 *(PULONG_PTR)CurrentOffset += BufferDistance;
1285             }
1286             else
1287             {
1288 #ifdef CSR_DBG
1289                 DPRINT1("*** CSRSS: CaptureBuffer MessagePointer outside of ClientView\n");
1290                 if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1291 #endif
1292                 /* Invalid pointer, fail */
1293                 ApiMessage->Status = STATUS_INVALID_PARAMETER;
1294                 break;
1295             }
1296         }
1297 
1298         ++OffsetPointer;
1299     }
1300 
1301     /* Check if we got success */
1302     if (ApiMessage->Status != STATUS_SUCCESS)
1303     {
1304         /* Failure, free the buffer and return */
1305         RtlFreeHeap(CsrHeap, 0, ServerCaptureBuffer);
1306         return FALSE;
1307     }
1308     else
1309     {
1310         /* Success, save the previous buffer and use the server capture buffer */
1311         ServerCaptureBuffer->PreviousCaptureBuffer = ClientCaptureBuffer;
1312         ApiMessage->CsrCaptureData = ServerCaptureBuffer;
1313     }
1314 
1315     /* Success */
1316     return TRUE;
1317 }
1318 
1319 /*++
1320  * @name CsrReleaseCapturedArguments
1321  * @implemented NT5.1
1322  *
1323  * The CsrReleaseCapturedArguments routine releases a Capture Buffer
1324  * that was previously captured with CsrCaptureArguments.
1325  *
1326  * @param ApiMessage
1327  *        Pointer to the CSR API Message containing the Capture Buffer
1328  *        that needs to be released.
1329  *
1330  * @return None.
1331  *
1332  * @remarks None.
1333  *
1334  *--*/
1335 VOID
1336 NTAPI
CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage)1337 CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage)
1338 {
1339     PCSR_CAPTURE_BUFFER ServerCaptureBuffer, ClientCaptureBuffer;
1340     SIZE_T BufferDistance;
1341     ULONG PointerCount;
1342     PULONG_PTR OffsetPointer;
1343     ULONG_PTR CurrentOffset;
1344 
1345     /* Get the server capture buffer */
1346     ServerCaptureBuffer = ApiMessage->CsrCaptureData;
1347 
1348     /* Do not continue if there is no captured buffer */
1349     if (!ServerCaptureBuffer) return;
1350 
1351     /* If there is one, get the corresponding client capture buffer */
1352     ClientCaptureBuffer = ServerCaptureBuffer->PreviousCaptureBuffer;
1353 
1354     /* Free the previous one and use again the client capture buffer */
1355     ServerCaptureBuffer->PreviousCaptureBuffer = NULL;
1356     ApiMessage->CsrCaptureData = ClientCaptureBuffer;
1357 
1358     /* Calculate the difference between our buffer and the client's */
1359     BufferDistance = (ULONG_PTR)ServerCaptureBuffer - (ULONG_PTR)ClientCaptureBuffer;
1360 
1361     /*
1362      * All the pointer offsets correspond to pointers that point
1363      * to the client data buffer instead of the server one (reverse
1364      * the logic of CsrCaptureArguments()).
1365      */
1366     PointerCount  = ServerCaptureBuffer->PointerCount;
1367     OffsetPointer = ServerCaptureBuffer->PointerOffsetsArray;
1368     while (PointerCount--)
1369     {
1370         CurrentOffset = *OffsetPointer;
1371 
1372         if (CurrentOffset != 0)
1373         {
1374             /* Get the pointer corresponding to the offset */
1375             CurrentOffset += (ULONG_PTR)ApiMessage;
1376 
1377             /* Modify the pointed pointer to take into account its new position */
1378             *(PULONG_PTR)CurrentOffset -= BufferDistance;
1379         }
1380 
1381         ++OffsetPointer;
1382     }
1383 
1384     /* Copy the data back into the client buffer */
1385     _SEH2_TRY
1386     {
1387         RtlMoveMemory(ClientCaptureBuffer, ServerCaptureBuffer, ServerCaptureBuffer->Size);
1388     }
1389     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1390     {
1391 #ifdef CSR_DBG
1392         DPRINT1("*** CSRSS: Took exception during release %x\n", _SEH2_GetExceptionCode());
1393         if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1394 #endif
1395         /* Return failure */
1396         ApiMessage->Status = _SEH2_GetExceptionCode();
1397     }
1398     _SEH2_END;
1399 
1400     /* Free our allocated buffer */
1401     RtlFreeHeap(CsrHeap, 0, ServerCaptureBuffer);
1402 }
1403 
1404 /*++
1405  * @name CsrValidateMessageBuffer
1406  * @implemented NT5.1
1407  *
1408  * The CsrValidateMessageBuffer routine validates a captured message buffer
1409  * present in the CSR Api Message
1410  *
1411  * @param ApiMessage
1412  *        Pointer to the CSR API Message containing the CSR Capture Buffer.
1413  *
1414  * @param Buffer
1415  *        Pointer to the message buffer to validate.
1416  *
1417  * @param ElementCount
1418  *        Number of elements contained in the message buffer.
1419  *
1420  * @param ElementSize
1421  *        Size of each element.
1422  *
1423  * @return TRUE if validation succeeded, FALSE otherwise.
1424  *
1425  * @remarks None.
1426  *
1427  *--*/
1428 BOOLEAN
1429 NTAPI
CsrValidateMessageBuffer(IN PCSR_API_MESSAGE ApiMessage,IN PVOID * Buffer,IN ULONG ElementCount,IN ULONG ElementSize)1430 CsrValidateMessageBuffer(IN PCSR_API_MESSAGE ApiMessage,
1431                          IN PVOID *Buffer,
1432                          IN ULONG ElementCount,
1433                          IN ULONG ElementSize)
1434 {
1435     PCSR_CAPTURE_BUFFER CaptureBuffer = ApiMessage->CsrCaptureData;
1436     SIZE_T BufferDistance = (ULONG_PTR)Buffer - (ULONG_PTR)ApiMessage;
1437     ULONG PointerCount;
1438     PULONG_PTR OffsetPointer;
1439 
1440     /*
1441      * Check whether we have a valid buffer pointer, elements
1442      * of non-trivial size and that we don't overflow.
1443      */
1444     if (!Buffer || ElementSize == 0 ||
1445         (ULONGLONG)ElementCount * ElementSize > (ULONGLONG)MAXULONG)
1446     {
1447         return FALSE;
1448     }
1449 
1450     /* Check if didn't get a buffer and there aren't any arguments to check */
1451     // if (!*Buffer && (ElementCount * ElementSize == 0))
1452     if (!*Buffer && ElementCount == 0) // Here ElementSize != 0 therefore only ElementCount can be == 0
1453         return TRUE;
1454 
1455     /* Check if we have no capture buffer */
1456     if (!CaptureBuffer)
1457     {
1458         /* In this case, succeed only if the caller is CSRSS */
1459         if (NtCurrentTeb()->ClientId.UniqueProcess ==
1460             ApiMessage->Header.ClientId.UniqueProcess)
1461         {
1462             return TRUE;
1463         }
1464     }
1465     else
1466     {
1467         /* Make sure that there is still space left in the capture buffer */
1468         if ((CaptureBuffer->Size - (ULONG_PTR)*Buffer + (ULONG_PTR)CaptureBuffer) >=
1469             (ElementCount * ElementSize))
1470         {
1471             /* Perform the validation test */
1472             PointerCount  = CaptureBuffer->PointerCount;
1473             OffsetPointer = CaptureBuffer->PointerOffsetsArray;
1474             while (PointerCount--)
1475             {
1476                 /*
1477                  * Find in the array, the pointer offset (from the
1478                  * API message) that corresponds to the buffer.
1479                  */
1480                 if (*OffsetPointer == BufferDistance)
1481                 {
1482                     return TRUE;
1483                 }
1484                 ++OffsetPointer;
1485             }
1486         }
1487     }
1488 
1489     /* Failure */
1490 #ifdef CSR_DBG
1491     DPRINT1("CSRSRV: Bad message buffer %p\n", ApiMessage);
1492     if (NtCurrentPeb()->BeingDebugged) DbgBreakPoint();
1493 #endif
1494     return FALSE;
1495 }
1496 
1497 /*++
1498  * @name CsrValidateMessageString
1499  * @implemented NT5.1
1500  *
1501  * The CsrValidateMessageString validates a captured Wide-Character String
1502  * present in a CSR API Message.
1503  *
1504  * @param ApiMessage
1505  *        Pointer to the CSR API Message containing the CSR Capture Buffer.
1506  *
1507  * @param MessageString
1508  *        Pointer to the buffer containing the string to validate.
1509  *
1510  * @return TRUE if validation succeeded, FALSE otherwise.
1511  *
1512  * @remarks None.
1513  *
1514  *--*/
1515 BOOLEAN
1516 NTAPI
CsrValidateMessageString(IN PCSR_API_MESSAGE ApiMessage,IN PWSTR * MessageString)1517 CsrValidateMessageString(IN PCSR_API_MESSAGE ApiMessage,
1518                          IN PWSTR *MessageString)
1519 {
1520     if (MessageString)
1521     {
1522         return CsrValidateMessageBuffer(ApiMessage,
1523                                         (PVOID*)MessageString,
1524                                         wcslen(*MessageString) + 1,
1525                                         sizeof(WCHAR));
1526     }
1527     else
1528     {
1529         return FALSE;
1530     }
1531 }
1532 
1533 /* EOF */
1534