reactos/subsystems/win32/csrsrv/procsup.c
2021-03-23 18:35:13 +01:00

1420 lines
38 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Client/Server Runtime SubSystem
* FILE: subsystems/win32/csrsrv/procsup.c
* PURPOSE: CSR Server DLL Process Management
* PROGRAMMERS: ReactOS Portable Systems Group
* Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES *******************************************************************/
#include <srv.h>
#include <winuser.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
RTL_CRITICAL_SECTION CsrProcessLock;
PCSR_PROCESS CsrRootProcess = NULL;
SECURITY_QUALITY_OF_SERVICE CsrSecurityQos =
{
sizeof(SECURITY_QUALITY_OF_SERVICE),
SecurityImpersonation,
SECURITY_STATIC_TRACKING,
FALSE
};
ULONG CsrProcessSequenceCount = 5;
extern ULONG CsrTotalPerProcessDataLength;
/* PRIVATE FUNCTIONS **********************************************************/
/*++
* @name CsrSetToNormalPriority
*
* The CsrSetToNormalPriority routine sets the current NT Process'
* priority to the normal priority for CSR Processes.
*
* @param None.
*
* @return None.
*
* @remarks The "Normal" Priority corresponds to the Normal Foreground
* Priority (9) plus a boost of 4.
*
*--*/
VOID
NTAPI
CsrSetToNormalPriority(VOID)
{
KPRIORITY BasePriority = (8 + 1) + 4;
/* Set the Priority */
NtSetInformationProcess(NtCurrentProcess(),
ProcessBasePriority,
&BasePriority,
sizeof(BasePriority));
}
/*++
* @name CsrSetToShutdownPriority
*
* The CsrSetToShutdownPriority routine sets the current NT Process'
* priority to the boosted priority for CSR Processes doing shutdown.
* Additonally, it acquires the Shutdown Privilege required for shutdown.
*
* @param None.
*
* @return None.
*
* @remarks The "Shutdown" Priority corresponds to the Normal Foreground
* Priority (9) plus a boost of 6.
*
*--*/
VOID
NTAPI
CsrSetToShutdownPriority(VOID)
{
KPRIORITY BasePriority = (8 + 1) + 6;
BOOLEAN Old;
/* Get the shutdown privilege */
if (NT_SUCCESS(RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
TRUE,
FALSE,
&Old)))
{
/* Set the Priority */
NtSetInformationProcess(NtCurrentProcess(),
ProcessBasePriority,
&BasePriority,
sizeof(BasePriority));
}
}
/*++
* @name CsrProcessRefcountZero
*
* The CsrProcessRefcountZero routine is executed when a CSR Process has lost
* all its active references. It removes and de-allocates the CSR Process.
*
* @param CsrProcess
* Pointer to the CSR Process that is to be deleted.
*
* @return None.
*
* @remarks Do not call this routine. It is reserved for the internal
* thread management routines when a CSR Process has lost all
* its references.
*
* This routine is called with the Process Lock held.
*
*--*/
VOID
NTAPI
CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess)
{
ASSERT(ProcessStructureListLocked());
/* Remove the Process from the list */
CsrRemoveProcess(CsrProcess);
/* Check if there's a session */
if (CsrProcess->NtSession)
{
/* Dereference the Session */
CsrDereferenceNtSession(CsrProcess->NtSession, 0);
}
/* Close the Client Port if there is one */
if (CsrProcess->ClientPort) NtClose(CsrProcess->ClientPort);
/* Close the process handle */
NtClose(CsrProcess->ProcessHandle);
/* Free the Proces Object */
CsrDeallocateProcess(CsrProcess);
}
/*++
* @name CsrLockedDereferenceProcess
*
* The CsrLockedDereferenceProcess dereferences a CSR Process while the
* Process Lock is already being held.
*
* @param CsrProcess
* Pointer to the CSR Process to be dereferenced.
*
* @return None.
*
* @remarks This routine will return with the Process Lock held.
*
*--*/
VOID
NTAPI
CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess)
{
LONG LockCount;
/* Decrease reference count */
LockCount = --CsrProcess->ReferenceCount;
ASSERT(LockCount >= 0);
if (LockCount == 0)
{
/* Call the generic cleanup code */
DPRINT1("Should kill process: %p\n", CsrProcess);
CsrProcessRefcountZero(CsrProcess);
/* Acquire the lock again, it was released in CsrProcessRefcountZero */
CsrAcquireProcessLock();
}
}
/*++
* @name CsrAllocateProcess
* @implemented NT4
*
* The CsrAllocateProcess routine allocates a new CSR Process object.
*
* @return Pointer to the newly allocated CSR Process.
*
* @remarks None.
*
*--*/
PCSR_PROCESS
NTAPI
CsrAllocateProcess(VOID)
{
PCSR_PROCESS CsrProcess;
ULONG TotalSize;
/* Calculate the amount of memory this should take */
TotalSize = sizeof(CSR_PROCESS) +
(CSR_SERVER_DLL_MAX * sizeof(PVOID)) +
CsrTotalPerProcessDataLength;
/* Allocate a Process */
CsrProcess = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, TotalSize);
if (!CsrProcess) return NULL;
/* Handle the Sequence Number and protect against overflow */
CsrProcess->SequenceNumber = CsrProcessSequenceCount++;
if (CsrProcessSequenceCount < 5) CsrProcessSequenceCount = 5;
/* Increase the reference count */
CsrLockedReferenceProcess(CsrProcess);
/* Initialize the Thread List */
InitializeListHead(&CsrProcess->ThreadList);
/* Return the Process */
return CsrProcess;
}
/*++
* @name CsrLockedReferenceProcess
*
* The CsrLockedReferenceProcess references a CSR Process while the
* Process Lock is already being held.
*
* @param CsrProcess
* Pointer to the CSR Process to be referenced.
*
* @return None.
*
* @remarks This routine will return with the Process Lock held.
*
*--*/
VOID
NTAPI
CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess)
{
/* Increment the reference count */
++CsrProcess->ReferenceCount;
}
/*++
* @name CsrInitializeProcessStructure
* @implemented NT4
*
* The CsrInitializeProcessStructure routine sets up support for CSR Processes
* and CSR Threads by initializing our own CSR Root Process.
*
* @param None.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrInitializeProcessStructure(VOID)
{
NTSTATUS Status;
ULONG i;
/* Initialize the Lock */
Status = RtlInitializeCriticalSection(&CsrProcessLock);
if (!NT_SUCCESS(Status)) return Status;
/* Set up the Root Process */
CsrRootProcess = CsrAllocateProcess();
if (!CsrRootProcess) return STATUS_NO_MEMORY;
/* Set up the minimal information for it */
InitializeListHead(&CsrRootProcess->ListLink);
CsrRootProcess->ProcessHandle = (HANDLE)-1;
CsrRootProcess->ClientId = NtCurrentTeb()->ClientId;
/* Initialize the Thread Hash List */
for (i = 0; i < NUMBER_THREAD_HASH_BUCKETS; i++) InitializeListHead(&CsrThreadHashTable[i]);
/* Initialize the Wait Lock */
return RtlInitializeCriticalSection(&CsrWaitListsLock);
}
/*++
* @name CsrDeallocateProcess
*
* The CsrDeallocateProcess frees the memory associated with a CSR Process.
*
* @param CsrProcess
* Pointer to the CSR Process to be freed.
*
* @return None.
*
* @remarks Do not call this routine. It is reserved for the internal
* thread management routines when a CSR Process has been cleanly
* dereferenced and killed.
*
*--*/
VOID
NTAPI
CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess)
{
/* Free the process object from the heap */
RtlFreeHeap(CsrHeap, 0, CsrProcess);
}
/*++
* @name CsrRemoveProcess
*
* The CsrRemoveProcess function undoes a CsrInsertProcess operation and
* removes the CSR Process from the Process List and notifies Server DLLs
* of this removal.
*
* @param CsrProcess
* Pointer to the CSR Process to remove.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrRemoveProcess(IN PCSR_PROCESS CsrProcess)
{
PCSR_SERVER_DLL ServerDll;
ULONG i;
ASSERT(ProcessStructureListLocked());
/* Remove us from the Process List */
RemoveEntryList(&CsrProcess->ListLink);
/* Release the lock */
CsrReleaseProcessLock();
/* Loop every Server DLL */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the Server DLL */
ServerDll = CsrLoadedServerDll[i];
/* Check if it's valid and if it has a Disconnect Callback */
if (ServerDll && ServerDll->DisconnectCallback)
{
/* Call it */
ServerDll->DisconnectCallback(CsrProcess);
}
}
}
/*++
* @name CsrInsertProcess
*
* The CsrInsertProcess routine inserts a CSR Process into the Process List
* and notifies Server DLLs of the creation of a new CSR Process.
*
* @param ParentProcess
* Optional pointer to the Parent Process creating this CSR Process.
*
* @param CsrProcess
* Pointer to the CSR Process which is to be inserted.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrInsertProcess(IN PCSR_PROCESS ParentProcess OPTIONAL,
IN PCSR_PROCESS CsrProcess)
{
PCSR_SERVER_DLL ServerDll;
ULONG i;
ASSERT(ProcessStructureListLocked());
/* Insert it into the Root List */
InsertTailList(&CsrRootProcess->ListLink, &CsrProcess->ListLink);
/* Notify the Server DLLs */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the current Server DLL */
ServerDll = CsrLoadedServerDll[i];
/* Make sure it's valid and that it has callback */
if (ServerDll && ServerDll->NewProcessCallback)
{
ServerDll->NewProcessCallback(ParentProcess, CsrProcess);
}
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*++
* @name CsrCreateProcess
* @implemented NT4
*
* The CsrCreateProcess routine creates a CSR Process object for an NT Process.
*
* @param hProcess
* Handle to an existing NT Process to which to associate this
* CSR Process.
*
* @param hThread
* Handle to an existing NT Thread to which to create its
* corresponding CSR Thread for this CSR Process.
*
* @param ClientId
* Pointer to the Client ID structure of the NT Process to associate
* with this CSR Process.
*
* @param NtSession
* @param Flags
* @param DebugCid
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrCreateProcess(IN HANDLE hProcess,
IN HANDLE hThread,
IN PCLIENT_ID ClientId,
IN PCSR_NT_SESSION NtSession,
IN ULONG Flags,
IN PCLIENT_ID DebugCid)
{
PCSR_THREAD CurrentThread = CsrGetClientThread();
CLIENT_ID CurrentCid;
PCSR_PROCESS CurrentProcess;
PCSR_SERVER_DLL ServerDll;
PVOID ProcessData;
ULONG i;
PCSR_PROCESS CsrProcess;
NTSTATUS Status;
PCSR_THREAD CsrThread;
KERNEL_USER_TIMES KernelTimes;
/* Get the current CID and lock Processes */
CurrentCid = CurrentThread->ClientId;
CsrAcquireProcessLock();
/* Get the current CSR Thread */
CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid);
if (!CurrentThread)
{
/* We've failed to locate the thread */
CsrReleaseProcessLock();
return STATUS_THREAD_IS_TERMINATING;
}
/* Allocate a new Process Object */
CsrProcess = CsrAllocateProcess();
if (!CsrProcess)
{
/* Couldn't allocate Process */
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Inherit the Process Data */
CurrentProcess = CurrentThread->Process;
ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX];
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the current Server */
ServerDll = CsrLoadedServerDll[i];
/* Check if the DLL is Loaded and has Per Process Data */
if (ServerDll && ServerDll->SizeOfProcessData)
{
/* Set the pointer */
CsrProcess->ServerData[i] = ProcessData;
/* Copy the Data */
RtlMoveMemory(ProcessData,
CurrentProcess->ServerData[i],
ServerDll->SizeOfProcessData);
/* Update next data pointer */
ProcessData = (PVOID)((ULONG_PTR)ProcessData +
ServerDll->SizeOfProcessData);
}
else
{
/* No data for this Server */
CsrProcess->ServerData[i] = NULL;
}
}
/* Set the Exception Port for us */
Status = NtSetInformationProcess(hProcess,
ProcessExceptionPort,
&CsrApiPort,
sizeof(CsrApiPort));
if (!NT_SUCCESS(Status))
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */
if (Flags & CsrProcessCreateNewGroup)
{
/*
* We create the process group leader of a new process group, therefore
* its process group ID and sequence number are its own ones.
*/
CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess);
CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber;
}
else
{
/* Inherit the process group ID and sequence number from the current process */
CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId;
CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence;
}
/* Check if this is a console process */
if (Flags & CsrProcessIsConsoleApp) CsrProcess->Flags |= CsrProcessIsConsoleApp;
/* Mask out non-debug flags */
Flags &= ~(CsrProcessIsConsoleApp | CsrProcessCreateNewGroup | CsrProcessPriorityFlags);
/* Check if every process will be debugged */
if (!(Flags) && (CurrentProcess->DebugFlags & CsrDebugProcessChildren))
{
/* Pass it on to the current process */
CsrProcess->DebugFlags = CsrDebugProcessChildren;
CsrProcess->DebugCid = CurrentProcess->DebugCid;
}
/* Check if Debugging was used on this process */
if ((Flags & (CsrDebugOnlyThisProcess | CsrDebugProcessChildren)) && (DebugCid))
{
/* Save the debug flag used */
CsrProcess->DebugFlags = Flags;
/* Save the CID */
CsrProcess->DebugCid = *DebugCid;
}
/* Check if Debugging is enabled */
if (CsrProcess->DebugFlags)
{
/* Set the Debug Port for us */
Status = NtSetInformationProcess(hProcess,
ProcessDebugPort,
&CsrApiPort,
sizeof(CsrApiPort));
ASSERT(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status))
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
}
/* Get the Thread Create Time */
Status = NtQueryInformationThread(hThread,
ThreadTimes,
&KernelTimes,
sizeof(KernelTimes),
NULL);
if (!NT_SUCCESS(Status))
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Allocate a CSR Thread Structure */
CsrThread = CsrAllocateThread(CsrProcess);
if (!CsrThread)
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Save the data we have */
CsrThread->CreateTime = KernelTimes.CreateTime;
CsrThread->ClientId = *ClientId;
CsrThread->ThreadHandle = hThread;
ProtectHandle(hThread);
CsrThread->Flags = 0;
/* Insert the Thread into the Process */
Status = CsrInsertThread(CsrProcess, CsrThread);
if (!NT_SUCCESS(Status))
{
/* Bail out */
CsrDeallocateProcess(CsrProcess);
CsrDeallocateThread(CsrThread);
CsrReleaseProcessLock();
return Status;
}
/* Reference the session */
CsrReferenceNtSession(NtSession);
CsrProcess->NtSession = NtSession;
/* Setup Process Data */
CsrProcess->ClientId = *ClientId;
CsrProcess->ProcessHandle = hProcess;
CsrProcess->ShutdownLevel = 0x280;
/* Set the Priority to Background */
CsrSetBackgroundPriority(CsrProcess);
/* Insert the Process */
CsrInsertProcess(CurrentProcess, CsrProcess);
/* Release lock and return */
CsrReleaseProcessLock();
return Status;
}
/*++
* @name CsrDebugProcess
* @implemented NT4
*
* The CsrDebugProcess routine is deprecated in NT 5.1 and higher. It is
* exported only for compatibility with older CSR Server DLLs.
*
* @param CsrProcess
* Deprecated.
*
* @return Deprecated
*
* @remarks Deprecated.
*
*--*/
NTSTATUS
NTAPI
CsrDebugProcess(IN PCSR_PROCESS CsrProcess)
{
/* CSR does not handle debugging anymore */
DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
return STATUS_UNSUCCESSFUL;
}
/*++
* @name CsrDebugProcessStop
* @implemented NT4
*
* The CsrDebugProcessStop routine is deprecated in NT 5.1 and higher. It is
* exported only for compatibility with older CSR Server DLLs.
*
* @param CsrProcess
* Deprecated.
*
* @return Deprecated
*
* @remarks Deprecated.
*
*--*/
NTSTATUS
NTAPI
CsrDebugProcessStop(IN PCSR_PROCESS CsrProcess)
{
/* CSR does not handle debugging anymore */
DPRINT("CSRSRV: %s(0x%p) called\n", __FUNCTION__, CsrProcess);
return STATUS_UNSUCCESSFUL;
}
/*++
* @name CsrDereferenceProcess
* @implemented NT4
*
* The CsrDereferenceProcess routine removes a reference from a CSR Process.
*
* @param CsrThread
* Pointer to the CSR Process to dereference.
*
* @return None.
*
* @remarks If the reference count has reached zero (ie: the CSR Process has
* no more active references), it will be deleted.
*
*--*/
VOID
NTAPI
CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess)
{
LONG LockCount;
/* Acquire process lock */
CsrAcquireProcessLock();
/* Decrease reference count */
LockCount = --CsrProcess->ReferenceCount;
ASSERT(LockCount >= 0);
if (LockCount == 0)
{
/* Call the generic cleanup code */
CsrProcessRefcountZero(CsrProcess);
}
else
{
/* Just release the lock */
CsrReleaseProcessLock();
}
}
/*++
* @name CsrDestroyProcess
* @implemented NT4
*
* The CsrDestroyProcess routine destroys the CSR Process corresponding to
* a given Client ID.
*
* @param Cid
* Pointer to the Client ID Structure corresponding to the CSR
* Process which is about to be destroyed.
*
* @param ExitStatus
* Unused.
*
* @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING
* if the CSR Process is already terminating.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrDestroyProcess(IN PCLIENT_ID Cid,
IN NTSTATUS ExitStatus)
{
PCSR_THREAD CsrThread;
PCSR_PROCESS CsrProcess;
CLIENT_ID ClientId = *Cid;
PLIST_ENTRY NextEntry;
/* Acquire lock */
CsrAcquireProcessLock();
/* Find the thread */
CsrThread = CsrLocateThreadByClientId(&CsrProcess, &ClientId);
/* Make sure we got one back, and that it's not already gone */
if (!(CsrThread) || (CsrProcess->Flags & CsrProcessTerminating))
{
/* Release the lock and return failure */
CsrReleaseProcessLock();
return STATUS_THREAD_IS_TERMINATING;
}
/* Set the terminated flag */
CsrProcess->Flags |= CsrProcessTerminating;
/* Get the List Pointers */
NextEntry = CsrProcess->ThreadList.Flink;
while (NextEntry != &CsrProcess->ThreadList)
{
/* Get the current thread entry */
CsrThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* Make sure the thread isn't already dead */
if (CsrThread->Flags & CsrThreadTerminated)
{
/* Go the the next thread */
continue;
}
/* Set the Terminated flag */
CsrThread->Flags |= CsrThreadTerminated;
/* Acquire the Wait Lock */
CsrAcquireWaitLock();
/* Do we have an active wait block? */
if (CsrThread->WaitBlock)
{
/* Notify waiters of termination */
CsrNotifyWaitBlock(CsrThread->WaitBlock,
NULL,
NULL,
NULL,
CsrProcessTerminating,
TRUE);
}
/* Release the Wait Lock */
CsrReleaseWaitLock();
/* Dereference the thread */
CsrLockedDereferenceThread(CsrThread);
}
/* Release the Process Lock and return success */
CsrReleaseProcessLock();
return STATUS_SUCCESS;
}
/*++
* @name CsrGetProcessLuid
* @implemented NT4
*
* The CsrGetProcessLuid routine gets the LUID of the given process.
*
* @param hProcess
* Optional handle to the process whose LUID should be returned.
*
* @param Luid
* Pointer to a LUID Pointer which will receive the CSR Process' LUID.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
*
* @remarks If hProcess is not supplied, then the current thread's token will
* be used. If that too is missing, then the current process' token
* will be used.
*
*--*/
NTSTATUS
NTAPI
CsrGetProcessLuid(IN HANDLE hProcess OPTIONAL,
OUT PLUID Luid)
{
HANDLE hToken = NULL;
NTSTATUS Status;
ULONG Length;
PTOKEN_STATISTICS TokenStats;
/* Check if we have a handle to a CSR Process */
if (!hProcess)
{
/* We don't, so try opening the Thread's Token */
Status = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
FALSE,
&hToken);
/* Check for success */
if (!NT_SUCCESS(Status))
{
/* If we got some other failure, then return and quit */
if (Status != STATUS_NO_TOKEN) return Status;
/* We don't have a Thread Token, use a Process Token */
hProcess = NtCurrentProcess();
hToken = NULL;
}
}
/* Check if we have a token by now */
if (!hToken)
{
/* No token yet, so open the Process Token */
Status = NtOpenProcessToken(hProcess,
TOKEN_QUERY,
&hToken);
if (!NT_SUCCESS(Status))
{
/* Still no token, return the error */
return Status;
}
}
/* Now get the size we'll need for the Token Information */
Status = NtQueryInformationToken(hToken,
TokenStatistics,
NULL,
0,
&Length);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
/* Close the token and fail */
NtClose(hToken);
return Status;
}
/* Allocate memory for the Token Info */
if (!(TokenStats = RtlAllocateHeap(CsrHeap, 0, Length)))
{
/* Fail and close the token */
NtClose(hToken);
return STATUS_NO_MEMORY;
}
/* Now query the information */
Status = NtQueryInformationToken(hToken,
TokenStatistics,
TokenStats,
Length,
&Length);
/* Close the handle */
NtClose(hToken);
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Return the LUID */
*Luid = TokenStats->AuthenticationId;
}
/* Free the query information */
RtlFreeHeap(CsrHeap, 0, TokenStats);
/* Return the Status */
return Status;
}
/*++
* @name CsrImpersonateClient
* @implemented NT4
*
* The CsrImpersonateClient will impersonate the given CSR Thread.
*
* @param CsrThread
* Pointer to the CSR Thread to impersonate.
*
* @return TRUE if impersonation succeeded, FALSE otherwise.
*
* @remarks Impersonation can be recursive.
*
*--*/
BOOLEAN
NTAPI
CsrImpersonateClient(IN PCSR_THREAD CsrThread)
{
NTSTATUS Status;
PCSR_THREAD CurrentThread = CsrGetClientThread();
/* Use the current thread if none given */
if (!CsrThread) CsrThread = CurrentThread;
/* Still no thread, something is wrong */
if (!CsrThread)
{
/* Failure */
return FALSE;
}
/* Make the call */
Status = NtImpersonateThread(NtCurrentThread(),
CsrThread->ThreadHandle,
&CsrSecurityQos);
if (!NT_SUCCESS(Status))
{
/* Failure */
#ifdef CSR_DBG
DPRINT1("CSRSS: Can't impersonate client thread - Status = %lx\n", Status);
// if (Status != STATUS_BAD_IMPERSONATION_LEVEL) DbgBreakPoint();
#endif
return FALSE;
}
/* Increase the impersonation count for the current thread */
if (CurrentThread) ++CurrentThread->ImpersonationCount;
/* Return Success */
return TRUE;
}
/*++
* @name CsrLockProcessByClientId
* @implemented NT4
*
* The CsrLockProcessByClientId routine locks the CSR Process corresponding
* to the given Process ID and optionally returns it.
*
* @param Pid
* Process ID corresponding to the CSR Process which will be locked.
*
* @param CsrProcess
* Optional pointer to a CSR Process pointer which will hold the
* CSR Process corresponding to the given Process ID.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
*
* @remarks Locking a CSR Process is defined as acquiring an extra
* reference to it and returning with the Process Lock held.
*
*--*/
NTSTATUS
NTAPI
CsrLockProcessByClientId(IN HANDLE Pid,
OUT PCSR_PROCESS *CsrProcess)
{
PLIST_ENTRY NextEntry;
PCSR_PROCESS CurrentProcess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
/* Acquire the lock */
CsrAcquireProcessLock();
/* Assume failure */
ASSERT(CsrProcess != NULL);
*CsrProcess = NULL;
/* Setup the List Pointers */
NextEntry = &CsrRootProcess->ListLink;
do
{
/* Get the Process */
CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
/* Check for PID Match */
if (CurrentProcess->ClientId.UniqueProcess == Pid)
{
Status = STATUS_SUCCESS;
break;
}
/* Move to the next entry */
NextEntry = NextEntry->Flink;
} while (NextEntry != &CsrRootProcess->ListLink);
/* Check if we didn't find it in the list */
if (!NT_SUCCESS(Status))
{
/* Nothing found, release the lock */
CsrReleaseProcessLock();
}
else
{
/* Lock the found process and return it */
CsrLockedReferenceProcess(CurrentProcess);
*CsrProcess = CurrentProcess;
}
/* Return the result */
return Status;
}
/*++
* @name CsrRevertToSelf
* @implemented NT4
*
* The CsrRevertToSelf routine will attempt to remove an active impersonation.
*
* @param None.
*
* @return TRUE if the reversion was succesful, FALSE otherwise.
*
* @remarks Impersonation can be recursive; as such, the impersonation token
* will only be deleted once the CSR Thread's impersonaton count
* has reached zero.
*
*--*/
BOOLEAN
NTAPI
CsrRevertToSelf(VOID)
{
NTSTATUS Status;
PCSR_THREAD CurrentThread = CsrGetClientThread();
HANDLE ImpersonationToken = NULL;
/* Check if we have a Current Thread */
if (CurrentThread)
{
/* Make sure impersonation is on */
if (!CurrentThread->ImpersonationCount)
{
DPRINT1("CSRSS: CsrRevertToSelf called while not impersonating\n");
// DbgBreakPoint();
return FALSE;
}
else if ((--CurrentThread->ImpersonationCount) > 0)
{
/* Success; impersonation count decreased but still not zero */
return TRUE;
}
}
/* Impersonation has been totally removed, revert to ourselves */
Status = NtSetInformationThread(NtCurrentThread(),
ThreadImpersonationToken,
&ImpersonationToken,
sizeof(ImpersonationToken));
/* Return TRUE or FALSE */
return NT_SUCCESS(Status);
}
/*++
* @name CsrSetBackgroundPriority
* @implemented NT4
*
* The CsrSetBackgroundPriority routine sets the priority for the given CSR
* Process as a Background priority.
*
* @param CsrProcess
* Pointer to the CSR Process whose priority will be modified.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess)
{
PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
/* Set the Foreground bit off */
ProcessPriority.Foreground = FALSE;
/* Set the new priority */
NtSetInformationProcess(CsrProcess->ProcessHandle,
ProcessForegroundInformation,
&ProcessPriority,
sizeof(ProcessPriority));
}
/*++
* @name CsrSetForegroundPriority
* @implemented NT4
*
* The CsrSetForegroundPriority routine sets the priority for the given CSR
* Process as a Foreground priority.
*
* @param CsrProcess
* Pointer to the CSR Process whose priority will be modified.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrSetForegroundPriority(IN PCSR_PROCESS CsrProcess)
{
PROCESS_FOREGROUND_BACKGROUND ProcessPriority;
/* Set the Foreground bit on */
ProcessPriority.Foreground = TRUE;
/* Set the new priority */
NtSetInformationProcess(CsrProcess->ProcessHandle,
ProcessForegroundInformation,
&ProcessPriority,
sizeof(ProcessPriority));
}
/*++
* @name FindProcessForShutdown
*
* The FindProcessForShutdown routine returns a CSR Process which is ready
* to be shutdown, and sets the appropriate shutdown flags for it.
*
* @param CallerLuid
* Pointer to the LUID of the CSR Process calling this routine.
*
* @return Pointer to a CSR Process which is ready to be shutdown.
*
* @remarks None.
*
*--*/
PCSR_PROCESS
NTAPI
FindProcessForShutdown(IN PLUID CallerLuid)
{
PCSR_PROCESS CsrProcess, ReturnCsrProcess = NULL;
PCSR_THREAD CsrThread;
NTSTATUS Status;
ULONG Level = 0;
LUID ProcessLuid;
LUID SystemLuid = SYSTEM_LUID;
PLIST_ENTRY NextEntry;
/* Set the List Pointers */
NextEntry = CsrRootProcess->ListLink.Flink;
while (NextEntry != &CsrRootProcess->ListLink)
{
/* Get the process */
CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* Skip this process if it's already been processed */
if (CsrProcess->Flags & CsrProcessSkipShutdown) continue;
/* Get the LUID of this process */
Status = CsrGetProcessLuid(CsrProcess->ProcessHandle, &ProcessLuid);
/* Check if we didn't get access to the LUID */
if (Status == STATUS_ACCESS_DENIED)
{
/* Check if we have any threads */
if (CsrProcess->ThreadCount)
{
/* Impersonate one of the threads and retry */
CsrThread = CONTAINING_RECORD(CsrProcess->ThreadList.Flink,
CSR_THREAD,
Link);
if (CsrImpersonateClient(CsrThread))
{
Status = CsrGetProcessLuid(NULL, &ProcessLuid);
CsrRevertToSelf();
}
else
{
Status = STATUS_BAD_IMPERSONATION_LEVEL;
}
}
}
if (!NT_SUCCESS(Status))
{
/* We didn't have access, so skip it */
CsrProcess->Flags |= CsrProcessSkipShutdown;
continue;
}
/* Check if this is the System LUID */
if (RtlEqualLuid(&ProcessLuid, &SystemLuid))
{
/* Mark this process */
CsrProcess->ShutdownFlags |= CsrShutdownSystem;
}
else if (!RtlEqualLuid(&ProcessLuid, CallerLuid))
{
/* Our LUID doesn't match with the caller's */
CsrProcess->ShutdownFlags |= CsrShutdownOther;
}
/* Check if we're past the previous level */
if ((CsrProcess->ShutdownLevel > Level) || !ReturnCsrProcess)
{
/* Update the level */
Level = CsrProcess->ShutdownLevel;
/* Set the final process */
ReturnCsrProcess = CsrProcess;
}
}
/* Check if we found a process */
if (ReturnCsrProcess)
{
/* Skip this one next time */
ReturnCsrProcess->Flags |= CsrProcessSkipShutdown;
}
return ReturnCsrProcess;
}
/*++
* @name CsrShutdownProcesses
* @implemented NT4
*
* The CsrShutdownProcesses routine shuts down every CSR Process possible
* and calls each Server DLL's shutdown notification.
*
* @param CallerLuid
* Pointer to the LUID of the CSR Process that is ordering the
* shutdown.
*
* @param Flags
* Flags to send to the shutdown notification routine.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL otherwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrShutdownProcesses(IN PLUID CallerLuid,
IN ULONG Flags)
{
PLIST_ENTRY NextEntry;
PCSR_PROCESS CsrProcess;
NTSTATUS Status;
BOOLEAN FirstTry;
ULONG i;
PCSR_SERVER_DLL ServerDll;
ULONG Result = 0;
/* Acquire process lock */
CsrAcquireProcessLock();
/* Add shutdown flag */
CsrRootProcess->ShutdownFlags |= CsrShutdownSystem;
/* Get the list pointers */
NextEntry = CsrRootProcess->ListLink.Flink;
while (NextEntry != &CsrRootProcess->ListLink)
{
/* Get the Process */
CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
/* Move to the next entry */
NextEntry = NextEntry->Flink;
/* Remove the skip flag, set shutdown flags to 0 */
CsrProcess->Flags &= ~CsrProcessSkipShutdown;
CsrProcess->ShutdownFlags = 0;
}
/* Set shutdown Priority */
CsrSetToShutdownPriority();
/* Start looping */
while (TRUE)
{
/* Find the next process to shutdown */
CsrProcess = FindProcessForShutdown(CallerLuid);
if (!CsrProcess) break;
/* Increase reference to process */
CsrLockedReferenceProcess(CsrProcess);
FirstTry = TRUE;
while (TRUE)
{
/* Loop all the servers */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the current server */
ServerDll = CsrLoadedServerDll[i];
/* Check if it's valid and if it has a Shutdown Process Callback */
if (ServerDll && ServerDll->ShutdownProcessCallback)
{
/* Release the lock, make the callback, and acquire it back */
CsrReleaseProcessLock();
Result = ServerDll->ShutdownProcessCallback(CsrProcess,
Flags,
FirstTry);
CsrAcquireProcessLock();
/* Check the result */
if (Result == CsrShutdownCsrProcess)
{
/* The callback unlocked the process */
break;
}
else if (Result == CsrShutdownCancelled)
{
#ifdef CSR_DBG
/* Check if this was a forced shutdown */
if (Flags & EWX_FORCE)
{
DPRINT1("Process %x cancelled forced shutdown (Dll = %d)\n",
CsrProcess->ClientId.UniqueProcess, i);
DbgBreakPoint();
}
#endif
/* Shutdown was cancelled, unlock and exit */
CsrReleaseProcessLock();
Status = STATUS_CANCELLED;
goto Quickie;
}
}
}
/* No matches during the first try, so loop again */
if (FirstTry && (Result == CsrShutdownNonCsrProcess))
{
FirstTry = FALSE;
continue;
}
/* Second try, break out */
break;
}
/* We've reached the final loop here, so dereference */
if (i == CSR_SERVER_DLL_MAX)
CsrLockedDereferenceProcess(CsrProcess);
}
/* Success path */
CsrReleaseProcessLock();
Status = STATUS_SUCCESS;
Quickie:
/* Return to normal priority */
CsrSetToNormalPriority();
return Status;
}
/*++
* @name CsrUnlockProcess
* @implemented NT4
*
* The CsrUnlockProcess undoes a previous CsrLockProcessByClientId operation.
*
* @param CsrProcess
* Pointer to a previously locked CSR Process.
*
* @return STATUS_SUCCESS.
*
* @remarks This routine must be called with the Process Lock held.
*
*--*/
NTSTATUS
NTAPI
CsrUnlockProcess(IN PCSR_PROCESS CsrProcess)
{
/* Dereference the process */
CsrLockedDereferenceProcess(CsrProcess);
/* Release the lock and return */
CsrReleaseProcessLock();
return STATUS_SUCCESS;
}
/* EOF */