558 lines
13 KiB
C
558 lines
13 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
obclose.c
|
||
|
||
Abstract:
|
||
|
||
Object close system service
|
||
|
||
Author:
|
||
|
||
Steve Wood (stevewo) 31-Mar-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "obp.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,NtMakeTemporaryObject)
|
||
#pragma alloc_text(PAGE,NtClose)
|
||
#pragma alloc_text(PAGE,ObMakeTemporaryObject)
|
||
#pragma alloc_text(PAGE,ObpCloseHandleTableEntry)
|
||
#pragma alloc_text(PAGE,ObCloseHandle)
|
||
#pragma alloc_text(PAGE,ObpCloseHandle)
|
||
#endif
|
||
|
||
//
|
||
// Indicates if auditing is enabled so we have to close down the object
|
||
// audit alarm
|
||
//
|
||
|
||
extern BOOLEAN SepAdtAuditingEnabled;
|
||
|
||
ObpCloseHandleTableEntry (
|
||
IN PHANDLE_TABLE ObjectTable,
|
||
IN PHANDLE_TABLE_ENTRY ObjectTableEntry,
|
||
IN HANDLE Handle,
|
||
IN KPROCESSOR_MODE PreviousMode,
|
||
IN BOOLEAN Rundown,
|
||
IN BOOLEAN CanNotRaise
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to close a handle table entry
|
||
|
||
Arguments:
|
||
|
||
ObjectTableEntry - Supplies the entry being closed. It must be locked
|
||
PreviousMode - Mode of caller
|
||
Rundown - Called as part of process rundown, ignore protected handles in this mode
|
||
CanNotRaise - Can not raise user exceptions
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
{
|
||
POBJECT_HEADER ObjectHeader;
|
||
POBJECT_TYPE ObjectType;
|
||
PVOID Object;
|
||
ULONG CapturedGrantedAccess;
|
||
ULONG CapturedAttributes;
|
||
#if DBG
|
||
KIRQL SaveIrql;
|
||
#endif // DBG
|
||
|
||
//
|
||
// From the object table entry we can grab a pointer to the object
|
||
// header, get its type and its body
|
||
//
|
||
|
||
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
||
ObjectType = ObjectHeader->Type;
|
||
Object = &ObjectHeader->Body;
|
||
|
||
//
|
||
// If the object type specifies an okay to close procedure then we
|
||
// need to invoke that callback. If the callback doesn't want us to
|
||
// close handle then unlock the object table and return the error
|
||
// to our caller
|
||
//
|
||
|
||
if (ObjectType->TypeInfo.OkayToCloseProcedure != NULL) {
|
||
|
||
ObpBeginTypeSpecificCallOut( SaveIrql );
|
||
|
||
if (!(*ObjectType->TypeInfo.OkayToCloseProcedure)( PsGetCurrentProcess(),
|
||
Object,
|
||
Handle,
|
||
PreviousMode )) {
|
||
|
||
ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object );
|
||
|
||
ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );
|
||
|
||
return STATUS_HANDLE_NOT_CLOSABLE;
|
||
}
|
||
|
||
ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object );
|
||
}
|
||
|
||
CapturedAttributes = ObpGetHandleAttributes(ObjectTableEntry);
|
||
|
||
//
|
||
// If the previous mode was user and the handle is protected from
|
||
// being closed, then we'll either raise or return an error depending
|
||
// on the global flags and debugger port situation.
|
||
//
|
||
|
||
if ((CapturedAttributes & OBJ_PROTECT_CLOSE) != 0 && Rundown == FALSE) {
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
PETHREAD CurrentThread;
|
||
|
||
ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
|
||
if (!CanNotRaise && !KeIsAttachedProcess() &&
|
||
!PsIsThreadTerminating (CurrentThread) &&
|
||
!CurrentThread->Tcb.ApcState.KernelApcInProgress &&
|
||
((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) ||
|
||
(PsGetCurrentProcess()->DebugPort != NULL))) {
|
||
|
||
//
|
||
// Raise and exception in user mode
|
||
//
|
||
return KeRaiseUserException(STATUS_HANDLE_NOT_CLOSABLE);
|
||
|
||
} else {
|
||
|
||
return STATUS_HANDLE_NOT_CLOSABLE;
|
||
}
|
||
|
||
} else {
|
||
KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 0, 0, 0);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the granted access for the handle
|
||
//
|
||
|
||
#if i386
|
||
|
||
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
||
|
||
CapturedGrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );
|
||
|
||
} else {
|
||
|
||
CapturedGrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
||
}
|
||
|
||
#else
|
||
|
||
CapturedGrantedAccess = ObpDecodeGrantedAccess(ObjectTableEntry->GrantedAccess);
|
||
|
||
#endif // i386
|
||
|
||
//
|
||
// Now remove the handle from the handle table
|
||
//
|
||
|
||
ExDestroyHandle( ObjectTable,
|
||
Handle,
|
||
ObjectTableEntry );
|
||
|
||
//
|
||
// perform any auditing required
|
||
//
|
||
|
||
//
|
||
// Extract the value of the GenerateOnClose bit stored
|
||
// after object open auditing is performed. This value
|
||
// was stored by a call to ObSetGenerateOnClosed.
|
||
//
|
||
|
||
if (CapturedAttributes & OBJ_AUDIT_OBJECT_CLOSE) {
|
||
|
||
if ( SepAdtAuditingEnabled ) {
|
||
|
||
SeCloseObjectAuditAlarm( Object,
|
||
(HANDLE)((ULONG_PTR)Handle & ~OBJ_HANDLE_TAGBITS), // Mask off the tagbits defined for OB objects.
|
||
TRUE );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Since we took the handle away we need to decrement the objects
|
||
// handle count, and remove a reference
|
||
//
|
||
|
||
ObpDecrementHandleCount( PsGetCurrentProcess(),
|
||
ObjectHeader,
|
||
ObjectType,
|
||
CapturedGrantedAccess );
|
||
|
||
ObDereferenceObject( Object );
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ObpCloseHandle (
|
||
IN HANDLE Handle,
|
||
IN KPROCESSOR_MODE PreviousMode,
|
||
IN BOOLEAN CanNotRaise
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to close access to the specified handle with the given mode
|
||
|
||
Arguments:
|
||
|
||
Handle - Supplies the handle being closed
|
||
PreviousMode - Processor mode to be used in the handle access checks.
|
||
CanNotRaise - We are not allowed to do a user mode raise.
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
{
|
||
PHANDLE_TABLE ObjectTable;
|
||
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
||
NTSTATUS Status;
|
||
BOOLEAN AttachedToProcess = FALSE;
|
||
KAPC_STATE ApcState;
|
||
PETHREAD CurrentThread;
|
||
PEPROCESS CurrentProcess;
|
||
|
||
|
||
ObpValidateIrql( "NtClose" );
|
||
|
||
CurrentThread = PsGetCurrentThread ();
|
||
CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
|
||
//
|
||
// For the current process we will grab its handle/object table and
|
||
// translate the handle to its corresponding table entry. If the
|
||
// call is successful it also lock down the handle table. But first
|
||
// check for a kernel handle and attach and use that table if so.
|
||
//
|
||
|
||
if (IsKernelHandle( Handle, PreviousMode )) {
|
||
|
||
Handle = DecodeKernelHandle( Handle );
|
||
|
||
ObjectTable = ObpKernelHandleTable;
|
||
|
||
//
|
||
// Go to the system process if we have to
|
||
//
|
||
if (CurrentProcess != PsInitialSystemProcess) {
|
||
KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
|
||
AttachedToProcess = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
ObjectTable = CurrentProcess->ObjectTable;
|
||
}
|
||
|
||
//
|
||
// Protect ourselves from being interrupted while we hold a handle table
|
||
// entry lock
|
||
//
|
||
|
||
KeEnterCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
ObjectTableEntry = ExMapHandleToPointer( ObjectTable,
|
||
Handle );
|
||
|
||
//
|
||
// Check that the specified handle is legitimate otherwise we can
|
||
// assume the caller just passed in some bogus handle value
|
||
//
|
||
|
||
if (ObjectTableEntry != NULL) {
|
||
|
||
Status = ObpCloseHandleTableEntry (ObjectTable, ObjectTableEntry, Handle, PreviousMode, FALSE, CanNotRaise);
|
||
|
||
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
||
//
|
||
// If we are attached to the system process then detach
|
||
//
|
||
if (AttachedToProcess) {
|
||
|
||
KeUnstackDetachProcess(&ApcState);
|
||
AttachedToProcess = FALSE;
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
||
|
||
//
|
||
// At this point the input handle did not translate to a valid
|
||
// object table entry
|
||
//
|
||
|
||
//
|
||
// If we are attached to the system process then return
|
||
// back to our caller
|
||
//
|
||
|
||
if (AttachedToProcess) {
|
||
KeUnstackDetachProcess(&ApcState);
|
||
AttachedToProcess = FALSE;
|
||
}
|
||
|
||
//
|
||
// Now if the handle is not null and it does not represent the
|
||
// current thread or process then if we're user mode we either raise
|
||
// or return an error
|
||
//
|
||
|
||
if ((Handle != NULL) &&
|
||
(Handle != NtCurrentThread()) &&
|
||
(Handle != NtCurrentProcess())) {
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
if ((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) ||
|
||
(CurrentProcess->DebugPort != NULL)) {
|
||
|
||
if (!CanNotRaise && !KeIsAttachedProcess() &&
|
||
!PsIsThreadTerminating (CurrentThread) &&
|
||
!CurrentThread->Tcb.ApcState.KernelApcInProgress) {
|
||
return KeRaiseUserException (STATUS_INVALID_HANDLE);
|
||
} else {
|
||
return STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// bugcheck here if kernel debugger is enabled and if kernel mode code is
|
||
// closing a bogus handle and process is not exiting. Ignore
|
||
// if no PEB as this occurs if process is killed before
|
||
// really starting.
|
||
//
|
||
|
||
if (( !PsIsThreadTerminating(CurrentThread)) &&
|
||
(CurrentProcess->Peb != NULL)) {
|
||
|
||
if (KdDebuggerEnabled) {
|
||
KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 1, 0, 0);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
Status = STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ObCloseHandle (
|
||
IN HANDLE Handle,
|
||
IN KPROCESSOR_MODE PreviousMode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to close access to the specified handle with the given mode
|
||
|
||
Arguments:
|
||
|
||
Handle - Supplies the handle being closed
|
||
PreviousMode - Processor mode to be used in the handle access checks.
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
{
|
||
return ObpCloseHandle (Handle,
|
||
PreviousMode,
|
||
TRUE);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtClose (
|
||
IN HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to close access to the specified handle
|
||
|
||
Arguments:
|
||
|
||
Handle - Supplies the handle being closed
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
return ObpCloseHandle (Handle,
|
||
KeGetPreviousMode (),
|
||
FALSE);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtMakeTemporaryObject (
|
||
IN HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine makes the specified object non permanent.
|
||
|
||
Arguments:
|
||
|
||
Handle - Supplies a handle to the object being modified
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value.
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
PVOID Object;
|
||
OBJECT_HANDLE_INFORMATION HandleInformation;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and probe output argument if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
Status = ObReferenceObjectByHandle( Handle,
|
||
DELETE,
|
||
(POBJECT_TYPE)NULL,
|
||
PreviousMode,
|
||
&Object,
|
||
&HandleInformation );
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Make the object temporary. Note that the object should still
|
||
// have a name and directory entry because its handle count is not
|
||
// zero
|
||
//
|
||
|
||
ObMakeTemporaryObject( Object );
|
||
|
||
//
|
||
// Check if we need to generate a delete object audit/alarm
|
||
//
|
||
|
||
if (HandleInformation.HandleAttributes & OBJ_AUDIT_OBJECT_CLOSE) {
|
||
|
||
SeDeleteObjectAuditAlarm( Object,
|
||
Handle );
|
||
}
|
||
|
||
ObDereferenceObject( Object );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
VOID
|
||
ObMakeTemporaryObject (
|
||
IN PVOID Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes the name of the object from its parent
|
||
directory. The object is only removed if it has a non zero
|
||
handle count and a name. Otherwise the object is simply
|
||
made non permanent
|
||
|
||
Arguments:
|
||
|
||
Object - Supplies the object being modified
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POBJECT_HEADER ObjectHeader;
|
||
POBJECT_TYPE ObjectType;
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
||
ObjectType = ObjectHeader->Type;
|
||
|
||
//
|
||
// Other bits are set in this flags field by the handle database code. Synchronise with that.
|
||
//
|
||
ObpLockObject( ObjectHeader );
|
||
|
||
ObjectHeader->Flags &= ~OB_FLAG_PERMANENT_OBJECT;
|
||
|
||
ObpUnlockObject( ObjectHeader );
|
||
|
||
//
|
||
// Now delete the object name if no more handles are present.
|
||
//
|
||
ObpDeleteNameCheck( Object );
|
||
|
||
return;
|
||
}
|
||
|