windows-nt/Source/XPSP1/NT/base/ntos/ob/obclose.c
2020-09-26 16:20:57 +08:00

558 lines
13 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}