/*++ 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 // 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; iAddProcessRoutine) { (*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; iDisconnectRoutine) { (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; iPerProcessDataLength) { 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