xref: /reactos/subsystems/csr/csrsrv/procsup.c (revision 565bf9e3)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Client/Server Runtime SubSystem
4  * FILE:            subsystems/win32/csrsrv/procsup.c
5  * PURPOSE:         CSR Server DLL Process Management
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Alex Ionescu (alex@relsoft.net)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <srv.h>
13 
14 #include <winuser.h>
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 RTL_CRITICAL_SECTION CsrProcessLock;
22 PCSR_PROCESS CsrRootProcess = NULL;
23 SECURITY_QUALITY_OF_SERVICE CsrSecurityQos =
24 {
25     sizeof(SECURITY_QUALITY_OF_SERVICE),
26     SecurityImpersonation,
27     SECURITY_STATIC_TRACKING,
28     FALSE
29 };
30 ULONG CsrProcessSequenceCount = 5;
31 extern ULONG CsrTotalPerProcessDataLength;
32 
33 
34 /* PRIVATE FUNCTIONS **********************************************************/
35 
36 /*++
37  * @name CsrSetToNormalPriority
38  *
39  * The CsrSetToNormalPriority routine sets the current NT Process'
40  * priority to the normal priority for CSR Processes.
41  *
42  * @param None.
43  *
44  * @return None.
45  *
46  * @remarks The "Normal" Priority corresponds to the Normal Foreground
47  *          Priority (9) plus a boost of 4.
48  *
49  *--*/
50 VOID
51 NTAPI
52 CsrSetToNormalPriority(VOID)
53 {
54     KPRIORITY BasePriority = PROCESS_PRIORITY_NORMAL_FOREGROUND + 4;
55 
56     /* Set the base priority */
57     NtSetInformationProcess(NtCurrentProcess(),
58                             ProcessBasePriority,
59                             &BasePriority,
60                             sizeof(BasePriority));
61 }
62 
63 /*++
64  * @name CsrSetToShutdownPriority
65  *
66  * The CsrSetToShutdownPriority routine sets the current NT Process'
67  * priority to the boosted priority for CSR Processes doing shutdown,
68  * acquiring also the required Increase Base Priority privilege.
69  *
70  * @param None.
71  *
72  * @return None.
73  *
74  * @remarks The "Shutdown" Priority corresponds to the Normal Foreground
75  *          Priority (9) plus a boost of 6.
76  *
77  *--*/
78 VOID
79 NTAPI
80 CsrSetToShutdownPriority(VOID)
81 {
82     KPRIORITY BasePriority = PROCESS_PRIORITY_NORMAL_FOREGROUND + 6;
83     BOOLEAN Old;
84 
85     /* Get the Increase Base Priority privilege */
86     if (NT_SUCCESS(RtlAdjustPrivilege(SE_INC_BASE_PRIORITY_PRIVILEGE,
87                                       TRUE,
88                                       FALSE,
89                                       &Old)))
90     {
91         /* Set the base priority */
92         NtSetInformationProcess(NtCurrentProcess(),
93                                 ProcessBasePriority,
94                                 &BasePriority,
95                                 sizeof(BasePriority));
96     }
97 }
98 
99 /*++
100  * @name CsrProcessRefcountZero
101  *
102  * The CsrProcessRefcountZero routine is executed when a CSR Process has lost
103  * all its active references. It removes and de-allocates the CSR Process.
104  *
105  * @param CsrProcess
106  *        Pointer to the CSR Process that is to be deleted.
107  *
108  * @return None.
109  *
110  * @remarks Do not call this routine. It is reserved for the internal
111  *          thread management routines when a CSR Process has lost all
112  *          its references.
113  *
114  *          This routine is called with the Process Lock held.
115  *
116  *--*/
117 VOID
118 NTAPI
119 CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess)
120 {
121     ASSERT(ProcessStructureListLocked());
122 
123     /* Remove the Process from the list */
124     CsrRemoveProcess(CsrProcess);
125 
126     /* Check if there's a session */
127     if (CsrProcess->NtSession)
128     {
129         /* Dereference the Session */
130         CsrDereferenceNtSession(CsrProcess->NtSession, STATUS_SUCCESS);
131     }
132 
133     /* Close the Client Port if there is one */
134     if (CsrProcess->ClientPort) NtClose(CsrProcess->ClientPort);
135 
136     /* Close the process handle */
137     NtClose(CsrProcess->ProcessHandle);
138 
139     /* Free the Process Object */
140     CsrDeallocateProcess(CsrProcess);
141 }
142 
143 /*++
144  * @name CsrLockedDereferenceProcess
145  *
146  * The CsrLockedDereferenceProcess dereferences a CSR Process while the
147  * Process Lock is already being held.
148  *
149  * @param CsrProcess
150  *        Pointer to the CSR Process to be dereferenced.
151  *
152  * @return None.
153  *
154  * @remarks This routine will return with the Process Lock held.
155  *
156  *--*/
157 VOID
158 NTAPI
159 CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess)
160 {
161     LONG LockCount;
162 
163     /* Decrease reference count */
164     LockCount = --CsrProcess->ReferenceCount;
165     ASSERT(LockCount >= 0);
166     if (LockCount == 0)
167     {
168         /* Call the generic cleanup code */
169         DPRINT1("Should kill process: %p\n", CsrProcess);
170         CsrProcessRefcountZero(CsrProcess);
171         /* Acquire the lock again, it was released in CsrProcessRefcountZero */
172         CsrAcquireProcessLock();
173     }
174 }
175 
176 /*++
177  * @name CsrAllocateProcess
178  * @implemented NT4
179  *
180  * The CsrAllocateProcess routine allocates a new CSR Process object.
181  *
182  * @return Pointer to the newly allocated CSR Process.
183  *
184  * @remarks None.
185  *
186  *--*/
187 PCSR_PROCESS
188 NTAPI
189 CsrAllocateProcess(VOID)
190 {
191     PCSR_PROCESS CsrProcess;
192     ULONG TotalSize;
193 
194     /* Calculate the amount of memory this should take */
195     TotalSize = sizeof(CSR_PROCESS) +
196                 (CSR_SERVER_DLL_MAX * sizeof(PVOID)) +
197                 CsrTotalPerProcessDataLength;
198 
199     /* Allocate a Process */
200     CsrProcess = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, TotalSize);
201     if (!CsrProcess) return NULL;
202 
203     /* Handle the Sequence Number and protect against overflow */
204     CsrProcess->SequenceNumber = CsrProcessSequenceCount++;
205     if (CsrProcessSequenceCount < 5) CsrProcessSequenceCount = 5;
206 
207     /* Increase the reference count */
208     CsrLockedReferenceProcess(CsrProcess);
209 
210     /* Initialize the Thread List */
211     InitializeListHead(&CsrProcess->ThreadList);
212 
213     /* Return the Process */
214     return CsrProcess;
215 }
216 
217 /*++
218  * @name CsrLockedReferenceProcess
219  *
220  * The CsrLockedReferenceProcess references a CSR Process while the
221  * Process Lock is already being held.
222  *
223  * @param CsrProcess
224  *        Pointer to the CSR Process to be referenced.
225  *
226  * @return None.
227  *
228  * @remarks This routine will return with the Process Lock held.
229  *
230  *--*/
231 VOID
232 NTAPI
233 CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess)
234 {
235     /* Increment the reference count */
236     ++CsrProcess->ReferenceCount;
237 }
238 
239 /*++
240  * @name CsrInitializeProcessStructure
241  * @implemented NT4
242  *
243  * The CsrInitializeProcessStructure routine sets up support for CSR Processes
244  * and CSR Threads by initializing our own CSR Root Process.
245  *
246  * @param None.
247  *
248  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
249  *
250  * @remarks None.
251  *
252  *--*/
253 NTSTATUS
254 NTAPI
255 CsrInitializeProcessStructure(VOID)
256 {
257     NTSTATUS Status;
258     ULONG i;
259 
260     /* Initialize the Lock */
261     Status = RtlInitializeCriticalSection(&CsrProcessLock);
262     if (!NT_SUCCESS(Status)) return Status;
263 
264     /* Set up the Root Process */
265     CsrRootProcess = CsrAllocateProcess();
266     if (!CsrRootProcess) return STATUS_NO_MEMORY;
267 
268     /* Set up the minimal information for it */
269     InitializeListHead(&CsrRootProcess->ListLink);
270     CsrRootProcess->ProcessHandle = (HANDLE)-1;
271     CsrRootProcess->ClientId = NtCurrentTeb()->ClientId;
272 
273     /* Initialize the Thread Hash List */
274     for (i = 0; i < NUMBER_THREAD_HASH_BUCKETS; i++) InitializeListHead(&CsrThreadHashTable[i]);
275 
276     /* Initialize the Wait Lock */
277     return RtlInitializeCriticalSection(&CsrWaitListsLock);
278 }
279 
280 /*++
281  * @name CsrDeallocateProcess
282  *
283  * The CsrDeallocateProcess frees the memory associated with a CSR Process.
284  *
285  * @param CsrProcess
286  *        Pointer to the CSR Process to be freed.
287  *
288  * @return None.
289  *
290  * @remarks Do not call this routine. It is reserved for the internal
291  *          thread management routines when a CSR Process has been cleanly
292  *          dereferenced and killed.
293  *
294  *--*/
295 VOID
296 NTAPI
297 CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess)
298 {
299     /* Free the process object from the heap */
300     RtlFreeHeap(CsrHeap, 0, CsrProcess);
301 }
302 
303 /*++
304  * @name CsrRemoveProcess
305  *
306  * The CsrRemoveProcess function undoes a CsrInsertProcess operation and
307  * removes the CSR Process from the Process List and notifies Server DLLs
308  * of this removal.
309  *
310  * @param CsrProcess
311  *        Pointer to the CSR Process to remove.
312  *
313  * @return None.
314  *
315  * @remarks None.
316  *
317  *--*/
318 VOID
319 NTAPI
320 CsrRemoveProcess(IN PCSR_PROCESS CsrProcess)
321 {
322     PCSR_SERVER_DLL ServerDll;
323     ULONG i;
324     ASSERT(ProcessStructureListLocked());
325 
326     /* Remove us from the Process List */
327     RemoveEntryList(&CsrProcess->ListLink);
328 
329     /* Release the lock */
330     CsrReleaseProcessLock();
331 
332     /* Loop every Server DLL */
333     for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
334     {
335         /* Get the Server DLL */
336         ServerDll = CsrLoadedServerDll[i];
337 
338         /* Check if it's valid and if it has a Disconnect Callback */
339         if (ServerDll && ServerDll->DisconnectCallback)
340         {
341             /* Call it */
342             ServerDll->DisconnectCallback(CsrProcess);
343         }
344     }
345 }
346 
347 /*++
348  * @name CsrInsertProcess
349  *
350  * The CsrInsertProcess routine inserts a CSR Process into the Process List
351  * and notifies Server DLLs of the creation of a new CSR Process.
352  *
353  * @param ParentProcess
354  *        Optional pointer to the Parent Process creating this CSR Process.
355  *
356  * @param CsrProcess
357  *        Pointer to the CSR Process which is to be inserted.
358  *
359  * @return None.
360  *
361  * @remarks None.
362  *
363  *--*/
364 VOID
365 NTAPI
366 CsrInsertProcess(IN PCSR_PROCESS ParentProcess OPTIONAL,
367                  IN PCSR_PROCESS CsrProcess)
368 {
369     PCSR_SERVER_DLL ServerDll;
370     ULONG i;
371     ASSERT(ProcessStructureListLocked());
372 
373     /* Insert it into the Root List */
374     InsertTailList(&CsrRootProcess->ListLink, &CsrProcess->ListLink);
375 
376     /* Notify the Server DLLs */
377     for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
378     {
379         /* Get the current Server DLL */
380         ServerDll = CsrLoadedServerDll[i];
381 
382         /* Make sure it's valid and that it has callback */
383         if (ServerDll && ServerDll->NewProcessCallback)
384         {
385             ServerDll->NewProcessCallback(ParentProcess, CsrProcess);
386         }
387     }
388 }
389 
390 
391 /* PUBLIC FUNCTIONS ***********************************************************/
392 
393 /*++
394  * @name CsrCreateProcess
395  * @implemented NT4
396  *
397  * The CsrCreateProcess routine creates a CSR Process object for an NT Process.
398  *
399  * @param hProcess
400  *        Handle to an existing NT Process to which to associate this
401  *        CSR Process.
402  *
403  * @param hThread
404  *        Handle to an existing NT Thread to which to create its
405  *        corresponding CSR Thread for this CSR Process.
406  *
407  * @param ClientId
408  *        Pointer to the Client ID structure of the NT Process to associate
409  *        with this CSR Process.
410  *
411  * @param NtSession
412  * @param Flags
413  * @param DebugCid
414  *
415  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
416  *
417  * @remarks None.
418  *
419  *--*/
420 NTSTATUS
421 NTAPI
422 CsrCreateProcess(IN HANDLE hProcess,
423                  IN HANDLE hThread,
424                  IN PCLIENT_ID ClientId,
425                  IN PCSR_NT_SESSION NtSession,
426                  IN ULONG Flags,
427                  IN PCLIENT_ID DebugCid)
428 {
429     PCSR_THREAD CurrentThread = CsrGetClientThread();
430     CLIENT_ID CurrentCid;
431     PCSR_PROCESS CurrentProcess;
432     PCSR_SERVER_DLL ServerDll;
433     PVOID ProcessData;
434     ULONG i;
435     PCSR_PROCESS CsrProcess;
436     NTSTATUS Status;
437     PCSR_THREAD CsrThread;
438     KERNEL_USER_TIMES KernelTimes;
439 
440     /* Get the current CID and lock Processes */
441     CurrentCid = CurrentThread->ClientId;
442     CsrAcquireProcessLock();
443 
444     /* Get the current CSR Thread */
445     CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid);
446     if (!CurrentThread)
447     {
448         /* We've failed to locate the thread */
449         CsrReleaseProcessLock();
450         return STATUS_THREAD_IS_TERMINATING;
451     }
452 
453     /* Allocate a new Process Object */
454     CsrProcess = CsrAllocateProcess();
455     if (!CsrProcess)
456     {
457         /* Couldn't allocate Process */
458         CsrReleaseProcessLock();
459         return STATUS_NO_MEMORY;
460     }
461 
462     /* Inherit the Process Data */
463     CurrentProcess = CurrentThread->Process;
464     ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
465     for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
466     {
467         /* Get the current Server */
468         ServerDll = CsrLoadedServerDll[i];
469 
470         /* Check if the DLL is Loaded and has Per Process Data */
471         if (ServerDll && ServerDll->SizeOfProcessData)
472         {
473             /* Set the pointer */
474             CsrProcess->ServerData[i] = ProcessData;
475 
476             /* Copy the Data */
477             RtlMoveMemory(ProcessData,
478                           CurrentProcess->ServerData[i],
479                           ServerDll->SizeOfProcessData);
480 
481             /* Update next data pointer */
482             ProcessData = (PVOID)((ULONG_PTR)ProcessData +
483                                   ServerDll->SizeOfProcessData);
484         }
485         else
486         {
487             /* No data for this Server */
488             CsrProcess->ServerData[i] = NULL;
489         }
490     }
491 
492     /* Set the Exception Port for us */
493     Status = NtSetInformationProcess(hProcess,
494                                      ProcessExceptionPort,
495                                      &CsrApiPort,
496                                      sizeof(CsrApiPort));
497     if (!NT_SUCCESS(Status))
498     {
499         /* Failed */
500         CsrDeallocateProcess(CsrProcess);
501         CsrReleaseProcessLock();
502         return STATUS_NO_MEMORY;
503     }
504 
505     /* Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */
506     if (Flags & CsrProcessCreateNewGroup)
507     {
508         /*
509          * We create the process group leader of a new process group, therefore
510          * its process group ID and sequence number are its own ones.
511          */
512         CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess);
513         CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber;
514     }
515     else
516     {
517         /* Inherit the process group ID and sequence number from the current process */
518         CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId;
519         CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence;
520     }
521 
522     /* Check if this is a console process */
523     if (Flags & CsrProcessIsConsoleApp) CsrProcess->Flags |= CsrProcessIsConsoleApp;
524 
525     /* Mask out non-debug flags */
526     Flags &= ~(CsrProcessIsConsoleApp | CsrProcessCreateNewGroup | CsrProcessPriorityFlags);
527 
528     /* Check if every process will be debugged */
529     if (!(Flags) && (CurrentProcess->DebugFlags & CsrDebugProcessChildren))
530     {
531         /* Pass it on to the current process */
532         CsrProcess->DebugFlags = CsrDebugProcessChildren;
533         CsrProcess->DebugCid = CurrentProcess->DebugCid;
534     }
535 
536     /* Check if Debugging was used on this process */
537     if ((Flags & (CsrDebugOnlyThisProcess | CsrDebugProcessChildren)) && (DebugCid))
538     {
539         /* Save the debug flag used */
540         CsrProcess->DebugFlags = Flags;
541 
542         /* Save the CID */
543         CsrProcess->DebugCid = *DebugCid;
544     }
545 
546     /* Check if Debugging is enabled */
547     if (CsrProcess->DebugFlags)
548     {
549         /* Set the Debug Port for us */
550         Status = NtSetInformationProcess(hProcess,
551                                          ProcessDebugPort,
552                                          &CsrApiPort,
553                                          sizeof(CsrApiPort));
554         ASSERT(NT_SUCCESS(Status));
555         if (!NT_SUCCESS(Status))
556         {
557             /* Failed */
558             CsrDeallocateProcess(CsrProcess);
559             CsrReleaseProcessLock();
560             return STATUS_NO_MEMORY;
561         }
562     }
563 
564     /* Get the Thread Create Time */
565     Status = NtQueryInformationThread(hThread,
566                                       ThreadTimes,
567                                       &KernelTimes,
568                                       sizeof(KernelTimes),
569                                       NULL);
570     if (!NT_SUCCESS(Status))
571     {
572         /* Failed */
573         CsrDeallocateProcess(CsrProcess);
574         CsrReleaseProcessLock();
575         return STATUS_NO_MEMORY;
576     }
577 
578     /* Allocate a CSR Thread Structure */
579     CsrThread = CsrAllocateThread(CsrProcess);
580     if (!CsrThread)
581     {
582         /* Failed */
583         CsrDeallocateProcess(CsrProcess);
584         CsrReleaseProcessLock();
585         return STATUS_NO_MEMORY;
586     }
587 
588     /* Save the data we have */
589     CsrThread->CreateTime = KernelTimes.CreateTime;
590     CsrThread->ClientId = *ClientId;
591     CsrThread->ThreadHandle = hThread;
592     ProtectHandle(hThread);
593     CsrThread->Flags = 0;
594 
595     /* Insert the Thread into the Process */
596     Status = CsrInsertThread(CsrProcess, CsrThread);
597     if (!NT_SUCCESS(Status))
598     {
599         /* Bail out */
600         CsrDeallocateProcess(CsrProcess);
601         CsrDeallocateThread(CsrThread);
602         CsrReleaseProcessLock();
603         return Status;
604     }
605 
606     /* Reference the session */
607     CsrReferenceNtSession(NtSession);
608     CsrProcess->NtSession = NtSession;
609 
610     /* Setup Process Data */
611     CsrProcess->ClientId = *ClientId;
612     CsrProcess->ProcessHandle = hProcess;
613     CsrProcess->ShutdownLevel = 0x280;
614 
615     /* Set the priority to Background */
616     CsrSetBackgroundPriority(CsrProcess);
617 
618     /* Insert the Process */
619     CsrInsertProcess(CurrentProcess, CsrProcess);
620 
621     /* Release lock and return */
622     CsrReleaseProcessLock();
623     return Status;
624 }
625 
626 /*++
627  * @name CsrDebugProcess
628  * @implemented NT4
629  *
630  * The CsrDebugProcess routine is deprecated in NT 5.1 and higher. It is
631  * exported only for compatibility with older CSR Server DLLs.
632  *
633  * @param CsrProcess
634  *        Deprecated.
635  *
636  * @return Deprecated
637  *
638  * @remarks Deprecated.
639  *
640  *--*/
641 NTSTATUS
642 NTAPI
643 CsrDebugProcess(IN PCSR_PROCESS CsrProcess)
644 {
645     /* CSR does not handle debugging anymore */
646     DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
647     return STATUS_UNSUCCESSFUL;
648 }
649 
650 /*++
651  * @name CsrDebugProcessStop
652  * @implemented NT4
653  *
654  * The CsrDebugProcessStop routine is deprecated in NT 5.1 and higher. It is
655  * exported only for compatibility with older CSR Server DLLs.
656  *
657  * @param CsrProcess
658  *        Deprecated.
659  *
660  * @return Deprecated
661  *
662  * @remarks Deprecated.
663  *
664  *--*/
665 NTSTATUS
666 NTAPI
667 CsrDebugProcessStop(IN PCSR_PROCESS CsrProcess)
668 {
669     /* CSR does not handle debugging anymore */
670     DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
671     return STATUS_UNSUCCESSFUL;
672 }
673 
674 /*++
675  * @name CsrDereferenceProcess
676  * @implemented NT4
677  *
678  * The CsrDereferenceProcess routine removes a reference from a CSR Process.
679  *
680  * @param CsrThread
681  *        Pointer to the CSR Process to dereference.
682  *
683  * @return None.
684  *
685  * @remarks If the reference count has reached zero (ie: the CSR Process has
686  *          no more active references), it will be deleted.
687  *
688  *--*/
689 VOID
690 NTAPI
691 CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess)
692 {
693     LONG LockCount;
694 
695     /* Acquire process lock */
696     CsrAcquireProcessLock();
697 
698     /* Decrease reference count */
699     LockCount = --CsrProcess->ReferenceCount;
700     ASSERT(LockCount >= 0);
701     if (LockCount == 0)
702     {
703         /* Call the generic cleanup code */
704         CsrProcessRefcountZero(CsrProcess);
705     }
706     else
707     {
708         /* Just release the lock */
709         CsrReleaseProcessLock();
710     }
711 }
712 
713 /*++
714  * @name CsrDestroyProcess
715  * @implemented NT4
716  *
717  * The CsrDestroyProcess routine destroys the CSR Process corresponding to
718  * a given Client ID.
719  *
720  * @param Cid
721  *        Pointer to the Client ID Structure corresponding to the CSR
722  *        Process which is about to be destroyed.
723  *
724  * @param ExitStatus
725  *        Unused.
726  *
727  * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
728  *         if the CSR Process is already terminating.
729  *
730  * @remarks None.
731  *
732  *--*/
733 NTSTATUS
734 NTAPI
735 CsrDestroyProcess(IN PCLIENT_ID Cid,
736                   IN NTSTATUS ExitStatus)
737 {
738     PCSR_THREAD CsrThread;
739     PCSR_PROCESS CsrProcess;
740     CLIENT_ID ClientId = *Cid;
741     PLIST_ENTRY NextEntry;
742 
743     /* Acquire lock */
744     CsrAcquireProcessLock();
745 
746     /* Find the thread */
747     CsrThread = CsrLocateThreadByClientId(&CsrProcess, &ClientId);
748 
749     /* Make sure we got one back, and that it's not already gone */
750     if (!(CsrThread) || (CsrProcess->Flags & CsrProcessTerminating))
751     {
752         /* Release the lock and return failure */
753         CsrReleaseProcessLock();
754         return STATUS_THREAD_IS_TERMINATING;
755     }
756 
757     /* Set the terminated flag */
758     CsrProcess->Flags |= CsrProcessTerminating;
759 
760     /* Get the List Pointers */
761     NextEntry = CsrProcess->ThreadList.Flink;
762     while (NextEntry != &CsrProcess->ThreadList)
763     {
764         /* Get the current thread entry */
765         CsrThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
766 
767         /* Move to the next entry */
768         NextEntry = NextEntry->Flink;
769 
770         /* Make sure the thread isn't already dead */
771         if (CsrThread->Flags & CsrThreadTerminated)
772         {
773             /* Go the the next thread */
774             continue;
775         }
776 
777         /* Set the Terminated flag */
778         CsrThread->Flags |= CsrThreadTerminated;
779 
780         /* Acquire the Wait Lock */
781         CsrAcquireWaitLock();
782 
783         /* Do we have an active wait block? */
784         if (CsrThread->WaitBlock)
785         {
786             /* Notify waiters of termination */
787             CsrNotifyWaitBlock(CsrThread->WaitBlock,
788                                NULL,
789                                NULL,
790                                NULL,
791                                CsrProcessTerminating,
792                                TRUE);
793         }
794 
795         /* Release the Wait Lock */
796         CsrReleaseWaitLock();
797 
798         /* Dereference the thread */
799         CsrLockedDereferenceThread(CsrThread);
800     }
801 
802     /* Release the Process Lock and return success */
803     CsrReleaseProcessLock();
804     return STATUS_SUCCESS;
805 }
806 
807 /*++
808  * @name CsrGetProcessLuid
809  * @implemented NT4
810  *
811  * The CsrGetProcessLuid routine gets the LUID of the given process.
812  *
813  * @param hProcess
814  *        Optional handle to the process whose LUID should be returned.
815  *
816  * @param Luid
817  *        Pointer to a LUID Pointer which will receive the CSR Process' LUID.
818  *
819  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
820  *
821  * @remarks If hProcess is not supplied, then the current thread's token will
822  *          be used. If that too is missing, then the current process' token
823  *          will be used.
824  *
825  *--*/
826 NTSTATUS
827 NTAPI
828 CsrGetProcessLuid(IN HANDLE hProcess OPTIONAL,
829                   OUT PLUID Luid)
830 {
831     HANDLE hToken = NULL;
832     NTSTATUS Status;
833     ULONG Length;
834     PTOKEN_STATISTICS TokenStats;
835 
836     /* Check if we have a handle to a CSR Process */
837     if (!hProcess)
838     {
839         /* We don't, so try opening the Thread's Token */
840         Status = NtOpenThreadToken(NtCurrentThread(),
841                                    TOKEN_QUERY,
842                                    FALSE,
843                                    &hToken);
844 
845         /* Check for success */
846         if (!NT_SUCCESS(Status))
847         {
848             /* If we got some other failure, then return and quit */
849             if (Status != STATUS_NO_TOKEN) return Status;
850 
851             /* We don't have a Thread Token, use a Process Token */
852             hProcess = NtCurrentProcess();
853             hToken = NULL;
854         }
855     }
856 
857     /* Check if we have a token by now */
858     if (!hToken)
859     {
860         /* No token yet, so open the Process Token */
861         Status = NtOpenProcessToken(hProcess,
862                                     TOKEN_QUERY,
863                                     &hToken);
864         if (!NT_SUCCESS(Status))
865         {
866             /* Still no token, return the error */
867             return Status;
868         }
869     }
870 
871     /* Now get the size we'll need for the Token Information */
872     Status = NtQueryInformationToken(hToken,
873                                      TokenStatistics,
874                                      NULL,
875                                      0,
876                                      &Length);
877     if (Status != STATUS_BUFFER_TOO_SMALL)
878     {
879         /* Close the token and fail */
880         NtClose(hToken);
881         return Status;
882     }
883 
884     /* Allocate memory for the Token Info */
885     if (!(TokenStats = RtlAllocateHeap(CsrHeap, 0, Length)))
886     {
887         /* Fail and close the token */
888         NtClose(hToken);
889         return STATUS_NO_MEMORY;
890     }
891 
892     /* Now query the information */
893     Status = NtQueryInformationToken(hToken,
894                                      TokenStatistics,
895                                      TokenStats,
896                                      Length,
897                                      &Length);
898 
899     /* Close the handle */
900     NtClose(hToken);
901 
902     /* Check for success */
903     if (NT_SUCCESS(Status))
904     {
905         /* Return the LUID */
906         *Luid = TokenStats->AuthenticationId;
907     }
908 
909     /* Free the query information */
910     RtlFreeHeap(CsrHeap, 0, TokenStats);
911 
912     /* Return the Status */
913     return Status;
914 }
915 
916 /*++
917  * @name CsrImpersonateClient
918  * @implemented NT4
919  *
920  * The CsrImpersonateClient will impersonate the given CSR Thread.
921  *
922  * @param CsrThread
923  *        Pointer to the CSR Thread to impersonate.
924  *
925  * @return TRUE if impersonation succeeded, FALSE otherwise.
926  *
927  * @remarks Impersonation can be recursive.
928  *
929  *--*/
930 BOOLEAN
931 NTAPI
932 CsrImpersonateClient(IN PCSR_THREAD CsrThread)
933 {
934     NTSTATUS Status;
935     PCSR_THREAD CurrentThread = CsrGetClientThread();
936 
937     /* Use the current thread if none given */
938     if (!CsrThread) CsrThread = CurrentThread;
939 
940     /* Still no thread, something is wrong */
941     if (!CsrThread)
942     {
943         /* Failure */
944         return FALSE;
945     }
946 
947     /* Make the call */
948     Status = NtImpersonateThread(NtCurrentThread(),
949                                  CsrThread->ThreadHandle,
950                                  &CsrSecurityQos);
951 
952     if (!NT_SUCCESS(Status))
953     {
954         /* Failure */
955 #ifdef CSR_DBG
956         DPRINT1("CSRSS: Can't impersonate client thread - Status = %lx\n", Status);
957         // if (Status != STATUS_BAD_IMPERSONATION_LEVEL) DbgBreakPoint();
958 #endif
959         return FALSE;
960     }
961 
962     /* Increase the impersonation count for the current thread */
963     if (CurrentThread) ++CurrentThread->ImpersonationCount;
964 
965     /* Return Success */
966     return TRUE;
967 }
968 
969 /*++
970  * @name CsrLockProcessByClientId
971  * @implemented NT4
972  *
973  * The CsrLockProcessByClientId routine locks the CSR Process corresponding
974  * to the given Process ID and optionally returns it.
975  *
976  * @param Pid
977  *        Process ID corresponding to the CSR Process which will be locked.
978  *
979  * @param CsrProcess
980  *        Optional pointer to a CSR Process pointer which will hold the
981  *        CSR Process corresponding to the given Process ID.
982  *
983  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
984  *
985  * @remarks Locking a CSR Process is defined as acquiring an extra
986  *          reference to it and returning with the Process Lock held.
987  *
988  *--*/
989 NTSTATUS
990 NTAPI
991 CsrLockProcessByClientId(IN HANDLE Pid,
992                          OUT PCSR_PROCESS *CsrProcess)
993 {
994     PLIST_ENTRY NextEntry;
995     PCSR_PROCESS CurrentProcess = NULL;
996     NTSTATUS Status = STATUS_UNSUCCESSFUL;
997 
998     /* Acquire the lock */
999     CsrAcquireProcessLock();
1000 
1001     /* Assume failure */
1002     ASSERT(CsrProcess != NULL);
1003     *CsrProcess = NULL;
1004 
1005     /* Setup the List Pointers */
1006     NextEntry = &CsrRootProcess->ListLink;
1007     do
1008     {
1009         /* Get the Process */
1010         CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
1011 
1012         /* Check for PID Match */
1013         if (CurrentProcess->ClientId.UniqueProcess == Pid)
1014         {
1015             Status = STATUS_SUCCESS;
1016             break;
1017         }
1018 
1019         /* Move to the next entry */
1020         NextEntry = NextEntry->Flink;
1021     } while (NextEntry != &CsrRootProcess->ListLink);
1022 
1023     /* Check if we didn't find it in the list */
1024     if (!NT_SUCCESS(Status))
1025     {
1026         /* Nothing found, release the lock */
1027         CsrReleaseProcessLock();
1028     }
1029     else
1030     {
1031         /* Lock the found process and return it */
1032         CsrLockedReferenceProcess(CurrentProcess);
1033         *CsrProcess = CurrentProcess;
1034     }
1035 
1036     /* Return the result */
1037     return Status;
1038 }
1039 
1040 /*++
1041  * @name CsrRevertToSelf
1042  * @implemented NT4
1043  *
1044  * The CsrRevertToSelf routine will attempt to remove an active impersonation.
1045  *
1046  * @param None.
1047  *
1048  * @return TRUE if the reversion was succesful, FALSE otherwise.
1049  *
1050  * @remarks Impersonation can be recursive; as such, the impersonation token
1051  *          will only be deleted once the CSR Thread's impersonation count
1052  *          has reached zero.
1053  *
1054  *--*/
1055 BOOLEAN
1056 NTAPI
1057 CsrRevertToSelf(VOID)
1058 {
1059     NTSTATUS Status;
1060     PCSR_THREAD CurrentThread = CsrGetClientThread();
1061     HANDLE ImpersonationToken = NULL;
1062 
1063     /* Check if we have a Current Thread */
1064     if (CurrentThread)
1065     {
1066         /* Make sure impersonation is on */
1067         if (!CurrentThread->ImpersonationCount)
1068         {
1069             DPRINT1("CSRSS: CsrRevertToSelf called while not impersonating\n");
1070             // DbgBreakPoint();
1071             return FALSE;
1072         }
1073         else if ((--CurrentThread->ImpersonationCount) > 0)
1074         {
1075             /* Success; impersonation count decreased but still not zero */
1076             return TRUE;
1077         }
1078     }
1079 
1080     /* Impersonation has been totally removed, revert to ourselves */
1081     Status = NtSetInformationThread(NtCurrentThread(),
1082                                     ThreadImpersonationToken,
1083                                     &ImpersonationToken,
1084                                     sizeof(ImpersonationToken));
1085 
1086     /* Return TRUE or FALSE */
1087     return NT_SUCCESS(Status);
1088 }
1089 
1090 /*++
1091  * @name CsrSetBackgroundPriority
1092  * @implemented NT4
1093  *
1094  * The CsrSetBackgroundPriority routine sets the priority for the given CSR
1095  * Process as a Background priority.
1096  *
1097  * @param CsrProcess
1098  *        Pointer to the CSR Process whose priority will be modified.
1099  *
1100  * @return None.
1101  *
1102  * @remarks None.
1103  *
1104  *--*/
1105 VOID
1106 NTAPI
1107 CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess)
1108 {
1109     PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
1110 
1111     /* Set the Foreground bit off */
1112     ProcessPriority.Foreground = FALSE;
1113 
1114     /* Set the new priority */
1115     NtSetInformationProcess(CsrProcess->ProcessHandle,
1116                             ProcessForegroundInformation,
1117                             &ProcessPriority,
1118                             sizeof(ProcessPriority));
1119 }
1120 
1121 /*++
1122  * @name CsrSetForegroundPriority
1123  * @implemented NT4
1124  *
1125  * The CsrSetForegroundPriority routine sets the priority for the given CSR
1126  * Process as a Foreground priority.
1127  *
1128  * @param CsrProcess
1129  *        Pointer to the CSR Process whose priority will be modified.
1130  *
1131  * @return None.
1132  *
1133  * @remarks None.
1134  *
1135  *--*/
1136 VOID
1137 NTAPI
1138 CsrSetForegroundPriority(IN PCSR_PROCESS CsrProcess)
1139 {
1140     PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
1141 
1142     /* Set the Foreground bit on */
1143     ProcessPriority.Foreground = TRUE;
1144 
1145     /* Set the new priority */
1146     NtSetInformationProcess(CsrProcess->ProcessHandle,
1147                             ProcessForegroundInformation,
1148                             &ProcessPriority,
1149                             sizeof(ProcessPriority));
1150 }
1151 
1152 /*++
1153  * @name FindProcessForShutdown
1154  *
1155  * The FindProcessForShutdown routine returns a CSR Process which is ready
1156  * to be shutdown, and sets the appropriate shutdown flags for it.
1157  *
1158  * @param CallerLuid
1159  *        Pointer to the LUID of the CSR Process calling this routine.
1160  *
1161  * @return Pointer to a CSR Process which is ready to be shutdown.
1162  *
1163  * @remarks None.
1164  *
1165  *--*/
1166 PCSR_PROCESS
1167 NTAPI
1168 FindProcessForShutdown(IN PLUID CallerLuid)
1169 {
1170     PCSR_PROCESS CsrProcess, ReturnCsrProcess = NULL;
1171     PCSR_THREAD CsrThread;
1172     NTSTATUS Status;
1173     ULONG Level = 0;
1174     LUID ProcessLuid;
1175     LUID SystemLuid = SYSTEM_LUID;
1176     PLIST_ENTRY NextEntry;
1177 
1178     /* Set the List Pointers */
1179     NextEntry = CsrRootProcess->ListLink.Flink;
1180     while (NextEntry != &CsrRootProcess->ListLink)
1181     {
1182         /* Get the process */
1183         CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
1184 
1185         /* Move to the next entry */
1186         NextEntry = NextEntry->Flink;
1187 
1188         /* Skip this process if it's already been processed */
1189         if (CsrProcess->Flags & CsrProcessSkipShutdown) continue;
1190 
1191         /* Get the LUID of this process */
1192         Status = CsrGetProcessLuid(CsrProcess->ProcessHandle, &ProcessLuid);
1193 
1194         /* Check if we didn't get access to the LUID */
1195         if (Status == STATUS_ACCESS_DENIED)
1196         {
1197             /* Check if we have any threads */
1198             if (CsrProcess->ThreadCount)
1199             {
1200                 /* Impersonate one of the threads and retry */
1201                 CsrThread = CONTAINING_RECORD(CsrProcess->ThreadList.Flink,
1202                                               CSR_THREAD,
1203                                               Link);
1204                 if (CsrImpersonateClient(CsrThread))
1205                 {
1206                     Status = CsrGetProcessLuid(NULL, &ProcessLuid);
1207                     CsrRevertToSelf();
1208                 }
1209                 else
1210                 {
1211                     Status = STATUS_BAD_IMPERSONATION_LEVEL;
1212                 }
1213             }
1214         }
1215 
1216         if (!NT_SUCCESS(Status))
1217         {
1218             /* We didn't have access, so skip it */
1219             CsrProcess->Flags |= CsrProcessSkipShutdown;
1220             continue;
1221         }
1222 
1223         /* Check if this is the System LUID */
1224         if (RtlEqualLuid(&ProcessLuid, &SystemLuid))
1225         {
1226             /* Mark this process */
1227             CsrProcess->ShutdownFlags |= CsrShutdownSystem;
1228         }
1229         else if (!RtlEqualLuid(&ProcessLuid, CallerLuid))
1230         {
1231             /* Our LUID doesn't match with the caller's */
1232             CsrProcess->ShutdownFlags |= CsrShutdownOther;
1233         }
1234 
1235         /* Check if we're past the previous level */
1236         if ((CsrProcess->ShutdownLevel > Level) || !ReturnCsrProcess)
1237         {
1238             /* Update the level */
1239             Level = CsrProcess->ShutdownLevel;
1240 
1241             /* Set the final process */
1242             ReturnCsrProcess = CsrProcess;
1243         }
1244     }
1245 
1246     /* Check if we found a process */
1247     if (ReturnCsrProcess)
1248     {
1249         /* Skip this one next time */
1250         ReturnCsrProcess->Flags |= CsrProcessSkipShutdown;
1251     }
1252 
1253     return ReturnCsrProcess;
1254 }
1255 
1256 /*++
1257  * @name CsrShutdownProcesses
1258  * @implemented NT4
1259  *
1260  * The CsrShutdownProcesses routine shuts down every CSR Process possible
1261  * and calls each Server DLL's shutdown notification.
1262  *
1263  * @param CallerLuid
1264  *        Pointer to the LUID of the CSR Process that is ordering the
1265  *        shutdown.
1266  *
1267  * @param Flags
1268  *        Flags to send to the shutdown notification routine.
1269  *
1270  * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
1271  *
1272  * @remarks None.
1273  *
1274  *--*/
1275 NTSTATUS
1276 NTAPI
1277 CsrShutdownProcesses(IN PLUID CallerLuid,
1278                      IN ULONG Flags)
1279 {
1280     PLIST_ENTRY NextEntry;
1281     PCSR_PROCESS CsrProcess;
1282     NTSTATUS Status;
1283     BOOLEAN FirstTry;
1284     ULONG i;
1285     PCSR_SERVER_DLL ServerDll;
1286     ULONG Result = 0;
1287 
1288     /* Acquire process lock */
1289     CsrAcquireProcessLock();
1290 
1291     /* Add shutdown flag */
1292     CsrRootProcess->ShutdownFlags |= CsrShutdownSystem;
1293 
1294     /* Get the list pointers */
1295     NextEntry = CsrRootProcess->ListLink.Flink;
1296     while (NextEntry != &CsrRootProcess->ListLink)
1297     {
1298         /* Get the Process */
1299         CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
1300 
1301         /* Move to the next entry */
1302         NextEntry = NextEntry->Flink;
1303 
1304         /* Remove the skip flag, set shutdown flags to 0 */
1305         CsrProcess->Flags &= ~CsrProcessSkipShutdown;
1306         CsrProcess->ShutdownFlags = 0;
1307     }
1308 
1309     /* Set shutdown Priority */
1310     CsrSetToShutdownPriority();
1311 
1312     /* Start looping */
1313     while (TRUE)
1314     {
1315         /* Find the next process to shutdown */
1316         CsrProcess = FindProcessForShutdown(CallerLuid);
1317         if (!CsrProcess) break;
1318 
1319         /* Increase reference to process */
1320         CsrLockedReferenceProcess(CsrProcess);
1321 
1322         FirstTry = TRUE;
1323         while (TRUE)
1324         {
1325             /* Loop all the servers */
1326             for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
1327             {
1328                 /* Get the current server */
1329                 ServerDll = CsrLoadedServerDll[i];
1330 
1331                 /* Check if it's valid and if it has a Shutdown Process Callback */
1332                 if (ServerDll && ServerDll->ShutdownProcessCallback)
1333                 {
1334                     /* Release the lock, make the callback, and acquire it back */
1335                     CsrReleaseProcessLock();
1336                     Result = ServerDll->ShutdownProcessCallback(CsrProcess,
1337                                                                 Flags,
1338                                                                 FirstTry);
1339                     CsrAcquireProcessLock();
1340 
1341                     /* Check the result */
1342                     if (Result == CsrShutdownCsrProcess)
1343                     {
1344                         /* The callback unlocked the process */
1345                         break;
1346                     }
1347                     else if (Result == CsrShutdownCancelled)
1348                     {
1349 #ifdef CSR_DBG
1350                         /* Check if this was a forced shutdown */
1351                         if (Flags & EWX_FORCE)
1352                         {
1353                             DPRINT1("Process %x cancelled forced shutdown (Dll = %d)\n",
1354                                      CsrProcess->ClientId.UniqueProcess, i);
1355                             DbgBreakPoint();
1356                         }
1357 #endif
1358 
1359                         /* Shutdown was cancelled, unlock and exit */
1360                         CsrReleaseProcessLock();
1361                         Status = STATUS_CANCELLED;
1362                         goto Quickie;
1363                     }
1364                 }
1365             }
1366 
1367             /* No matches during the first try, so loop again */
1368             if (FirstTry && (Result == CsrShutdownNonCsrProcess))
1369             {
1370                 FirstTry = FALSE;
1371                 continue;
1372             }
1373 
1374             /* Second try, break out */
1375             break;
1376         }
1377 
1378         /* We've reached the final loop here, so dereference */
1379         if (i == CSR_SERVER_DLL_MAX)
1380             CsrLockedDereferenceProcess(CsrProcess);
1381     }
1382 
1383     /* Success path */
1384     CsrReleaseProcessLock();
1385     Status = STATUS_SUCCESS;
1386 
1387 Quickie:
1388     /* Return to normal priority */
1389     CsrSetToNormalPriority();
1390 
1391     return Status;
1392 }
1393 
1394 /*++
1395  * @name CsrUnlockProcess
1396  * @implemented NT4
1397  *
1398  * The CsrUnlockProcess undoes a previous CsrLockProcessByClientId operation.
1399  *
1400  * @param CsrProcess
1401  *        Pointer to a previously locked CSR Process.
1402  *
1403  * @return STATUS_SUCCESS.
1404  *
1405  * @remarks This routine must be called with the Process Lock held.
1406  *
1407  *--*/
1408 NTSTATUS
1409 NTAPI
1410 CsrUnlockProcess(IN PCSR_PROCESS CsrProcess)
1411 {
1412     /* Dereference the process */
1413     CsrLockedDereferenceProcess(CsrProcess);
1414 
1415     /* Release the lock and return */
1416     CsrReleaseProcessLock();
1417     return STATUS_SUCCESS;
1418 }
1419 
1420 /* EOF */
1421