Windows2000/private/ntos/ob/obclose.c
2020-09-30 17:12:32 +02:00

520 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,ObMakeTemporaryObject)
#endif
// Indicates if auditing is enabled so we have to close down the object
// audit alarm
extern BOOLEAN SepAdtAuditingEnabled;
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
--*/
{
PHANDLE_TABLE ObjectTable;
PHANDLE_TABLE_ENTRY ObjectTableEntry;
PVOID Object;
ULONG CapturedGrantedAccess;
ULONG CapturedAttributes;
POBJECT_HEADER ObjectHeader;
POBJECT_TYPE ObjectType;
NTSTATUS Status;
BOOLEAN AttachedToProcess = FALSE;
KAPC_STATE ApcState;
// Protect ourselves from being interrupted while we hold a handle table
// entry lock
KeEnterCriticalRegion();
try {
#if DBG
KIRQL SaveIrql;
#endif // DBG
ObpValidateIrql( "NtClose" );
ObpBeginTypeSpecificCallOut( SaveIrql );
#if DBG
// On checked builds, check that if the Kernel handle bit is set, then
// we're coming from Kernel mode. We should probably fail the call if
// bit set && !Kmode
if ((Handle != NtCurrentThread()) && (Handle != NtCurrentProcess())) {
ASSERT((Handle < 0 ) ? (KeGetPreviousMode() == KernelMode) : TRUE);
}
#endif
// 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, KeGetPreviousMode() )) {
Handle = DecodeKernelHandle( Handle );
ObjectTable = ObpKernelHandleTable;
// Go to the system process if we have to
if (PsGetCurrentProcess() != PsInitialSystemProcess) {
KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
AttachedToProcess = TRUE;
}
} else {
ObjectTable = ObpGetObjectTable();
}
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) {
// 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) {
if (!(*ObjectType->TypeInfo.OkayToCloseProcedure)( PsGetCurrentProcess(), Object, Handle )) {
ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object );
ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
Status = STATUS_HANDLE_NOT_CLOSABLE;
leave;
}
}
CapturedAttributes = ObjectTableEntry->ObAttributes;
// 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) {
if (KeGetPreviousMode() != KernelMode) {
ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );
if ((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) ||
(PsGetCurrentProcess()->DebugPort != NULL)) {
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
Status = KeRaiseUserException(STATUS_HANDLE_NOT_CLOSABLE);
leave;
} else {
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
Status = STATUS_HANDLE_NOT_CLOSABLE;
leave;
}
} else {
if ((!PsIsThreadTerminating(PsGetCurrentThread())) &&
(PsGetCurrentProcess()->Peb != NULL)) {
ExUnlockHandleTableEntry( ObjectTable, ObjectTableEntry );
#if DBG
// bugcheck here on checked builds if kernel mode code is
// closing a protected handle and process is not exiting.
// Ignore if no PEB as this occurs if process is killed
// before really starting.
KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 0, 0, 0);
#else
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
Status = STATUS_HANDLE_NOT_CLOSABLE;
leave;
#endif // DBG
}
}
}
// Get the granted access for the handle
#if i386 && !FPO
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
CapturedGrantedAccess = ObpTranslateGrantedAccessIndex( ObjectTableEntry->GrantedAccessIndex );
} else {
CapturedGrantedAccess = ObjectTableEntry->GrantedAccess;
}
#else
CapturedGrantedAccess = ObjectTableEntry->GrantedAccess;
#endif // i386 && !FPO
// 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,
ObjectHeader->Type,
CapturedGrantedAccess );
ObDereferenceObject( Object );
ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObjectType, Object );
// If we are attached to the system process then return
// back to our caller
if (AttachedToProcess) {
KeUnstackDetachProcess(&ApcState);
AttachedToProcess = FALSE;
}
// And return to our caller
Status = STATUS_SUCCESS;
leave;
} else {
// At this point the input handle did not translate to a valid
// object table entry
ObpEndTypeSpecificCallOut( SaveIrql, "NtClose", ObpTypeObjectType, Handle );
// 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 (KeGetPreviousMode() != KernelMode) {
if ((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) ||
(PsGetCurrentProcess()->DebugPort != NULL)) {
Status = KeRaiseUserException(STATUS_INVALID_HANDLE);
leave;
} else {
Status = STATUS_INVALID_HANDLE;
leave;
}
} 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(PsGetCurrentThread())) &&
(PsGetCurrentProcess()->Peb != NULL)) {
if (KdDebuggerEnabled) {
KeBugCheckEx(INVALID_KERNEL_HANDLE, (ULONG_PTR)Handle, 1, 0, 0);
}
}
}
}
Status = STATUS_INVALID_HANDLE;
leave;
}
} finally {
KeLeaveCriticalRegion();
}
return Status;
}
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;
PAGED_CODE();
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
ObjectHeader->Flags &= ~OB_FLAG_PERMANENT_OBJECT;
ObpDeleteNameCheck( Object, FALSE );
return;
}