You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2044 lines
55 KiB
2044 lines
55 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
process.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the worker routines called to create and
|
|
maintain the application process structure for the Client-Server
|
|
Runtime Subsystem to the Session Manager SubSystem.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 10-Oct-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "csrsrv.h"
|
|
#include <wchar.h>
|
|
// ProcessSequenceCount will never be a value less than FIRST_SEQUENCE_COUNT
|
|
// currently GDI needs 0 - 4 to be reserved.
|
|
|
|
ULONG ProcessSequenceCount = FIRST_SEQUENCE_COUNT;
|
|
|
|
#define THREAD_HASH_SIZE 256
|
|
#define THREAD_ID_TO_HASH(id) (HandleToUlong(id)&(THREAD_HASH_SIZE-1))
|
|
LIST_ENTRY CsrThreadHashTable[THREAD_HASH_SIZE];
|
|
|
|
|
|
SECURITY_QUALITY_OF_SERVICE CsrSecurityQos = {
|
|
sizeof(SECURITY_QUALITY_OF_SERVICE), SecurityImpersonation,
|
|
SECURITY_DYNAMIC_TRACKING, FALSE
|
|
};
|
|
|
|
PCSR_PROCESS
|
|
FindProcessForShutdown(
|
|
PLUID CallerLuid
|
|
);
|
|
|
|
NTSTATUS
|
|
ReadUnicodeString(HANDLE ProcessHandle,
|
|
PUNICODE_STRING RemoteString,
|
|
PUNICODE_STRING LocalString
|
|
);
|
|
|
|
VOID
|
|
CsrpSetToNormalPriority(
|
|
VOID
|
|
)
|
|
{
|
|
KPRIORITY SetBasePriority;
|
|
|
|
SetBasePriority = FOREGROUND_BASE_PRIORITY + 4;
|
|
NtSetInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessBasePriority,
|
|
(PVOID) &SetBasePriority,
|
|
sizeof(SetBasePriority)
|
|
);
|
|
}
|
|
|
|
VOID
|
|
CsrpSetToShutdownPriority(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN WasEnabled;
|
|
KPRIORITY SetBasePriority;
|
|
|
|
Status = RtlAdjustPrivilege(
|
|
SE_INC_BASE_PRIORITY_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return;
|
|
|
|
SetBasePriority = FOREGROUND_BASE_PRIORITY + 6;
|
|
NtSetInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessBasePriority,
|
|
(PVOID) &SetBasePriority,
|
|
sizeof(SetBasePriority)
|
|
);
|
|
}
|
|
|
|
VOID
|
|
CsrSetForegroundPriority(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PROCESS_FOREGROUND_BACKGROUND Fg;
|
|
|
|
Fg.Foreground = TRUE;
|
|
|
|
NtSetInformationProcess(
|
|
Process->ProcessHandle,
|
|
ProcessForegroundInformation,
|
|
(PVOID)&Fg,
|
|
sizeof(Fg)
|
|
);
|
|
}
|
|
|
|
VOID
|
|
CsrSetBackgroundPriority(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PROCESS_FOREGROUND_BACKGROUND Fg;
|
|
|
|
Fg.Foreground = FALSE;
|
|
|
|
NtSetInformationProcess(
|
|
Process->ProcessHandle,
|
|
ProcessForegroundInformation,
|
|
(PVOID)&Fg,
|
|
sizeof(Fg)
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Though this function does not seem to cleanup on failure, failure
|
|
// will cause Csrss to exit, so any allocated memory will be freed and
|
|
// any open handle will be closed.
|
|
//
|
|
|
|
NTSTATUS
|
|
CsrInitializeProcessStructure(
|
|
VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
|
|
Status = RtlInitializeCriticalSection(&CsrProcessStructureLock);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
CsrRootProcess = CsrAllocateProcess();
|
|
if (CsrRootProcess == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
InitializeListHead( &CsrRootProcess->ListLink );
|
|
CsrRootProcess->ProcessHandle = (HANDLE) -1;
|
|
CsrRootProcess->ClientId = NtCurrentTeb()->ClientId;
|
|
for (i = 0; i < THREAD_HASH_SIZE; i++) {
|
|
InitializeListHead(&CsrThreadHashTable[i]);
|
|
}
|
|
|
|
Status = RtlInitializeCriticalSection(&CsrWaitListsLock);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
PCSR_PROCESS
|
|
CsrAllocateProcess(
|
|
VOID)
|
|
{
|
|
PCSR_PROCESS Process;
|
|
ULONG ProcessSize;
|
|
|
|
//
|
|
// Allocate an Windows Process Object. At the end of the process
|
|
// structure is an array of pointers to each server DLL's per process
|
|
// data. The per process data is contained in the memory after the
|
|
// array.
|
|
//
|
|
|
|
ProcessSize = (ULONG)QUAD_ALIGN(sizeof( CSR_PROCESS ) +
|
|
(CSR_MAX_SERVER_DLL * sizeof(PVOID))) + CsrTotalPerProcessDataLength;
|
|
Process = (PCSR_PROCESS)RtlAllocateHeap( CsrHeap, MAKE_TAG( PROCESS_TAG ),
|
|
ProcessSize
|
|
);
|
|
if (Process == NULL) {
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// Initialize the fields of the process object
|
|
//
|
|
|
|
RtlZeroMemory( Process, ProcessSize);
|
|
|
|
//
|
|
// grab the ProcessSequenceNumber and increment it, making sure that it
|
|
// is never less than FIRST_SEQUENCE_COUNT.
|
|
//
|
|
|
|
Process->SequenceNumber = ProcessSequenceCount++;
|
|
|
|
if (ProcessSequenceCount < FIRST_SEQUENCE_COUNT)
|
|
ProcessSequenceCount = FIRST_SEQUENCE_COUNT;
|
|
|
|
CsrLockedReferenceProcess(Process);
|
|
|
|
InitializeListHead( &Process->ThreadList );
|
|
return( Process );
|
|
}
|
|
|
|
|
|
VOID
|
|
CsrDeallocateProcess(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
RtlFreeHeap( CsrHeap, 0, Process );
|
|
}
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held when calling this routine.
|
|
//
|
|
VOID
|
|
CsrInsertProcess(
|
|
IN PCSR_PROCESS CallingProcess,
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PCSR_SERVER_DLL LoadedServerDll;
|
|
ULONG i;
|
|
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
InsertTailList( &CsrRootProcess->ListLink, &Process->ListLink );
|
|
|
|
for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
|
|
LoadedServerDll = CsrLoadedServerDll[i];
|
|
if (LoadedServerDll && LoadedServerDll->AddProcessRoutine) {
|
|
(*LoadedServerDll->AddProcessRoutine)(CallingProcess, Process);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held when calling this routine.
|
|
//
|
|
VOID
|
|
CsrRemoveProcess(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PCSR_SERVER_DLL LoadedServerDll;
|
|
ULONG i;
|
|
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
RemoveEntryList( &Process->ListLink );
|
|
ReleaseProcessStructureLock();
|
|
|
|
for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
|
|
LoadedServerDll = CsrLoadedServerDll[i];
|
|
if (LoadedServerDll && LoadedServerDll->DisconnectRoutine) {
|
|
(LoadedServerDll->DisconnectRoutine)(Process);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrCreateProcess(
|
|
IN HANDLE ProcessHandle,
|
|
IN HANDLE ThreadHandle,
|
|
IN PCLIENT_ID ClientId,
|
|
IN PCSR_NT_SESSION Session,
|
|
IN ULONG DebugFlags,
|
|
IN PCLIENT_ID DebugUserInterface OPTIONAL
|
|
)
|
|
{
|
|
PCSR_PROCESS Process;
|
|
PCSR_THREAD Thread;
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
PVOID ProcessDataPtr;
|
|
CLIENT_ID CallingClientId;
|
|
PCSR_THREAD CallingThread;
|
|
PCSR_PROCESS CallingProcess;
|
|
KERNEL_USER_TIMES TimeInfo;
|
|
|
|
CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
//
|
|
// remember the client id of the calling process.
|
|
//
|
|
|
|
CallingClientId = CallingThread->ClientId;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
//
|
|
// look for calling thread.
|
|
//
|
|
|
|
CallingThread = CsrLocateThreadByClientId(&CallingProcess, &CallingClientId);
|
|
if (CallingThread == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
|
|
Process = CsrAllocateProcess();
|
|
if (Process == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
ReleaseProcessStructureLock();
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// copy per-process data from parent to child
|
|
//
|
|
|
|
CallingProcess = (CSR_SERVER_QUERYCLIENTTHREAD())->Process;
|
|
ProcessDataPtr = (PVOID)QUAD_ALIGN(&Process->ServerDllPerProcessData[CSR_MAX_SERVER_DLL]);
|
|
for (i=0; i<CSR_MAX_SERVER_DLL; i++) {
|
|
if (CsrLoadedServerDll[i] != NULL && CsrLoadedServerDll[i]->PerProcessDataLength) {
|
|
Process->ServerDllPerProcessData[i] = ProcessDataPtr;
|
|
RtlMoveMemory(ProcessDataPtr,
|
|
CallingProcess->ServerDllPerProcessData[i],
|
|
CsrLoadedServerDll[i]->PerProcessDataLength
|
|
);
|
|
ProcessDataPtr = (PVOID)QUAD_ALIGN((PCHAR)ProcessDataPtr + CsrLoadedServerDll[i]->PerProcessDataLength);
|
|
}
|
|
else {
|
|
Process->ServerDllPerProcessData[i] = NULL;
|
|
}
|
|
}
|
|
|
|
Status = NtSetInformationProcess(
|
|
ProcessHandle,
|
|
ProcessExceptionPort,
|
|
(PVOID)&CsrApiPort,
|
|
sizeof(HANDLE)
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
CsrDeallocateProcess( Process );
|
|
ReleaseProcessStructureLock();
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
//
|
|
// If we are creating a process group, the group leader has the same
|
|
// process id and sequence number of itself. If the leader dies and
|
|
// his pid is recycled, the sequence number mismatch will prevent it
|
|
// from being viewed as a group leader.
|
|
//
|
|
|
|
if ( DebugFlags & CSR_CREATE_PROCESS_GROUP ) {
|
|
Process->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess);
|
|
Process->ProcessGroupSequence = Process->SequenceNumber;
|
|
}
|
|
else {
|
|
Process->ProcessGroupId = CallingProcess->ProcessGroupId;
|
|
Process->ProcessGroupSequence = CallingProcess->ProcessGroupSequence;
|
|
}
|
|
|
|
if ( DebugFlags & CSR_PROCESS_CONSOLEAPP ) {
|
|
Process->Flags |= CSR_PROCESS_CONSOLEAPP;
|
|
}
|
|
|
|
DebugFlags &= ~(CSR_PROCESS_CONSOLEAPP | CSR_CREATE_PROCESS_GROUP);
|
|
|
|
if ( !DebugFlags && CallingProcess->DebugFlags & CSR_DEBUG_PROCESS_TREE ) {
|
|
Process->DebugFlags = CSR_DEBUG_PROCESS_TREE;
|
|
Process->DebugUserInterface = CallingProcess->DebugUserInterface;
|
|
}
|
|
if ( DebugFlags & (CSR_DEBUG_THIS_PROCESS | CSR_DEBUG_PROCESS_TREE) &&
|
|
ARGUMENT_PRESENT(DebugUserInterface) ) {
|
|
Process->DebugFlags = DebugFlags;
|
|
Process->DebugUserInterface = *DebugUserInterface;
|
|
}
|
|
|
|
|
|
if ( Process->DebugFlags ) {
|
|
|
|
//
|
|
// Process is being debugged, so set up debug port
|
|
//
|
|
|
|
Status = NtSetInformationProcess(
|
|
ProcessHandle,
|
|
ProcessDebugPort,
|
|
(PVOID)&CsrApiPort,
|
|
sizeof(HANDLE)
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
CsrDeallocateProcess( Process );
|
|
ReleaseProcessStructureLock();
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
}
|
|
//
|
|
// capture the thread's createtime so that we can use
|
|
// this as a sequence number
|
|
//
|
|
|
|
Status = NtQueryInformationThread(
|
|
ThreadHandle,
|
|
ThreadTimes,
|
|
(PVOID)&TimeInfo,
|
|
sizeof(TimeInfo),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
CsrDeallocateProcess( Process );
|
|
ReleaseProcessStructureLock();
|
|
return( Status );
|
|
}
|
|
|
|
Thread = CsrAllocateThread( Process );
|
|
if (Thread == NULL) {
|
|
CsrDeallocateProcess( Process );
|
|
ReleaseProcessStructureLock();
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
Thread->CreateTime = TimeInfo.CreateTime;
|
|
|
|
Thread->ClientId = *ClientId;
|
|
Thread->ThreadHandle = ThreadHandle;
|
|
|
|
ProtectHandle(ThreadHandle);
|
|
|
|
Thread->Flags = 0;
|
|
CsrInsertThread( Process, Thread );
|
|
|
|
CsrReferenceNtSession(Session);
|
|
Process->NtSession = Session;
|
|
|
|
Process->ClientId = *ClientId;
|
|
Process->ProcessHandle = ProcessHandle;
|
|
|
|
CsrSetBackgroundPriority(Process);
|
|
|
|
Process->ShutdownLevel = 0x00000280;
|
|
|
|
CsrInsertProcess((CSR_SERVER_QUERYCLIENTTHREAD())->Process, Process);
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CsrDestroyProcess(
|
|
IN PCLIENT_ID ClientId,
|
|
IN NTSTATUS ExitStatus
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_THREAD DyingThread;
|
|
PCSR_PROCESS DyingProcess;
|
|
|
|
CLIENT_ID DyingClientId;
|
|
|
|
DyingClientId = *ClientId;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
|
|
DyingThread = CsrLocateThreadByClientId( &DyingProcess,
|
|
&DyingClientId
|
|
);
|
|
if (DyingThread == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
|
|
//
|
|
// prevent multiple destroys from causing problems. Scottlu and Markl
|
|
// beleive all known race conditions are now fixed. This is simply a
|
|
// precaution since we know that if this happens we process reference
|
|
// count underflow
|
|
//
|
|
|
|
if ( DyingProcess->Flags & CSR_PROCESS_DESTROYED ) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
|
|
DyingProcess->Flags |= CSR_PROCESS_DESTROYED;
|
|
|
|
ListHead = &DyingProcess->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
DyingThread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
|
|
if ( DyingThread->Flags & CSR_THREAD_DESTROYED ) {
|
|
ListNext = ListNext->Flink;
|
|
continue;
|
|
}
|
|
else {
|
|
DyingThread->Flags |= CSR_THREAD_DESTROYED;
|
|
}
|
|
AcquireWaitListsLock();
|
|
if (DyingThread->WaitBlock != NULL) {
|
|
CsrNotifyWaitBlock(DyingThread->WaitBlock,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
CSR_PROCESS_TERMINATING,
|
|
TRUE
|
|
);
|
|
}
|
|
ReleaseWaitListsLock();
|
|
CsrLockedDereferenceThread(DyingThread);
|
|
ListNext = ListHead->Flink;
|
|
}
|
|
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CsrCreateThread(
|
|
IN PCSR_PROCESS Process,
|
|
IN HANDLE ThreadHandle,
|
|
IN PCLIENT_ID ClientId,
|
|
IN BOOLEAN ValidateCaller
|
|
)
|
|
{
|
|
PCSR_THREAD Thread;
|
|
CLIENT_ID CallingClientId;
|
|
PCSR_THREAD CallingThread;
|
|
PCSR_PROCESS CallingProcess;
|
|
KERNEL_USER_TIMES TimeInfo;
|
|
NTSTATUS Status;
|
|
|
|
if (ValidateCaller)
|
|
{
|
|
CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
//
|
|
// remember the client id of the calling process.
|
|
//
|
|
|
|
CallingClientId = CallingThread->ClientId;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
//
|
|
// look for calling thread.
|
|
//
|
|
|
|
CallingThread = CsrLocateThreadByClientId( &CallingProcess,
|
|
&CallingClientId
|
|
);
|
|
if (CallingThread == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
} else {
|
|
AcquireProcessStructureLock();
|
|
}
|
|
|
|
Status = NtQueryInformationThread(
|
|
ThreadHandle,
|
|
ThreadTimes,
|
|
(PVOID)&TimeInfo,
|
|
sizeof(TimeInfo),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
ReleaseProcessStructureLock();
|
|
return( Status );
|
|
}
|
|
|
|
if (Process->Flags & CSR_PROCESS_DESTROYED) {
|
|
IF_DEBUG {
|
|
DbgPrint("CSRSS: CsrCreateThread - process %p is destroyed\n", Process);
|
|
}
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
|
|
Thread = CsrAllocateThread( Process );
|
|
if (Thread == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
|
|
Thread->CreateTime = TimeInfo.CreateTime;
|
|
|
|
Thread->ClientId = *ClientId;
|
|
Thread->ThreadHandle = ThreadHandle;
|
|
|
|
ProtectHandle(ThreadHandle);
|
|
|
|
Thread->Flags = 0;
|
|
CsrInsertThread( Process, Thread );
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrCreateRemoteThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN PCLIENT_ID ClientId
|
|
)
|
|
{
|
|
PCSR_THREAD Thread;
|
|
PCSR_PROCESS Process;
|
|
NTSTATUS Status;
|
|
HANDLE hThread;
|
|
KERNEL_USER_TIMES TimeInfo;
|
|
|
|
Status = NtQueryInformationThread(
|
|
ThreadHandle,
|
|
ThreadTimes,
|
|
(PVOID)&TimeInfo,
|
|
sizeof(TimeInfo),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return( Status );
|
|
}
|
|
|
|
Status = CsrLockProcessByClientId( ClientId->UniqueProcess,
|
|
&Process
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
return( Status );
|
|
}
|
|
|
|
//
|
|
// Don't create the thread structure if the thread
|
|
// has already terminated.
|
|
//
|
|
|
|
if ( TimeInfo.ExitTime.QuadPart != 0 ) {
|
|
CsrUnlockProcess( Process );
|
|
return( STATUS_THREAD_IS_TERMINATING );
|
|
}
|
|
|
|
Thread = CsrAllocateThread( Process );
|
|
if (Thread == NULL) {
|
|
CsrUnlockProcess( Process );
|
|
return( STATUS_NO_MEMORY );
|
|
}
|
|
Status = NtDuplicateObject(
|
|
NtCurrentProcess(),
|
|
ThreadHandle,
|
|
NtCurrentProcess(),
|
|
&hThread,
|
|
0L,
|
|
0L,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
hThread = ThreadHandle;
|
|
}
|
|
|
|
Thread->CreateTime = TimeInfo.CreateTime;
|
|
|
|
Thread->ClientId = *ClientId;
|
|
Thread->ThreadHandle = hThread;
|
|
|
|
ProtectHandle(hThread);
|
|
|
|
Thread->Flags = 0;
|
|
CsrInsertThread( Process, Thread );
|
|
CsrUnlockProcess( Process );
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CsrDestroyThread(
|
|
IN PCLIENT_ID ClientId
|
|
)
|
|
{
|
|
CLIENT_ID DyingClientId;
|
|
PCSR_THREAD DyingThread;
|
|
PCSR_PROCESS DyingProcess;
|
|
|
|
DyingClientId = *ClientId;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
DyingThread = CsrLocateThreadByClientId( &DyingProcess,
|
|
&DyingClientId
|
|
);
|
|
if (DyingThread == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
|
|
if ( DyingThread->Flags & CSR_THREAD_DESTROYED ) {
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_THREAD_IS_TERMINATING;
|
|
}
|
|
else {
|
|
DyingThread->Flags |= CSR_THREAD_DESTROYED;
|
|
}
|
|
|
|
AcquireWaitListsLock();
|
|
if (DyingThread->WaitBlock != NULL) {
|
|
CsrNotifyWaitBlock(DyingThread->WaitBlock,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
CSR_PROCESS_TERMINATING,
|
|
TRUE
|
|
);
|
|
}
|
|
ReleaseWaitListsLock();
|
|
CsrLockedDereferenceThread(DyingThread);
|
|
|
|
ReleaseProcessStructureLock();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PCSR_THREAD
|
|
CsrAllocateThread(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
PCSR_THREAD Thread;
|
|
ULONG ThreadSize;
|
|
|
|
//
|
|
// Allocate an Windows Thread Object.
|
|
//
|
|
|
|
ThreadSize = QUAD_ALIGN(sizeof( CSR_THREAD ));
|
|
Thread = (PCSR_THREAD)RtlAllocateHeap( CsrHeap, MAKE_TAG( THREAD_TAG ),
|
|
ThreadSize
|
|
);
|
|
if (Thread == NULL) {
|
|
return( NULL );
|
|
}
|
|
|
|
//
|
|
// Initialize the fields of the thread object
|
|
//
|
|
|
|
RtlZeroMemory( Thread, ThreadSize );
|
|
|
|
CsrLockedReferenceThread(Thread);
|
|
CsrLockedReferenceProcess(Process);
|
|
Thread->Process = Process;
|
|
|
|
return( Thread );
|
|
}
|
|
|
|
|
|
VOID
|
|
CsrDeallocateThread(
|
|
IN PCSR_THREAD Thread
|
|
)
|
|
{
|
|
ASSERT (Thread->WaitBlock == NULL);
|
|
RtlFreeHeap( CsrHeap, 0, Thread );
|
|
}
|
|
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held while calling this routine.
|
|
//
|
|
VOID
|
|
CsrInsertThread(
|
|
IN PCSR_PROCESS Process,
|
|
IN PCSR_THREAD Thread)
|
|
{
|
|
ULONG i;
|
|
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
InsertTailList(&Process->ThreadList, &Thread->Link);
|
|
Process->ThreadCount++;
|
|
i = THREAD_ID_TO_HASH(Thread->ClientId.UniqueThread);
|
|
InsertHeadList(&CsrThreadHashTable[i], &Thread->HashLinks);
|
|
}
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held while calling this routine.
|
|
//
|
|
VOID
|
|
CsrRemoveThread(
|
|
IN PCSR_THREAD Thread)
|
|
{
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
RemoveEntryList(&Thread->Link);
|
|
Thread->Process->ThreadCount--;
|
|
|
|
if (Thread->HashLinks.Flink) {
|
|
RemoveEntryList(&Thread->HashLinks);
|
|
}
|
|
|
|
//
|
|
// If this is the last thread, then make sure we undo the reference
|
|
// that this thread had on the process.
|
|
//
|
|
if (Thread->Process->ThreadCount == 0) {
|
|
if (!(Thread->Process->Flags & CSR_PROCESS_LASTTHREADOK)) {
|
|
Thread->Process->Flags |= CSR_PROCESS_LASTTHREADOK;
|
|
CsrLockedDereferenceProcess(Thread->Process);
|
|
}
|
|
}
|
|
|
|
Thread->Flags |= CSR_THREAD_TERMINATING;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CsrLockProcessByClientId(
|
|
IN HANDLE UniqueProcessId,
|
|
OUT PCSR_PROCESS *Process
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_PROCESS ProcessPtr;
|
|
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT( Process != NULL );
|
|
*Process = NULL;
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
ListHead = &CsrRootProcess->ListLink;
|
|
ListNext = ListHead;
|
|
do {
|
|
ProcessPtr = CONTAINING_RECORD( ListNext, CSR_PROCESS, ListLink );
|
|
if (ProcessPtr->ClientId.UniqueProcess == UniqueProcessId) {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
ListNext = ListNext->Flink;
|
|
} while (ListNext != ListHead);
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
CsrLockedReferenceProcess(ProcessPtr);
|
|
*Process = ProcessPtr;
|
|
}
|
|
else {
|
|
ReleaseProcessStructureLock();
|
|
}
|
|
|
|
return( Status );
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrUnlockProcess(
|
|
IN PCSR_PROCESS Process
|
|
)
|
|
{
|
|
CsrLockedDereferenceProcess( Process );
|
|
ReleaseProcessStructureLock();
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrLockThreadByClientId(
|
|
IN HANDLE UniqueThreadId,
|
|
OUT PCSR_THREAD *Thread)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Index;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_THREAD ThreadPtr;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT(Thread != NULL);
|
|
|
|
Index = THREAD_ID_TO_HASH(UniqueThreadId);
|
|
|
|
ListHead = &CsrThreadHashTable[Index];
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
ThreadPtr = CONTAINING_RECORD( ListNext, CSR_THREAD, HashLinks );
|
|
if (ThreadPtr->ClientId.UniqueThread == UniqueThreadId &&
|
|
!(ThreadPtr->Flags & CSR_THREAD_DESTROYED)) {
|
|
break;
|
|
}
|
|
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
if (ListNext != ListHead) {
|
|
*Thread = ThreadPtr;
|
|
Status = STATUS_SUCCESS;
|
|
CsrLockedReferenceThread(ThreadPtr);
|
|
} else {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
ReleaseProcessStructureLock();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held while calling this routine.
|
|
//
|
|
NTSTATUS
|
|
CsrUnlockThread(
|
|
IN PCSR_THREAD Thread)
|
|
{
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
CsrLockedDereferenceThread(Thread);
|
|
ReleaseProcessStructureLock();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held while calling this routine.
|
|
//
|
|
PCSR_THREAD
|
|
CsrLocateThreadByClientId(
|
|
OUT PCSR_PROCESS *Process OPTIONAL,
|
|
IN PCLIENT_ID ClientId)
|
|
{
|
|
ULONG Index;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_THREAD Thread;
|
|
|
|
ASSERT(ProcessStructureListLocked());
|
|
|
|
Index = THREAD_ID_TO_HASH(ClientId->UniqueThread);
|
|
|
|
if (ARGUMENT_PRESENT(Process)) {
|
|
*Process = NULL;
|
|
}
|
|
|
|
ListHead = &CsrThreadHashTable[Index];
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, HashLinks );
|
|
if (Thread->ClientId.UniqueThread == ClientId->UniqueThread &&
|
|
Thread->ClientId.UniqueProcess == ClientId->UniqueProcess) {
|
|
if (ARGUMENT_PRESENT(Process)) {
|
|
*Process = Thread->Process;
|
|
}
|
|
|
|
return Thread;
|
|
}
|
|
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// NOTE: The process structure lock must be held while calling this routine.
|
|
//
|
|
PCSR_THREAD
|
|
CsrLocateServerThread(
|
|
IN PCLIENT_ID ClientId)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_THREAD Thread;
|
|
|
|
ListHead = &CsrRootProcess->ThreadList;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Thread = CONTAINING_RECORD( ListNext, CSR_THREAD, Link );
|
|
if (Thread->ClientId.UniqueThread == ClientId->UniqueThread) {
|
|
return Thread;
|
|
}
|
|
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
CsrImpersonateClient(
|
|
IN PCSR_THREAD Thread)
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_THREAD CallingThread;
|
|
|
|
CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
if (Thread == NULL) {
|
|
Thread = CallingThread;
|
|
if (Thread == NULL) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Status = NtImpersonateThread(NtCurrentThread(),
|
|
Thread->ThreadHandle,
|
|
&CsrSecurityQos);
|
|
if (!NT_SUCCESS(Status)) {
|
|
IF_DEBUG {
|
|
DbgPrint("CSRSS: Can't impersonate client thread - Status = %lx\n",
|
|
Status);
|
|
if (Status != STATUS_BAD_IMPERSONATION_LEVEL) {
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Keep track of recursion by printer drivers.
|
|
//
|
|
if (CallingThread != NULL) {
|
|
++CallingThread->ImpersonateCount;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
CsrRevertToSelf(
|
|
VOID)
|
|
{
|
|
HANDLE NewToken;
|
|
NTSTATUS Status;
|
|
PCSR_THREAD CallingThread;
|
|
|
|
CallingThread = CSR_SERVER_QUERYCLIENTTHREAD();
|
|
|
|
//
|
|
// Keep track of recursion by printer drivers.
|
|
//
|
|
if (CallingThread != NULL) {
|
|
if (CallingThread->ImpersonateCount == 0) {
|
|
IF_DEBUG {
|
|
DbgPrint( "CSRSS: CsrRevertToSelf called while not impersonating\n" );
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (--CallingThread->ImpersonateCount > 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
NewToken = NULL;
|
|
Status = NtSetInformationThread(NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID)&NewToken,
|
|
(ULONG)sizeof(HANDLE));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function must be called by client DLL's whenever they create a
|
|
thread that runs in the context of CSR. This function is not called
|
|
for server threads that are attached to a client in the "server
|
|
handle" field. This function replaces the old static thread tables.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Supplies a handle to the thread.
|
|
|
|
ClientId - Supplies the address of the thread's client id.
|
|
|
|
Flags - Not Used.
|
|
|
|
Return Value:
|
|
|
|
Returns the address of the static server thread created by this
|
|
function.
|
|
|
|
--*/
|
|
|
|
PVOID
|
|
CsrAddStaticServerThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN PCLIENT_ID ClientId,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
PCSR_THREAD Thread;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT(CsrRootProcess != NULL);
|
|
Thread = CsrAllocateThread(CsrRootProcess);
|
|
if (Thread) {
|
|
Thread->ThreadHandle = ThreadHandle;
|
|
|
|
ProtectHandle(ThreadHandle);
|
|
|
|
Thread->ClientId = *ClientId;
|
|
Thread->Flags = Flags;
|
|
InsertTailList(&CsrRootProcess->ThreadList, &Thread->Link);
|
|
CsrRootProcess->ThreadCount++;
|
|
}
|
|
|
|
ReleaseProcessStructureLock();
|
|
return (PVOID)Thread;
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrExecServerThread(
|
|
IN PUSER_THREAD_START_ROUTINE StartAddress,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
PCSR_THREAD Thread;
|
|
NTSTATUS Status;
|
|
HANDLE ThreadHandle;
|
|
CLIENT_ID ClientId;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT(CsrRootProcess != NULL);
|
|
Thread = CsrAllocateThread(CsrRootProcess);
|
|
if (Thread == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
0,
|
|
0,
|
|
(PUSER_THREAD_START_ROUTINE)StartAddress,
|
|
NULL,
|
|
&ThreadHandle,
|
|
&ClientId);
|
|
if (NT_SUCCESS(Status)) {
|
|
Thread->ThreadHandle = ThreadHandle;
|
|
|
|
ProtectHandle(ThreadHandle);
|
|
|
|
Thread->ClientId = ClientId;
|
|
Thread->Flags = Flags;
|
|
InsertTailList(&CsrRootProcess->ThreadList, &Thread->Link);
|
|
CsrRootProcess->ThreadCount++;
|
|
} else {
|
|
CsrDeallocateThread(Thread);
|
|
}
|
|
|
|
Exit:
|
|
ReleaseProcessStructureLock();
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CsrReferenceThread(
|
|
PCSR_THREAD t
|
|
)
|
|
{
|
|
ASSERT (t != NULL);
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT((t->Flags & CSR_THREAD_DESTROYED) == 0);
|
|
ASSERT(t->ReferenceCount != 0);
|
|
|
|
t->ReferenceCount++;
|
|
ReleaseProcessStructureLock();
|
|
}
|
|
|
|
VOID
|
|
CsrProcessRefcountZero(
|
|
PCSR_PROCESS p
|
|
)
|
|
{
|
|
ASSERT (p != NULL);
|
|
|
|
CsrRemoveProcess(p);
|
|
if (p->NtSession) {
|
|
CsrDereferenceNtSession(p->NtSession,0);
|
|
}
|
|
|
|
//
|
|
// process might not have made it through dll init routine.
|
|
//
|
|
|
|
if ( p->ClientPort ) {
|
|
NtClose(p->ClientPort);
|
|
}
|
|
NtClose(p->ProcessHandle );
|
|
CsrDeallocateProcess(p);
|
|
}
|
|
|
|
VOID
|
|
CsrDereferenceProcess(
|
|
PCSR_PROCESS p
|
|
)
|
|
{
|
|
LONG LockCount;
|
|
|
|
ASSERT (p != NULL);
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
LockCount = --(p->ReferenceCount);
|
|
|
|
ASSERT(LockCount >= 0);
|
|
if ( !LockCount ) {
|
|
CsrProcessRefcountZero(p);
|
|
} else {
|
|
ReleaseProcessStructureLock();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CsrThreadRefcountZero(
|
|
PCSR_THREAD t
|
|
)
|
|
{
|
|
PCSR_PROCESS p;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT (t != NULL);
|
|
p = t->Process;
|
|
ASSERT (p != NULL);
|
|
|
|
CsrRemoveThread(t);
|
|
|
|
ReleaseProcessStructureLock();
|
|
|
|
UnProtectHandle(t->ThreadHandle);
|
|
Status = NtClose(t->ThreadHandle);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
CsrDeallocateThread(t);
|
|
|
|
CsrDereferenceProcess(p);
|
|
}
|
|
|
|
ULONG
|
|
CsrDereferenceThread(
|
|
PCSR_THREAD t)
|
|
{
|
|
ULONG LockCount;
|
|
|
|
|
|
ASSERT (t != NULL);
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
ASSERT(t->ReferenceCount > 0);
|
|
|
|
LockCount = --(t->ReferenceCount);
|
|
if (!LockCount) {
|
|
CsrThreadRefcountZero(t);
|
|
} else {
|
|
ReleaseProcessStructureLock();
|
|
}
|
|
|
|
return LockCount;
|
|
}
|
|
|
|
VOID
|
|
CsrLockedReferenceProcess(
|
|
PCSR_PROCESS p)
|
|
{
|
|
ASSERT (p != NULL);
|
|
p->ReferenceCount++;
|
|
}
|
|
|
|
VOID
|
|
CsrLockedReferenceThread(
|
|
PCSR_THREAD t)
|
|
{
|
|
ASSERT (t != NULL);
|
|
t->ReferenceCount++;
|
|
}
|
|
|
|
VOID
|
|
CsrLockedDereferenceProcess(
|
|
PCSR_PROCESS p)
|
|
{
|
|
LONG LockCount;
|
|
|
|
ASSERT (p != NULL);
|
|
|
|
LockCount = --(p->ReferenceCount);
|
|
|
|
ASSERT(LockCount >= 0);
|
|
if (!LockCount) {
|
|
CsrProcessRefcountZero(p);
|
|
AcquireProcessStructureLock();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CsrLockedDereferenceThread(
|
|
PCSR_THREAD t
|
|
)
|
|
{
|
|
LONG LockCount;
|
|
|
|
ASSERT (t != NULL);
|
|
|
|
LockCount = --(t->ReferenceCount);
|
|
|
|
ASSERT(LockCount >= 0);
|
|
if ( !LockCount ) {
|
|
CsrThreadRefcountZero(t);
|
|
AcquireProcessStructureLock();
|
|
}
|
|
}
|
|
|
|
//
|
|
// This routine will shutdown processes so either a logoff or a shutdown can
|
|
// occur. This simply calls the shutdown process handlers for each .dll until
|
|
// one .dll recognizes this process and will shut it down. Only the processes
|
|
// with the passed sid are shutdown.
|
|
//
|
|
|
|
NTSTATUS
|
|
CsrShutdownProcesses(
|
|
PLUID CallerLuid,
|
|
ULONG Flags
|
|
)
|
|
{
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_PROCESS Process;
|
|
ULONG i;
|
|
PCSR_SERVER_DLL LoadedServerDll;
|
|
ULONG Command;
|
|
BOOLEAN fFirstPass;
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
|
|
AcquireProcessStructureLock();
|
|
|
|
//
|
|
// Mark the root process as system context.
|
|
//
|
|
CsrRootProcess->ShutdownFlags |= SHUTDOWN_SYSTEMCONTEXT;
|
|
|
|
//
|
|
// Clear all the bits indicating that shutdown has visited the existing
|
|
// processes.
|
|
//
|
|
ListHead = &CsrRootProcess->ListLink;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Process = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
|
|
Process->Flags &= ~CSR_PROCESS_SHUTDOWNSKIP;
|
|
Process->ShutdownFlags = 0;
|
|
ListNext = ListNext->Flink;
|
|
}
|
|
|
|
CsrpSetToShutdownPriority();
|
|
while (TRUE) {
|
|
//
|
|
// Find the next process to shutdown.
|
|
//
|
|
Process = FindProcessForShutdown(CallerLuid);
|
|
if (Process == NULL) {
|
|
ReleaseProcessStructureLock();
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
CsrLockedReferenceProcess(Process);
|
|
|
|
fFirstPass = TRUE;
|
|
TryAgain:
|
|
for (i = 0; i < CSR_MAX_SERVER_DLL; i++) {
|
|
LoadedServerDll = CsrLoadedServerDll[i];
|
|
if (LoadedServerDll && LoadedServerDll->ShutdownProcessRoutine) {
|
|
//
|
|
// Release process structure lock before calling off.
|
|
// CSR_PROCESS structure is still reference counted.
|
|
//
|
|
ReleaseProcessStructureLock();
|
|
Command = (*LoadedServerDll->ShutdownProcessRoutine)(Process,
|
|
Flags,
|
|
fFirstPass);
|
|
AcquireProcessStructureLock();
|
|
|
|
if (Command == SHUTDOWN_KNOWN_PROCESS) {
|
|
//
|
|
// Process structure is unlocked.
|
|
//
|
|
break;
|
|
} else if (Command == SHUTDOWN_UNKNOWN_PROCESS) {
|
|
//
|
|
// Process structure is locked.
|
|
//
|
|
continue;
|
|
} else if (Command == SHUTDOWN_CANCEL) {
|
|
#if DBG
|
|
if (Flags & 4) {
|
|
DbgPrint("Process %x cancelled forced shutdown\n",
|
|
Process->ClientId.UniqueProcess);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
//
|
|
// Unlock process structure.
|
|
//
|
|
ReleaseProcessStructureLock();
|
|
Status = STATUS_CANCELLED;
|
|
goto ExitLoop;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// No subsystem has an exact match. Now go through them again and
|
|
// let them know there was no exact match. Some .dll should terminate
|
|
// it for us (most likely, console).
|
|
//
|
|
if (fFirstPass && Command == SHUTDOWN_UNKNOWN_PROCESS) {
|
|
fFirstPass = FALSE;
|
|
goto TryAgain;
|
|
}
|
|
|
|
//
|
|
// Dereference this process structure if nothing knows about it.
|
|
//
|
|
if (i == CSR_MAX_SERVER_DLL) {
|
|
CsrLockedDereferenceProcess(Process);
|
|
}
|
|
}
|
|
|
|
ExitLoop:
|
|
|
|
CsrpSetToNormalPriority();
|
|
|
|
return Status;
|
|
}
|
|
|
|
PCSR_PROCESS
|
|
FindProcessForShutdown(
|
|
PLUID CallerLuid)
|
|
{
|
|
LUID ProcessLuid;
|
|
LUID SystemLuid = SYSTEM_LUID;
|
|
PLIST_ENTRY ListHead, ListNext;
|
|
PCSR_PROCESS Process;
|
|
PCSR_PROCESS ProcessT = NULL;
|
|
PCSR_THREAD Thread;
|
|
ULONG dwLevel = 0;
|
|
NTSTATUS Status;
|
|
|
|
ListHead = &CsrRootProcess->ListLink;
|
|
ListNext = ListHead->Flink;
|
|
while (ListNext != ListHead) {
|
|
Process = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
|
|
ListNext = ListNext->Flink;
|
|
|
|
//
|
|
// If we've visited this process already, then skip it.
|
|
//
|
|
if (Process->Flags & CSR_PROCESS_SHUTDOWNSKIP) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// See if this process is running under the passed sid. If not, mark
|
|
// it as visited and continue.
|
|
//
|
|
Status = CsrGetProcessLuid(Process->ProcessHandle, &ProcessLuid);
|
|
if (Status == STATUS_ACCESS_DENIED && Process->ThreadCount > 0) {
|
|
//
|
|
// Impersonate one of the threads and try again.
|
|
//
|
|
Thread = CONTAINING_RECORD(Process->ThreadList.Flink, CSR_THREAD, Link);
|
|
if (CsrImpersonateClient(Thread)) {
|
|
Status = CsrGetProcessLuid(NULL, &ProcessLuid);
|
|
CsrRevertToSelf();
|
|
} else {
|
|
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// We don't have access to this process's luid, so skip it.
|
|
//
|
|
Process->Flags |= CSR_PROCESS_SHUTDOWNSKIP;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Is it equal to the system context luid? If so, we want to
|
|
// remember this because we don't terminate this process: we only
|
|
// notify them.
|
|
//
|
|
if (RtlEqualLuid(&ProcessLuid, &SystemLuid)) {
|
|
Process->ShutdownFlags |= SHUTDOWN_SYSTEMCONTEXT;
|
|
} else {
|
|
//
|
|
// See if this process's luid is the same as the luid we're supposed
|
|
// to shut down (CallerSid).
|
|
//
|
|
if (!RtlEqualLuid(&ProcessLuid, CallerLuid)) {
|
|
//
|
|
// If not equal to either, mark it as such.
|
|
//
|
|
Process->ShutdownFlags |= SHUTDOWN_OTHERCONTEXT;
|
|
}
|
|
}
|
|
|
|
if (Process->ShutdownLevel > dwLevel || ProcessT == NULL) {
|
|
dwLevel = Process->ShutdownLevel;
|
|
ProcessT = Process;
|
|
}
|
|
}
|
|
|
|
if (ProcessT != NULL) {
|
|
ProcessT->Flags |= CSR_PROCESS_SHUTDOWNSKIP;
|
|
return ProcessT;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
CsrGetProcessLuid(
|
|
HANDLE ProcessHandle,
|
|
PLUID LuidProcess
|
|
)
|
|
{
|
|
HANDLE UserToken = NULL;
|
|
PTOKEN_STATISTICS pStats;
|
|
ULONG BytesRequired;
|
|
NTSTATUS Status, CloseStatus;
|
|
|
|
if (ProcessHandle == NULL) {
|
|
|
|
//
|
|
// Check for a thread token first
|
|
//
|
|
|
|
Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE,
|
|
&UserToken);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status != STATUS_NO_TOKEN)
|
|
return Status;
|
|
|
|
//
|
|
// No thread token, go to the process
|
|
//
|
|
|
|
ProcessHandle = NtCurrentProcess();
|
|
UserToken = NULL;
|
|
}
|
|
}
|
|
|
|
if (UserToken == NULL) {
|
|
Status = NtOpenProcessToken(ProcessHandle, TOKEN_QUERY, &UserToken);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenStatistics, // TokenInformationClass
|
|
NULL, // TokenInformation
|
|
0, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
NtClose(UserToken);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pStats = (PTOKEN_STATISTICS)RtlAllocateHeap(CsrHeap, MAKE_TAG( TMP_TAG ), BytesRequired);
|
|
if (pStats == NULL) {
|
|
NtClose(UserToken);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Read in the user info
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
UserToken, // Handle
|
|
TokenStatistics, // TokenInformationClass
|
|
pStats, // TokenInformation
|
|
BytesRequired, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
//
|
|
// We're finished with the token handle
|
|
//
|
|
|
|
CloseStatus = NtClose(UserToken);
|
|
ASSERT(NT_SUCCESS(CloseStatus));
|
|
|
|
//
|
|
// Return the authentication LUID
|
|
//
|
|
|
|
*LuidProcess = pStats->AuthenticationId;
|
|
|
|
RtlFreeHeap(CsrHeap, 0, pStats);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CsrSetCallingSpooler(
|
|
BOOLEAN fSet)
|
|
{
|
|
//
|
|
// Obsolete function that may be called by third part drivers.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER(fSet);
|
|
}
|
|
|
|
//
|
|
// This routine creates a process based on a message passed in to sb port.
|
|
// Used by smss to have Posix and OS/2 apps created in the appropriate
|
|
// (terminal server) session.
|
|
//
|
|
|
|
BOOLEAN
|
|
CsrSbCreateProcess(
|
|
IN OUT PSBAPIMSG m
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
RTL_USER_PROCESS_INFORMATION ProcessInformation;
|
|
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
|
|
PROCESS_SESSION_INFORMATION ProcessInfo;
|
|
PSBCREATEPROCESS a = &(m->u.CreateProcess);
|
|
HANDLE RemoteProcess = NULL;
|
|
CLIENT_ID RemoteClientId;
|
|
UNICODE_STRING ImageFileName, DefaultLibPath, CurrentDirectory, CommandLine;
|
|
PVOID DefaultEnvironment = NULL;
|
|
PROCESS_BASIC_INFORMATION ProcInfo;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
|
|
RtlInitUnicodeString(&ImageFileName,NULL);
|
|
RtlInitUnicodeString(&DefaultLibPath,NULL);
|
|
RtlInitUnicodeString(&CurrentDirectory,NULL);
|
|
RtlInitUnicodeString(&CommandLine,NULL);
|
|
|
|
Status = NtQueryInformationProcess(NtCurrentProcess(),
|
|
ProcessBasicInformation,
|
|
&ProcInfo,
|
|
sizeof(ProcInfo),
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: NtQueryInformationProcess failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjA, NULL, 0, NULL, NULL );
|
|
RemoteClientId.UniqueProcess = (HANDLE)ProcInfo.InheritedFromUniqueProcessId;
|
|
RemoteClientId.UniqueThread = NULL;
|
|
|
|
Status = NtOpenProcess(&RemoteProcess,
|
|
PROCESS_ALL_ACCESS,
|
|
&ObjA,
|
|
&RemoteClientId);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: NtOpenProcess failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Read pointer parameters from calling process's virtual memory
|
|
//
|
|
|
|
Status = ReadUnicodeString(RemoteProcess,a->i.ImageFileName,&ImageFileName);
|
|
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: ReadUnicodeString ImageFileName failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
Status = ReadUnicodeString(RemoteProcess,a->i.DefaultLibPath,&DefaultLibPath);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: ReadUnicodeString DefaultLibPath failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
Status = ReadUnicodeString(RemoteProcess,a->i.CurrentDirectory,&CurrentDirectory);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: ReadUnicodeString CurrentDirectory failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
Status = ReadUnicodeString(RemoteProcess,a->i.CommandLine,&CommandLine);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: ReadUnicodeString CommandLine failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Copy our environment to be used by new process
|
|
//
|
|
Status = RtlCreateEnvironment(TRUE, &DefaultEnvironment);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: Can't create environemnt\n");
|
|
goto Done;
|
|
}
|
|
|
|
Status = RtlCreateProcessParameters( &ProcessParameters,
|
|
&ImageFileName,
|
|
DefaultLibPath.Length == 0 ?
|
|
NULL : &DefaultLibPath,
|
|
&CurrentDirectory,
|
|
&CommandLine,
|
|
DefaultEnvironment,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: RtlCreateProcessParameters failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
if (a->i.Flags & SMP_DEBUG_FLAG) {
|
|
ProcessParameters->DebugFlags = TRUE;
|
|
}
|
|
else {
|
|
ProcessParameters->DebugFlags = a->i.DefaultDebugFlags;
|
|
}
|
|
|
|
if ( a->i.Flags & SMP_SUBSYSTEM_FLAG ) {
|
|
ProcessParameters->Flags |= RTL_USER_PROC_RESERVE_1MB;
|
|
}
|
|
|
|
ProcessInformation.Length = sizeof( RTL_USER_PROCESS_INFORMATION );
|
|
Status = RtlCreateUserProcess( &ImageFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ProcessParameters,
|
|
NULL,
|
|
NULL,
|
|
RemoteProcess, // set smss as the parent
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
&ProcessInformation
|
|
);
|
|
|
|
RtlDestroyProcessParameters( ProcessParameters );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: RtlCreateUserProcess failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
if (IsTerminalServer()) {
|
|
|
|
//
|
|
// Set the MuSessionId in the PEB of the new process
|
|
//
|
|
|
|
ProcessInfo.SessionId = NtCurrentPeb()->SessionId;
|
|
if (ProcessInfo.SessionId) {
|
|
PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Attributes;
|
|
HANDLE DirectoryHandle = NULL;
|
|
WCHAR szSessionString[MAX_SESSION_PATH];
|
|
|
|
//
|
|
// Change the devmap of the process to per session.
|
|
//
|
|
swprintf(szSessionString, L"%ws\\%ld%ws", SESSION_ROOT, NtCurrentPeb()->SessionId, DOSDEVICES);
|
|
RtlInitUnicodeString(&UnicodeString, szSessionString);
|
|
|
|
InitializeObjectAttributes(&Attributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenDirectoryObject(&DirectoryHandle,
|
|
DIRECTORY_ALL_ACCESS,
|
|
&Attributes);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint("CSRSS: NtOpenDirectoryObject failed in CsrSbCreateProcess - status = %lx\n", Status);
|
|
goto Done;
|
|
}
|
|
|
|
ProcessDeviceMapInfo.Set.DirectoryHandle = DirectoryHandle;
|
|
Status = NtSetInformationProcess(ProcessInformation.Process,
|
|
ProcessDeviceMap,
|
|
&ProcessDeviceMapInfo.Set,
|
|
sizeof(ProcessDeviceMapInfo.Set));
|
|
if (!NT_SUCCESS( Status )) {
|
|
DbgPrint("CSRSS: NtSetInformationProcess failed in CsrSbCreateProcess - status = %lx\n", Status);
|
|
if (DirectoryHandle) {
|
|
NtClose(DirectoryHandle);
|
|
}
|
|
|
|
goto Done;
|
|
}
|
|
|
|
if (DirectoryHandle) {
|
|
NtClose(DirectoryHandle);
|
|
}
|
|
}
|
|
|
|
Status = NtSetInformationProcess( ProcessInformation.Process,
|
|
ProcessSessionInformation,
|
|
&ProcessInfo, sizeof( ProcessInfo ));
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: NtSetInformationProcess failed - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
}
|
|
|
|
if (!(a->i.Flags & SMP_DONT_START)) {
|
|
if (ProcessInformation.ImageInformation.SubSystemType !=
|
|
IMAGE_SUBSYSTEM_NATIVE
|
|
) {
|
|
NtTerminateProcess( ProcessInformation.Process,
|
|
STATUS_INVALID_IMAGE_FORMAT
|
|
);
|
|
|
|
NtWaitForSingleObject( ProcessInformation.Thread, FALSE, NULL );
|
|
|
|
NtClose( ProcessInformation.Thread );
|
|
NtClose( ProcessInformation.Process );
|
|
|
|
Status = STATUS_INVALID_IMAGE_FORMAT;
|
|
goto Done;
|
|
}
|
|
|
|
Status = NtResumeThread( ProcessInformation.Thread, NULL );
|
|
if (!NT_SUCCESS(Status)) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess - NtResumeThread failed Status %lx\n",Status );
|
|
goto Done;
|
|
}
|
|
|
|
if (!(a->i.Flags & SMP_ASYNC_FLAG)) {
|
|
NtWaitForSingleObject( ProcessInformation.Thread, FALSE, NULL );
|
|
}
|
|
|
|
NtClose( ProcessInformation.Thread );
|
|
NtClose( ProcessInformation.Process );
|
|
|
|
}
|
|
|
|
//
|
|
// Copy output parameters to message
|
|
//
|
|
a->o.SubSystemType = ProcessInformation.ImageInformation.SubSystemType;
|
|
a->o.ClientId.UniqueProcess = ProcessInformation.ClientId.UniqueProcess;
|
|
a->o.ClientId.UniqueThread = ProcessInformation.ClientId.UniqueThread;
|
|
|
|
//
|
|
// Convert handles to caller's process
|
|
//
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
ProcessInformation.Process,
|
|
RemoteProcess,
|
|
&a->o.Process,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: NtDuplicateObject failed for process - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
Status = NtDuplicateObject( NtCurrentProcess(),
|
|
ProcessInformation.Thread,
|
|
RemoteProcess,
|
|
&a->o.Thread,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
DbgPrint( "CSRSS: CsrSrvCreateProcess: NtDuplicateObject failed for thread - Status = %lx\n", Status );
|
|
goto Done;
|
|
}
|
|
|
|
Done:
|
|
if (NULL != ImageFileName.Buffer)
|
|
RtlFreeHeap(CsrHeap,0,ImageFileName.Buffer);
|
|
if (NULL != DefaultLibPath.Buffer)
|
|
RtlFreeHeap(CsrHeap,0,DefaultLibPath.Buffer);
|
|
if (NULL != CurrentDirectory.Buffer)
|
|
RtlFreeHeap(CsrHeap,0,CurrentDirectory.Buffer);
|
|
if (NULL != CommandLine.Buffer)
|
|
RtlFreeHeap(CsrHeap,0,CommandLine.Buffer);
|
|
if (NULL != RemoteProcess)
|
|
NtClose(RemoteProcess);
|
|
|
|
m->ReturnedStatus = Status;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// This routine will copy a UNICODE_STRING from a remote process to this one
|
|
//
|
|
NTSTATUS
|
|
ReadUnicodeString(HANDLE ProcessHandle,
|
|
PUNICODE_STRING RemoteString,
|
|
PUNICODE_STRING LocalString
|
|
)
|
|
{
|
|
PWSTR Buffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
RtlInitUnicodeString(LocalString, NULL);
|
|
|
|
if (NULL != RemoteString) {
|
|
Status = NtReadVirtualMemory(ProcessHandle,
|
|
RemoteString,
|
|
LocalString,
|
|
sizeof(UNICODE_STRING),
|
|
NULL);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: ReadUnicodeString: NtReadVirtualMemory failed - Status = %lx\n", Status );
|
|
return Status;
|
|
}
|
|
|
|
if ((0 != LocalString->Length) && (NULL != LocalString->Buffer)) {
|
|
Buffer = RtlAllocateHeap( CsrHeap,
|
|
MAKE_TAG( PROCESS_TAG ),
|
|
LocalString->Length + sizeof(WCHAR)
|
|
);
|
|
|
|
if (Buffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Status = NtReadVirtualMemory(ProcessHandle,
|
|
LocalString->Buffer,
|
|
Buffer,
|
|
LocalString->Length + sizeof(WCHAR),
|
|
NULL);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
DbgPrint( "CSRSS: ReadUnicodeString: NtReadVirtualMemory Buffer failed - Status = %lx\n", Status );
|
|
|
|
RtlFreeHeap(CsrHeap,0,Buffer);
|
|
LocalString->Buffer = NULL; // don't want caller to free this
|
|
|
|
return Status;
|
|
}
|
|
|
|
LocalString->Buffer = Buffer;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#if CSRSS_PROTECT_HANDLES
|
|
BOOLEAN
|
|
ProtectHandle(
|
|
HANDLE hObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
|
|
|
|
Status = NtQueryObject( hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof( HandleInfo ),
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
HandleInfo.ProtectFromClose = TRUE;
|
|
|
|
Status = NtSetInformationObject( hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof( HandleInfo )
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
UnProtectHandle(
|
|
HANDLE hObject
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_HANDLE_FLAG_INFORMATION HandleInfo;
|
|
|
|
Status = NtQueryObject( hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof( HandleInfo ),
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
HandleInfo.ProtectFromClose = FALSE;
|
|
|
|
Status = NtSetInformationObject( hObject,
|
|
ObjectHandleFlagInformation,
|
|
&HandleInfo,
|
|
sizeof( HandleInfo )
|
|
);
|
|
if (NT_SUCCESS( Status )) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif // CSRSS_PROTECT_HANDLES
|