reactos/subsystems/csr/csrsrv/process.c

1229 lines
32 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS CSR Sub System
* FILE: subsys/csr/csrsrv/process.c
* PURPOSE: CSR Server DLL Process Implementation
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
*/
/* INCLUDES ******************************************************************/
#include "srv.h"
#define NDEBUG
#include <debug.h>
/* DATA **********************************************************************/
PCSR_PROCESS CsrRootProcess = NULL;
RTL_CRITICAL_SECTION CsrProcessLock;
ULONG CsrProcessSequenceCount = 5;
ULONG CsrTotalPerProcessDataLength;
/* PRIVATE FUNCTIONS *********************************************************/
/*++
* @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 */
CsrProcess->SequenceNumber = CsrProcessSequenceCount++;
/* Increase the reference count */
CsrProcess->ReferenceCount++;
/* Initialize the Thread List */
InitializeListHead(&CsrProcess->ThreadList);
/* Return the Process */
return CsrProcess;
}
/*++
* @name CsrServerInitialization
* @implemented NT4
*
* The CsrInitializeProcesses routine sets up support for CSR Processes
* and CSR Threads.
*
* @param None.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
* othwerwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrInitializeProcesses(VOID)
{
NTSTATUS Status;
ULONG i;
/* Initialize the Lock */
Status = RtlInitializeCriticalSection(&CsrProcessLock);
/* 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 < 256; i++) InitializeListHead(&CsrThreadHashTable[i]);
/* Initialize the Wait Lock */
Status = RtlInitializeCriticalSection(&CsrWaitListsLock);
return Status;
}
/*++
* @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 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 Parent
* Optional pointer to the CSR Process creating this CSR Process.
*
* @param CurrentProcess
* Optional pointer to the current CSR Process.
*
* @param CsrProcess
* Pointer to the CSR Process which is to be inserted.
*
* @return None.
*
* @remarks None.
*
*--*/
VOID
NTAPI
CsrInsertProcess(IN PCSR_PROCESS Parent OPTIONAL,
IN PCSR_PROCESS CurrentProcess OPTIONAL,
IN PCSR_PROCESS CsrProcess)
{
PCSR_SERVER_DLL ServerDll;
ULONG i;
/* Set the parent */
CsrProcess->Parent = Parent;
/* 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)(CurrentProcess, 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)
{
/* Decrease reference count */
if (!(--CsrProcess->ReferenceCount))
{
/* Call the generic cleanup code */
CsrAcquireProcessLock();
CsrProcessRefcountZero(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;
/* 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 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)
{
/* 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 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 Forground
* Priority (9) plus a boost of 4.
*
*--*/
VOID
NTAPI
CsrSetToNormalPriority(VOID)
{
KPRIORITY BasePriority = (8 + 1) + 4;
/* Set the Priority */
NtSetInformationProcess(NtCurrentProcess(),
ProcessBasePriority,
&BasePriority,
sizeof(KPRIORITY));
}
/*++
* @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 Forground
* Priority (9) plus a boost of 6.
*
*--*/
VOID
NTAPI
CsrSetToShutdownPriority(VOID)
{
KPRIORITY SetBasePriority = (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,
&SetBasePriority,
sizeof(KPRIORITY));
}
}
/*++
* @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(PLUID CallerLuid)
{
PLIST_ENTRY ListHead, NextEntry;
LUID ProcessLuid;
NTSTATUS Status;
LUID SystemLuid = SYSTEM_LUID;
PCSR_PROCESS CsrProcess;
PCSR_THREAD CsrThread;
BOOLEAN IsSystemLuid = FALSE, IsOurLuid = FALSE;
PCSR_PROCESS ReturnCsrProcess = NULL;
ULONG Level = 0;
/* Set the List Pointers */
ListHead = &CsrRootProcess->ListLink;
NextEntry = ListHead->Flink;
/* Start looping */
while (NextEntry != ListHead)
{
/* 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);
CsrImpersonateClient(CsrThread);
Status = CsrGetProcessLuid(NULL, &ProcessLuid);
CsrRevertToSelf();
}
}
if (!NT_SUCCESS(Status))
{
/* We didn't have access, so skip it */
CsrProcess->Flags |= CsrProcessSkipShutdown;
continue;
}
/* Check if this is the System LUID */
if ((IsSystemLuid = RtlEqualLuid(&ProcessLuid, &SystemLuid)))
{
/* Mark this process */
CsrProcess->ShutdownFlags |= CsrShutdownSystem;
}
else if (!(IsOurLuid = 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)
{
/* 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;
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*++
* @name CsrCreateProcess
* @implemented NT4
*
* Do nothing for 500ms.
*
* @param ArgumentCount
* Description of the parameter. Wrapped to more lines on ~70th
* column.
*
* @param Arguments
* Description of the parameter. Wrapped to more lines on ~70th
* column.
*
* @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL
* othwerwise.
*
* @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 = NtCurrentTeb()->CsrClientThread;
CLIENT_ID CurrentCid;
PCSR_PROCESS CurrentProcess;
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 */
if (!(CsrProcess = CsrAllocateProcess()))
{
/* Couldn't allocate Process */
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Setup Process Data */
CsrProcess->ClientId = *ClientId;
CsrProcess->ProcessHandle = hProcess;
CsrProcess->ShutdownLevel = 0x280;
/* Inherit the Process Data */
ProcessData = &CurrentProcess->ServerData[CSR_SERVER_DLL_MAX];
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Check if the DLL is Loaded and has Per Process Data */
if (CsrLoadedServerDll[i] && CsrLoadedServerDll[i]->SizeOfProcessData)
{
/* Set the pointer */
CsrProcess->ServerData[i] = ProcessData;
/* Copy the Data */
RtlMoveMemory(ProcessData,
CurrentProcess->ServerData[i],
CsrLoadedServerDll[i]->SizeOfProcessData);
/* Update next data pointer */
ProcessData = (PVOID)((ULONG_PTR)ProcessData +
CsrLoadedServerDll[i]->SizeOfProcessData);
}
else
{
/* No data for this Server */
CsrProcess->ServerData[i] = NULL;
}
}
/* Set the Exception port to us */
Status = NtSetInformationProcess(hProcess,
ProcessExceptionPort,
&CsrApiPort,
sizeof(HANDLE));
if (!NT_SUCCESS(Status))
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* If Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */
if (!(Flags & CsrProcessCreateNewGroup))
{
/* Create new data */
CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess);
CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber;
}
else
{
/* Copy it 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);
/* 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))
{
/* Save the debug flag used */
CsrProcess->DebugFlags = Flags;
/* Save the CID */
if (DebugCid) CsrProcess->DebugCid = *DebugCid;
}
/* Check if we debugging is enabled */
if (CsrProcess->DebugFlags)
{
/* Set the Debug Port to us */
Status = NtSetInformationProcess(hProcess,
ProcessDebugPort,
&CsrApiPort,
sizeof(HANDLE));
if (!NT_SUCCESS(Status))
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
}
/* Get the Thread Create Time */
Status = NtQueryInformationThread(hThread,
ThreadTimes,
(PVOID)&KernelTimes,
sizeof(KernelTimes),
NULL);
/* Allocate a CSR Thread Structure */
CsrThread = CsrAllocateThread(CsrProcess);
if (CsrThread == NULL)
{
/* Failed */
CsrDeallocateProcess(CsrProcess);
CsrReleaseProcessLock();
return STATUS_NO_MEMORY;
}
/* Save the data we have */
CsrThread->CreateTime = KernelTimes.CreateTime;
CsrThread->ClientId = *ClientId;
CsrThread->ThreadHandle = hThread;
CsrThread->Flags = 0;
/* Insert the Thread into the Process */
CsrInsertThread(CsrProcess, CsrThread);
/* Reference the session */
CsrReferenceNtSession(NtSession);
CsrProcess->NtSession = NtSession;
/* Set the Priority to Background */
CsrSetBackgroundPriority(CsrProcess);
/* Insert the Process */
CsrInsertProcess(NULL, 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(PCSR_PROCESS CsrProcess)
{
/* CSR does not handle debugging anymore */
DPRINT("CSRSRV: %s(%08lx) called\n", __FUNCTION__, CsrProcess);
return STATUS_UNSUCCESSFUL;
}
/*++
* @name CsrServerInitialization
* @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(PCSR_PROCESS CsrProcess)
{
/* CSR does not handle debugging anymore */
DPRINT("CSRSRV: %s(%08lx) 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(PCSR_PROCESS CsrProcess)
{
/* Acquire process lock */
CsrAcquireProcessLock();
/* Decrease reference count */
if (!(--CsrProcess->ReferenceCount))
{
/* 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 ListHead, 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 */
ListHead = &CsrProcess->ThreadList;
NextEntry = ListHead->Flink;
/* Loop the list */
while (NextEntry != ListHead)
{
/* 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) 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
*
* Do nothing for 500ms.
*
* @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
* othwerwise.
*
* @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(HANDLE hProcess OPTIONAL,
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);
/* 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,
[NTOS]: While attempting to ressucitate the user-mode shutdown code in CSRSS (disabled since 2006), it seemed clear that one of the main steps is to obtain the caller's LUID in order to kill the right processes. This LUID is obtained from the current thread's token, and we know it's the callers since we're supposed to impersonate the caller. Unfortunately, impersonation failed, making the whole process fail. Impersonation failed because NtImpersonateThread was actually inverting the THREAD_IMPERSONATE rights, asking the client thread for the server's permissions, and vice versa. Fixing this resulted in yet another failure. Analysis of this failure showed that even opening the server (ie: current) thread for THREAD_DIRECT_IMPERSONATION failed, which is unusual since the current thread should have access to all its rights. This is determined in PspCreateThread when the ETHREAD->GrantedAccess field is set. Continuing onto this path, tracing revealed that GrantedAccess was merely 0x1607F and not 0x1F3FF as expected, meaning that not all rights were given, including the impersonate right (0x200), explaining the failure, but not the deeper reason behind it. Attempting to identify which code path set this GrantedAccess, the SepAccessCheck routine came to light. A bug there caused MAXIMUM_ALLOWED accesses to fail in certain scenarios, such as when the object had no security descriptor, because MAXIMUM_ALLOWED would be granted as an absolute value, when instead of it is a flag that should grant GENERIC_ALL. Fixing that bug, the failure continued. Further analysis identified that the Administrators SID was being found with GENERIC_READ + WRITE + EXECUTE access, and no SID was found for GENERIC_ALL access. This happened when searching the system token's default DACL, which is assigned to the kernel (but for kernel-mode callers, this check was skipped), smss, winlogon, etc. The code for creating this system token was heavily hacked, so the function to create the system token, as well as NtCreateToken were refactored to use a common piece of token-creating code. Furthermode, the system token was correctly created with Local System as the user, and Administrators as one of the groups. Finally, SeDefaultDacl was used (already setup properly) instead of a badly configured Default DACL. The new shared code also correctly sets the SE_GROUP_ENABLED flag on all SE_GROUP_MANDATORY groups, and scans tokens to set the TOKEN_HAS_ADMIN_GROUP and TOKEN_HAS_TRAVERSE_PRIVILEGE flags which were not previously set. With the correct system token and default DACL, the Local System SID was now found, but the failure continued. This was revealed to be due to an even deeper rooted problem, as the SepIsSidInToken routine checked for SE_GROUP_ENABLED before listing a SID as "present". Although this is correct for actual groups, the User SID will never have the SE_GROUP_ENABLED flag as it is not a group. This caused any granted access ACE belonging to a user (instead of a group) to be ignored, causing either access check failures, or limited rights returned (in the MAXIMUM_ALLOWED case). Upon fixing this bug, the NtImpersonateThread call finally returned success, since the rights were now correct. Promptly upon calling NtOpenThreadToken to query the LUID however, the system ASSERTED with FALSE. The code at fault was a line in NtOpenThreadTokenEx which forcefully ASSERTed if the impersonation code path was taken, presumably because this was never tested, and ReactOS would actually always fail impersonation attempts due to the bugs fixed above. This routine was thus quickly reworked to fix some mistakes (such as forgetting to actually impersonate the client, incorrect referencing/dereferencing of thread/tokens, and assumptions about DACL creation success). Having fixed the NtOpenThreadTokenEx routine, the LUID query now went through for the impersonated token, but soon causing a user-mode crash, due to an incorrect parameter reference in the CsrGetProcessLuid function in the csrsrv code by Alex (which I copy/pasted to reduce code duplication). Fixing this final bug finally allowed the correct LUID to be queried and I was able to continue development of not-yet-commited user-mode shutdown code. svn path=/trunk/; revision=46028
2010-03-09 10:35:58 +00:00
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 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
* othwerwise.
*
* @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 OPTIONAL)
{
PLIST_ENTRY ListHead, NextEntry;
PCSR_PROCESS CurrentProcess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
/* Acquire the lock */
CsrAcquireProcessLock();
/* Setup the List Pointers */
ListHead = &CsrRootProcess->ListLink;
NextEntry = ListHead;
/* Start Loop */
while (NextEntry != ListHead)
{
/* Get the Process */
CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
/* Check for PID Match */
if (CurrentProcess->ClientId.UniqueProcess == Pid)
{
/* Get out of here with success */
Status = STATUS_SUCCESS;
break;
}
/* Next entry */
NextEntry = NextEntry->Flink;
}
/* Did the loop find something? */
if (NT_SUCCESS(Status))
{
/* Lock the found process */
CurrentProcess->ReferenceCount++;
}
else
{
/* Nothing found, release the lock */
CsrReleaseProcessLock();
}
/* Return the status and process */
if (CsrProcess) *CsrProcess = CurrentProcess;
return Status;
}
/*++
* @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_PRIORITY_CLASS PriorityClass;
/* Set the Foreground bit on */
PriorityClass.Foreground = TRUE;
/* Set the new Priority */
NtSetInformationProcess(CsrProcess->ProcessHandle,
ProcessPriorityClass,
&PriorityClass,
sizeof(PriorityClass));
}
/*++
* @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_PRIORITY_CLASS PriorityClass;
/* Set the Foreground bit off */
PriorityClass.Foreground = FALSE;
/* Set the new Priority */
NtSetInformationProcess(CsrProcess->ProcessHandle,
ProcessPriorityClass,
&PriorityClass,
sizeof(PriorityClass));
}
/*++
* @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
* othwerwise.
*
* @remarks None.
*
*--*/
NTSTATUS
NTAPI
CsrShutdownProcesses(PLUID CallerLuid,
ULONG Flags)
{
PLIST_ENTRY ListHead, NextEntry;
PCSR_PROCESS CsrProcess = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
BOOLEAN FirstTry = TRUE;
ULONG i = 0;
PCSR_SERVER_DLL ServerDll = NULL;
ULONG Result = 0;
/* Acquire process lock */
CsrAcquireProcessLock();
/* Add shutdown flag */
CsrRootProcess->ShutdownFlags |= CsrShutdownSystem;
/* Get the list pointers */
ListHead = &CsrRootProcess->ListLink;
NextEntry = ListHead->Flink;
/* Start the loop */
while (NextEntry != ListHead)
{
/* Get the Process */
CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink);
/* Remove the skip flag, set shutdown flags to 0*/
CsrProcess->Flags &= ~CsrProcessSkipShutdown;
CsrProcess->ShutdownFlags = 0;
/* Move to the next */
NextEntry = NextEntry->Flink;
}
/* Set shudown Priority */
CsrSetToShutdownPriority();
/* Start looping */
while (TRUE)
{
/* Find the next process to shutdown */
if (!(CsrProcess = FindProcessForShutdown(CallerLuid)))
{
/* Done, quit */
CsrReleaseProcessLock();
Status = STATUS_SUCCESS;
goto Quickie;
}
/* Increase reference to process */
CsrProcess->ReferenceCount++;
LoopAgain:
/* Loop all the servers */
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
/* Get the current server */
ServerDll = CsrLoadedServerDll[i];
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 == CsrShutdownNonCsrProcess)
{
/* A non-CSR process, the callback didn't touch it */
continue;
}
else if (Result == CsrShutdownCancelled)
{
/* 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;
goto LoopAgain;
}
/* We've reached the final loop here, so dereference */
if (i == CSR_SERVER_DLL_MAX) CsrLockedDereferenceProcess(CsrProcess);
}
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(PCSR_PROCESS CsrProcess)
{
/* Dereference the process */
CsrLockedDereferenceProcess(CsrProcess);
/* Release the lock and return */
CsrReleaseProcessLock();
return STATUS_SUCCESS;
}
/* EOF */