1057 lines
39 KiB
C
1057 lines
39 KiB
C
/*++
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
obref.c
|
|
|
|
Abstract:
|
|
Object open API
|
|
|
|
Author:
|
|
Steve Wood (stevewo) 31-Mar-1989
|
|
--*/
|
|
|
|
#include "obp.h"
|
|
|
|
// A local variable to tell us if the worker queue is active
|
|
|
|
BOOLEAN ObpRemoveQueueActive;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,ObGetObjectPointerCount)
|
|
#pragma alloc_text(PAGE,ObOpenObjectByName)
|
|
#pragma alloc_text(PAGE,ObOpenObjectByPointer)
|
|
#pragma alloc_text(PAGE,ObReferenceObjectByName)
|
|
#pragma alloc_text(PAGE,ObpRemoveObjectRoutine)
|
|
#pragma alloc_text(PAGE,ObpDeleteNameCheck)
|
|
#endif
|
|
|
|
#ifndef LpcpAcquireLpcpLock
|
|
// ******************** WARNING ************************
|
|
// The following include is neccessary in order to hold the LPC lock
|
|
// while setting the port flag to PORT_DELETED.
|
|
// A cleanest solution could be a callback on DereferenceObject.
|
|
// In that case we'll define the callback for the LPC to do all these things.
|
|
#include "..\lpc\lpcp.h"
|
|
#endif //#ifndef LpcpAcquireLpcpLock
|
|
|
|
|
|
ULONG ObGetObjectPointerCount(IN PVOID Object)
|
|
/*++
|
|
Routine Description:
|
|
This routine returns the current pointer count for a specified object.
|
|
Arguments:
|
|
Object - Pointer to the object whose pointer count is to be returned.
|
|
Return Value:
|
|
The current pointer count for the specified object is returned.
|
|
Note:
|
|
This function cannot be made a macro, since fields in the thread object
|
|
move from release to release, so this must remain a full function.
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// Simply return the current pointer count for the object.
|
|
return OBJECT_TO_OBJECT_HEADER(Object)->PointerCount;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObOpenObjectByName(
|
|
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
|
IN POBJECT_TYPE ObjectType OPTIONAL,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN OUT PACCESS_STATE AccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
|
IN OUT PVOID ParseContext OPTIONAL,
|
|
OUT PHANDLE Handle
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function opens an object with full access validation and auditing.
|
|
Soon after entering we capture the SubjectContext for the caller. This
|
|
context must remain captured until auditing is complete, and passed to
|
|
any routine that may have to do access checking or auditing.
|
|
Arguments:
|
|
ObjectAttributes - Supplies a pointer to the object attributes.
|
|
ObjectType - Supplies an optional pointer to the object type descriptor.
|
|
AccessMode - Supplies the processor mode of the access.
|
|
AccessState - Supplies an optional pointer to the current access status
|
|
describing already granted access types, the privileges used to get
|
|
them, and any access types yet to be granted.
|
|
DesiredAcess - Supplies the desired access to the object.
|
|
ParseContext - Supplies an optional pointer to parse context.
|
|
Handle - Supplies a pointer to a variable that receives the handle value.
|
|
Return Value:
|
|
If the object is successfully opened, then a handle for the object is
|
|
created and a success status is returned. Otherwise, an error status is returned.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS HandleStatus;
|
|
PVOID ExistingObject;
|
|
HANDLE NewHandle;
|
|
BOOLEAN DirectoryLocked;
|
|
OB_OPEN_REASON OpenReason;
|
|
POBJECT_HEADER ObjectHeader;
|
|
OBJECT_CREATE_INFORMATION ObjectCreateInfo;
|
|
UNICODE_STRING CapturedObjectName;
|
|
ACCESS_STATE LocalAccessState;
|
|
AUX_ACCESS_DATA AuxData;
|
|
PGENERIC_MAPPING GenericMapping;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObOpenObjectByName");
|
|
|
|
// If the object attributes are not specified, then return an error.
|
|
*Handle = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(ObjectAttributes)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
// Capture the object creation information.
|
|
Status = ObpCaptureObjectCreateInformation(ObjectType,
|
|
AccessMode,
|
|
ObjectAttributes,
|
|
&CapturedObjectName,
|
|
&ObjectCreateInfo,
|
|
TRUE);
|
|
|
|
// If the object creation information is successfully captured,
|
|
// then generate the access state.
|
|
if (NT_SUCCESS(Status)) {
|
|
if (!ARGUMENT_PRESENT(AccessState)) {
|
|
// If an object type descriptor is specified, then use associated generic mapping.
|
|
// Otherwise, use no generic mapping.
|
|
GenericMapping = NULL;
|
|
if (ARGUMENT_PRESENT(ObjectType)) {
|
|
GenericMapping = &ObjectType->TypeInfo.GenericMapping;
|
|
}
|
|
|
|
AccessState = &LocalAccessState;
|
|
|
|
Status = SeCreateAccessState(&LocalAccessState, &AuxData, DesiredAccess, GenericMapping);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto FreeCreateInfo;
|
|
}
|
|
}
|
|
|
|
// If there is a security descriptor specified in the object
|
|
// attributes, then capture it in the access state.
|
|
if (ObjectCreateInfo.SecurityDescriptor != NULL) {
|
|
AccessState->SecurityDescriptor = ObjectCreateInfo.SecurityDescriptor;
|
|
}
|
|
|
|
// Validate the access state.
|
|
Status = ObpValidateAccessMask(AccessState);
|
|
|
|
// If the access state is valid, then lookup the object by name.
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = ObpLookupObjectName(ObjectCreateInfo.RootDirectory,
|
|
&CapturedObjectName,
|
|
ObjectCreateInfo.Attributes,
|
|
ObjectType,
|
|
AccessMode,
|
|
ParseContext,
|
|
ObjectCreateInfo.SecurityQos,
|
|
NULL,
|
|
AccessState,
|
|
&DirectoryLocked,
|
|
&ExistingObject);
|
|
|
|
// If the object was successfully looked up, then attempt
|
|
// to create or open a handle.
|
|
if (NT_SUCCESS(Status)) {
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(ExistingObject);
|
|
|
|
// If the object is being created, then the operation
|
|
// must be a open-if operation. Otherwise, a handle to
|
|
// an object is being opened.
|
|
if (ObjectHeader->Flags & OB_FLAG_NEW_OBJECT) {
|
|
OpenReason = ObCreateHandle;
|
|
if (ObjectHeader->ObjectCreateInfo != NULL) {
|
|
ObpFreeObjectCreateInformation(ObjectHeader->ObjectCreateInfo);
|
|
ObjectHeader->ObjectCreateInfo = NULL;
|
|
}
|
|
} else {
|
|
OpenReason = ObOpenHandle;
|
|
}
|
|
|
|
// If any of the object attributes are invalid, then
|
|
// return an error status.
|
|
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & ObjectCreateInfo.Attributes) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
if (DirectoryLocked) {
|
|
ObpLeaveRootDirectoryMutex();
|
|
}
|
|
} else {
|
|
// The status returned by the object lookup routine
|
|
// must be returned if the creation of a handle is
|
|
// successful. Otherwise, the handle creation status
|
|
// is returned.
|
|
HandleStatus = ObpCreateHandle(OpenReason,
|
|
ExistingObject,
|
|
ObjectType,
|
|
AccessState,
|
|
0,
|
|
ObjectCreateInfo.Attributes,
|
|
DirectoryLocked,
|
|
AccessMode,
|
|
(PVOID *)NULL,
|
|
&NewHandle);
|
|
if (!NT_SUCCESS(HandleStatus)) {
|
|
ObDereferenceObject(ExistingObject);
|
|
Status = HandleStatus;
|
|
} else {
|
|
*Handle = NewHandle;
|
|
}
|
|
}
|
|
} else {
|
|
if (DirectoryLocked) {
|
|
ObpLeaveRootDirectoryMutex();
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the access state was generated, then delete the access state.
|
|
if (AccessState == &LocalAccessState) {
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
// Free the create information.
|
|
FreeCreateInfo:
|
|
ObpReleaseObjectCreateInformation(&ObjectCreateInfo);
|
|
if (CapturedObjectName.Buffer != NULL) {
|
|
ObpFreeObjectNameBuffer(&CapturedObjectName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS ObOpenObjectByPointer(
|
|
IN PVOID Object,
|
|
IN ULONG HandleAttributes,
|
|
IN PACCESS_STATE PassedAccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PHANDLE Handle)
|
|
/*++
|
|
Routine Description:
|
|
This routine opens an object referenced by a pointer.
|
|
Arguments:
|
|
Object - A pointer to the object being opened.
|
|
HandleAttributes - The desired attributes for the handle, such
|
|
as OBJ_INHERIT, OBJ_PERMANENT, OBJ_EXCLUSIVE, OBJ_CASE_INSENSITIVE, OBJ_OPENIF, and OBJ_OPENLINK
|
|
PassedAccessState - Supplies an optional pointer to the current access
|
|
status describing already granted access types, the privileges used to get them, and any access types yet to be granted.
|
|
DesiredAcess - Supplies the desired access to the object.
|
|
ObjectType - Supplies the type of the object being opened
|
|
AccessMode - Supplies the processor mode of the access.
|
|
Handle - Supplies a pointer to a variable that receives the handle value.
|
|
Return Value:
|
|
An appropriate NTSTATUS value
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE NewHandle;
|
|
POBJECT_HEADER ObjectHeader;
|
|
ACCESS_STATE LocalAccessState;
|
|
PACCESS_STATE AccessState = NULL;
|
|
AUX_ACCESS_DATA AuxData;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObOpenObjectByPointer");
|
|
|
|
// First increment the pointer count for the object. This routine
|
|
// also checks the object types
|
|
Status = ObReferenceObjectByPointer(Object, 0, ObjectType, AccessMode);
|
|
if (NT_SUCCESS(Status)) {
|
|
// Get the object header for the input object body
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
|
|
// If the caller did not pass in an access state then
|
|
// we will create a new one based on the desired access and the object types generic mapping
|
|
if (!ARGUMENT_PRESENT(PassedAccessState)) {
|
|
Status = SeCreateAccessState(&LocalAccessState, &AuxData, DesiredAccess, &ObjectHeader->Type->TypeInfo.GenericMapping);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObDereferenceObject(Object);
|
|
return(Status);
|
|
}
|
|
|
|
AccessState = &LocalAccessState;
|
|
// Otherwise the caller did specify an access state so we use the one passed in.
|
|
} else {
|
|
AccessState = PassedAccessState;
|
|
}
|
|
|
|
// Make sure the caller is asking for handle attributes that are
|
|
// valid for the given object type
|
|
if (ObjectHeader->Type->TypeInfo.InvalidAttributes & HandleAttributes) {
|
|
if (AccessState == &LocalAccessState) {
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
ObDereferenceObject(Object);
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
// We've referenced the object and have an access state to give
|
|
// the new handle so now create a new handle for the object.
|
|
Status = ObpCreateHandle(ObOpenHandle,
|
|
Object,
|
|
ObjectType,
|
|
AccessState,
|
|
0,
|
|
HandleAttributes,
|
|
FALSE,
|
|
AccessMode,
|
|
(PVOID *)NULL,
|
|
&NewHandle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ObDereferenceObject(Object);
|
|
}
|
|
}
|
|
|
|
// If we successfully opened by object and created a new handle
|
|
// then set the output variable correctly
|
|
if (NT_SUCCESS(Status)) {
|
|
*Handle = NewHandle;
|
|
} else {
|
|
*Handle = NULL;
|
|
}
|
|
|
|
// Check if we used our own access state and now need to cleanup
|
|
if (AccessState == &LocalAccessState) {
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
return(Status);// And return to our caller
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByHandle(
|
|
IN HANDLE Handle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType OPTIONAL,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
OUT PVOID *Object,
|
|
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given a handle to an object this routine returns a pointer
|
|
to the body of the object with proper ref counts
|
|
Arguments:
|
|
Handle - Supplies a handle to the object being referenced. It can
|
|
also be the result of NtCurrentProcess or NtCurrentThread
|
|
DesiredAccess - Supplies the access being requested by the caller
|
|
ObjectType - Optionally supplies the type of the object we are expecting
|
|
AccessMode - Supplies the processor mode of the access
|
|
Object - Receives a pointer to the object body if the operation is successful
|
|
HandleInformation - Optionally receives information regarding the input handle.
|
|
Return Value:
|
|
An appropriate NTSTATUS value
|
|
--*/
|
|
{
|
|
ACCESS_MASK GrantedAccess;
|
|
PHANDLE_TABLE HandleTable;
|
|
POBJECT_HEADER ObjectHeader;
|
|
PHANDLE_TABLE_ENTRY ObjectTableEntry;
|
|
PEPROCESS Process;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
BOOLEAN AttachedToProcess = FALSE;
|
|
|
|
ObpValidateIrql("ObReferenceObjectByHandle");
|
|
|
|
// Protect ourselves from being interrupted while we hold a handle table entry lock
|
|
|
|
KeEnterCriticalRegion();
|
|
|
|
try {
|
|
// If the handle is equal to the current process handle and the object
|
|
// type is NULL or type process, then attempt to translate a handle to the current process.
|
|
// Otherwise, check if the handle is the current thread handle.
|
|
if (Handle == NtCurrentProcess()) {
|
|
if ((ObjectType == PsProcessType) || (ObjectType == NULL)) {
|
|
Process = PsGetCurrentProcess();
|
|
GrantedAccess = Process->GrantedAccess;
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Process);
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Process;
|
|
|
|
ASSERT(*Object != NULL);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
leave;
|
|
} else {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
} else {
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
// If the handle is equal to the current thread handle and the object
|
|
// type is NULL or type thread, then attempt to translate a handle to
|
|
// the current thread. Otherwise, the we'll try and translate the handle
|
|
} else if (Handle == NtCurrentThread()) {
|
|
if ((ObjectType == PsThreadType) || (ObjectType == NULL)) {
|
|
Thread = PsGetCurrentThread();
|
|
GrantedAccess = Thread->GrantedAccess;
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Thread);
|
|
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = 0;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = Thread;
|
|
|
|
ASSERT(*Object != NULL);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
leave;
|
|
} else {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
} else {
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
// Otherwise the handle is not a built in value. It must be an index into a handle table.
|
|
} else {
|
|
#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
|
|
|
|
// We know we're NOT a builtin handle at this point
|
|
ASSERT((Handle < 0) ? (AccessMode == KernelMode) : TRUE);
|
|
#endif
|
|
|
|
// First get a pointer to either the processes handle table or the
|
|
// global kernel handle table. If it is the kernel handle then we
|
|
// need to clear the handle bit and attach to the system process
|
|
if (IsKernelHandle(Handle, AccessMode)) {
|
|
Handle = DecodeKernelHandle(Handle);// Make the handle look like a regular handle
|
|
HandleTable = ObpKernelHandleTable;// The global kernel handle table
|
|
} else {
|
|
HandleTable = ObpGetObjectTable();
|
|
}
|
|
|
|
ASSERT(HandleTable != NULL);
|
|
|
|
// Translate the specified handle to an object table index.
|
|
ObjectTableEntry = ExMapHandleToPointer(HandleTable, Handle);
|
|
|
|
// Make sure the object table entry really does exist
|
|
if (ObjectTableEntry != NULL) {
|
|
ObjectHeader = (POBJECT_HEADER)(((ULONG_PTR)(ObjectTableEntry->Object)) & ~OBJ_HANDLE_ATTRIBUTES);
|
|
if ((ObjectHeader->Type == ObjectType) || (ObjectType == NULL)) {
|
|
|
|
#if i386 && !FPO
|
|
if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) {
|
|
if ((AccessMode != KernelMode) || ARGUMENT_PRESENT(HandleInformation)) {
|
|
GrantedAccess = ObpTranslateGrantedAccessIndex(ObjectTableEntry->GrantedAccessIndex);
|
|
}
|
|
} else {
|
|
GrantedAccess = ObjectTableEntry->GrantedAccess;
|
|
}
|
|
#else
|
|
GrantedAccess = ObjectTableEntry->GrantedAccess;
|
|
|
|
#endif // i386 && !FPO
|
|
|
|
if ((SeComputeDeniedAccesses(GrantedAccess, DesiredAccess) == 0) || (AccessMode == KernelMode)) {
|
|
// Access to the object is allowed. Return the handle
|
|
// information is requested, increment the object
|
|
// pointer count, unlock the handle table and return a success status.
|
|
|
|
// Note that this is the only successful return path
|
|
// out of this routine if the user did not specify
|
|
// the current process or current thread in the input handle.
|
|
if (ARGUMENT_PRESENT(HandleInformation)) {
|
|
HandleInformation->GrantedAccess = GrantedAccess;
|
|
HandleInformation->HandleAttributes = ObjectTableEntry->ObAttributes & OBJ_HANDLE_ATTRIBUTES;
|
|
}
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
*Object = &ObjectHeader->Body;
|
|
|
|
ASSERT(*Object != NULL);
|
|
|
|
ExUnlockHandleTableEntry(HandleTable, ObjectTableEntry);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
leave;
|
|
} else {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
} else {
|
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
ExUnlockHandleTableEntry(HandleTable, ObjectTableEntry);
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
// If we are attached to the system process then return
|
|
// back to our caller
|
|
if (AttachedToProcess) {
|
|
KeDetachProcess();
|
|
}
|
|
|
|
// No handle translation is possible. Set the object address to NULL
|
|
// and return an error status.
|
|
*Object = NULL;
|
|
} finally{
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByName(
|
|
IN PUNICODE_STRING ObjectName,
|
|
IN ULONG Attributes,
|
|
IN PACCESS_STATE AccessState OPTIONAL,
|
|
IN ACCESS_MASK DesiredAccess OPTIONAL,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode,
|
|
IN OUT PVOID ParseContext OPTIONAL,
|
|
OUT PVOID *Object
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given a name of an object this routine returns a pointer
|
|
to the body of the object with proper ref counts
|
|
Arguments:
|
|
ObjectName - Supplies the name of the object being referenced
|
|
Attributes - Supplies the desired handle attributes
|
|
AccessState - Supplies an optional pointer to the current access
|
|
status describing already granted access types, the privileges used
|
|
to get them, and any access types yet to be granted.
|
|
DesiredAccess - Optionally supplies the desired access to the for the object
|
|
ObjectType - Specifies the object type according to the caller
|
|
AccessMode - Supplies the processor mode of the access
|
|
ParseContext - Optionally supplies a context to pass down to the parse routine
|
|
Object - Receives a pointer to the referenced object body
|
|
Return Value:
|
|
An appropriate NTSTATUS value
|
|
--*/
|
|
{
|
|
UNICODE_STRING CapturedObjectName;
|
|
BOOLEAN DirectoryLocked;
|
|
PVOID ExistingObject;
|
|
ACCESS_STATE LocalAccessState;
|
|
AUX_ACCESS_DATA AuxData;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObReferenceObjectByName");
|
|
|
|
// If the object name descriptor is not specified, or the object name
|
|
// length is zero (tested after capture), then the object name is invalid.
|
|
|
|
if (ObjectName == NULL) {
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
// Capture the object name.
|
|
Status = ObpCaptureObjectName(AccessMode, ObjectName, &CapturedObjectName, TRUE);
|
|
if (NT_SUCCESS(Status)) {
|
|
// No buffer has been allocated for a zero length name so no free needed
|
|
if (CapturedObjectName.Length == 0) {
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
// If the access state is not specified, then create the access state.
|
|
if (!ARGUMENT_PRESENT(AccessState)) {
|
|
AccessState = &LocalAccessState;
|
|
Status = SeCreateAccessState(&LocalAccessState,
|
|
&AuxData,
|
|
DesiredAccess,
|
|
&ObjectType->TypeInfo.GenericMapping);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto FreeBuffer;
|
|
}
|
|
}
|
|
|
|
// Lookup object by name.
|
|
Status = ObpLookupObjectName(NULL,
|
|
&CapturedObjectName,
|
|
Attributes,
|
|
ObjectType,
|
|
AccessMode,
|
|
ParseContext,
|
|
NULL,
|
|
NULL,
|
|
AccessState,
|
|
&DirectoryLocked,
|
|
&ExistingObject);
|
|
|
|
// If the directory is returned locked, then unlock it.
|
|
if (DirectoryLocked) {
|
|
ObpLeaveRootDirectoryMutex();
|
|
}
|
|
|
|
// If the lookup was successful, then return the existing
|
|
// object if access is allowed. Otherwise, return NULL.
|
|
|
|
*Object = NULL;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (ObpCheckObjectReference(ExistingObject, AccessState, FALSE, AccessMode, &Status)) {
|
|
*Object = ExistingObject;
|
|
}
|
|
}
|
|
|
|
// If the access state was generated, then delete the access state.
|
|
if (AccessState == &LocalAccessState) {
|
|
SeDeleteAccessState(AccessState);
|
|
}
|
|
|
|
// Free the object name buffer.
|
|
FreeBuffer:
|
|
ObpFreeObjectNameBuffer(&CapturedObjectName);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ObReferenceObjectByPointer(
|
|
IN PVOID Object,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN POBJECT_TYPE ObjectType,
|
|
IN KPROCESSOR_MODE AccessMode
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine adds another reference count to an object denoted by
|
|
a pointer to the object body
|
|
Arguments:
|
|
Object - Supplies a pointer to the object being referenced
|
|
DesiredAccess - Specifies the desired access for the reference
|
|
ObjectType - Specifies the object type according to the caller
|
|
AccessMode - Supplies the processor mode of the access
|
|
Return Value:
|
|
STATUS_SUCCESS if successful and STATUS_OBJECT_TYPE_MISMATCH otherwise
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
// Translate the pointer to the object body to a pointer to the object header
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
|
|
// If the specified object type does not match and either the caller is
|
|
// not kernel mode or it is not a symbolic link object then it is an error
|
|
if ((ObjectHeader->Type != ObjectType) && (AccessMode != KernelMode || ObjectType == ObpSymbolicLinkObjectType)) {
|
|
return(STATUS_OBJECT_TYPE_MISMATCH);
|
|
}
|
|
|
|
// Otherwise increment the pointer count and return success to our caller
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
VOID FASTCALL ObfReferenceObject(IN PVOID Object)
|
|
/*++
|
|
Routine Description:
|
|
This function increments the reference count for an object.
|
|
N.B. This function should be used to increment the reference count
|
|
when the accessing mode is kernel or the objct type is known.
|
|
Arguments:
|
|
Object - Supplies a pointer to the object whose reference count is incremented.
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
|
|
ObpIncrPointerCount(ObjectHeader);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID FASTCALL ObfDereferenceObject(IN PVOID Object)
|
|
/*++
|
|
Routine Description:
|
|
This routine decrments the refernce count of the specified object and
|
|
does whatever cleanup there is if the count goes to zero.
|
|
Arguments:
|
|
Object - Supplies a pointer to the body of the object being dereferenced
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
KIRQL OldIrql;
|
|
BOOLEAN StartWorkerThread;
|
|
|
|
PLPCP_PORT_OBJECT Port = NULL;
|
|
|
|
#if DBG
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
#endif
|
|
|
|
// Translate a pointer to the object body to a pointer to the object header.
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
|
|
#if DBG
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
|
|
if (NameInfo) {
|
|
InterlockedDecrement(&NameInfo->DbgDereferenceCount);
|
|
}
|
|
#endif
|
|
|
|
// Decrement the point count and if the result is now then
|
|
// there is extra work to do
|
|
ObjectType = ObjectHeader->Type;
|
|
if ((ObjectType == LpcPortObjectType) || (ObjectType == LpcWaitablePortObjectType)) {
|
|
Port = Object;
|
|
LpcpAcquireLpcpLock();
|
|
}
|
|
|
|
if (ObpDecrPointerCountWithResult(ObjectHeader)) {
|
|
// Find out the level we're at and the object type
|
|
|
|
OldIrql = KeGetCurrentIrql();
|
|
ASSERT(ObjectHeader->HandleCount == 0);
|
|
if (Port != NULL) {
|
|
Port->Flags |= PORT_DELETED;
|
|
Port = NULL;
|
|
LpcpReleaseLpcpLock();
|
|
}
|
|
|
|
// If we're at the passive level then go ahead and delete the
|
|
// object now. Or if we're at APC level and object say's we're
|
|
// allocated out of paged pool then also go ahead and delete
|
|
// the object right now.
|
|
if ((OldIrql == PASSIVE_LEVEL) ||
|
|
((OldIrql == APC_LEVEL) &&
|
|
((ObjectType != NULL) && (ObjectType->TypeInfo.PoolType != NonPagedPool)))) {
|
|
ObpRemoveObjectRoutine(Object);
|
|
return;
|
|
} else {
|
|
// Objects can't be deleted from an IRQL above APC_LEVEL.
|
|
// Nonpaged objects can't be deleted from APC_LEVEL.
|
|
// So queue the delete operation.
|
|
|
|
ASSERT((ObjectHeader->Type == NULL) || (ObjectHeader->Type->TypeInfo.PoolType == NonPagedPool));
|
|
|
|
// Lock the work queue, enqueue the new work item, and if the
|
|
// work queue is not active then make it active, indicate that
|
|
// we need to start the worker thread, and unlock the work queue.
|
|
|
|
ExAcquireSpinLock(&ObpLock, &OldIrql);
|
|
|
|
PushEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue, (PSINGLE_LIST_ENTRY)&ObjectHeader->SEntry);
|
|
|
|
if (!ObpRemoveQueueActive) {
|
|
ObpRemoveQueueActive = TRUE;
|
|
StartWorkerThread = TRUE;
|
|
} else {
|
|
StartWorkerThread = FALSE;
|
|
}
|
|
|
|
#if 0
|
|
if (StartWorkerThread) {
|
|
KdPrint(("OB: %08x Starting ObpProcessRemoveObjectQueue thread.\n", Object));
|
|
} else {
|
|
KdPrint(("OB: %08x Queued to ObpProcessRemoveObjectQueue thread.\n", Object));
|
|
}
|
|
#endif // 1
|
|
|
|
ExReleaseSpinLock(&ObpLock, OldIrql);
|
|
|
|
// If we have to start the worker thread then go ahead and enqueue the work item
|
|
if (StartWorkerThread) {
|
|
ExInitializeWorkItem(&ObpRemoveObjectWorkItem, ObpProcessRemoveObjectQueue, NULL);
|
|
ExQueueWorkItem(&ObpRemoveObjectWorkItem, CriticalWorkQueue);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Port != NULL) {
|
|
LpcpReleaseLpcpLock();
|
|
}
|
|
}
|
|
|
|
|
|
VOID ObpProcessRemoveObjectQueue(PVOID Parameter)
|
|
/*++
|
|
Routine Description:
|
|
This is the work routine for the remove object work queue.
|
|
Its job is to remove and process items from the remove object queue.
|
|
Arguments:
|
|
Parameter - Ignored
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
PSINGLE_LIST_ENTRY Entry;
|
|
POBJECT_HEADER ObjectHeader;
|
|
KIRQL OldIrql;
|
|
|
|
// Lock the work queue this will keep the preceding routine
|
|
// from monkeying with the queue
|
|
ExAcquireSpinLock(&ObpLock, &OldIrql);
|
|
|
|
// While there are items in our private remove object work queue
|
|
// then we remove each item, get back up to the object header,
|
|
// and delete the object. The latter part done outside of the
|
|
// work queue lock
|
|
Entry = PopEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue);
|
|
while (Entry != NULL) {
|
|
ExReleaseSpinLock(&ObpLock, OldIrql);
|
|
ObjectHeader = CONTAINING_RECORD(Entry, OBJECT_HEADER, SEntry);
|
|
ObpRemoveObjectRoutine(&ObjectHeader->Body);
|
|
ExAcquireSpinLock(&ObpLock, &OldIrql);
|
|
Entry = PopEntryList((PSINGLE_LIST_ENTRY)&ObpRemoveObjectQueue);
|
|
}
|
|
|
|
// Indicate that we are now going inactive and then unlock the work queue
|
|
ObpRemoveQueueActive = FALSE;
|
|
|
|
ExReleaseSpinLock(&ObpLock, OldIrql);
|
|
}
|
|
|
|
|
|
VOID ObpRemoveObjectRoutine(PVOID Object)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to delete an object whose reference count has gone to zero.
|
|
Arguments:
|
|
Object - Supplies a pointer to the body of the object being deleted
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
POBJECT_HEADER_CREATOR_INFO CreatorInfo;
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObpRemoveObjectRoutine");
|
|
|
|
// Retrieve an object header from the object body, and also get
|
|
// the object type, creator and name info if available
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
ObjectType = ObjectHeader->Type;
|
|
CreatorInfo = OBJECT_HEADER_TO_CREATOR_INFO(ObjectHeader);
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
|
|
|
|
// Get exclusive access to the object type object
|
|
ObpEnterObjectTypeMutex(ObjectType);
|
|
|
|
// If there is a creator info record and we are on the list
|
|
// for the object type then remove this object from the list
|
|
if (CreatorInfo != NULL && !IsListEmpty(&CreatorInfo->TypeList)) {
|
|
RemoveEntryList(&CreatorInfo->TypeList);
|
|
}
|
|
|
|
// If there is a name info record and the name buffer is not null
|
|
// then free the buffer and zero out the name record
|
|
if (NameInfo != NULL && NameInfo->Name.Buffer != NULL) {
|
|
ExFreePool(NameInfo->Name.Buffer);
|
|
NameInfo->Name.Buffer = NULL;
|
|
NameInfo->Name.Length = 0;
|
|
NameInfo->Name.MaximumLength = 0;
|
|
}
|
|
|
|
// We are done with the object type object so we can now release it
|
|
ObpLeaveObjectTypeMutex(ObjectType);
|
|
|
|
// Security descriptor deletion must precede the
|
|
// call to the object's DeleteProcedure. Check if we have
|
|
// a security descriptor and if so then call the routine
|
|
// to delete the security descritpor.
|
|
if (ObjectHeader->SecurityDescriptor != NULL) {
|
|
KIRQL SaveIrql;
|
|
|
|
ObpBeginTypeSpecificCallOut(SaveIrql);
|
|
|
|
Status = (ObjectType->TypeInfo.SecurityProcedure)(Object,
|
|
DeleteSecurityDescriptor,
|
|
NULL, NULL, NULL,
|
|
&ObjectHeader->SecurityDescriptor,
|
|
0,
|
|
NULL);
|
|
ObpEndTypeSpecificCallOut(SaveIrql, "Security", ObjectType, Object);
|
|
}
|
|
|
|
// Now if there is a delete callback for the object type invoke the routine
|
|
if (ObjectType->TypeInfo.DeleteProcedure) {
|
|
KIRQL SaveIrql;
|
|
|
|
ObpBeginTypeSpecificCallOut(SaveIrql);
|
|
(*(ObjectType->TypeInfo.DeleteProcedure))(Object);
|
|
ObpEndTypeSpecificCallOut(SaveIrql, "Delete", ObjectType, Object);
|
|
}
|
|
|
|
// Finally return the object back to pool including releasing any quota charges
|
|
ObpFreeObject(Object);
|
|
}
|
|
|
|
|
|
VOID ObpDeleteNameCheck(IN PVOID Object, IN BOOLEAN TypeMutexHeld)
|
|
/*++
|
|
Routine Description:
|
|
This routine removes the name of an object from its parent directory
|
|
Arguments:
|
|
Object - Supplies a pointer to the object body whose name is being checked
|
|
TypeMutexHeld - Indicates if the lock on object type is being held by the caller
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
POBJECT_HEADER ObjectHeader;
|
|
POBJECT_TYPE ObjectType;
|
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|
PVOID DirObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
ObpValidateIrql("ObpDeleteNameCheck");
|
|
|
|
// Translate the object body to an object header also get
|
|
// the object type and name info if present
|
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
|
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
|
|
ObjectType = ObjectHeader->Type;
|
|
|
|
// If the lock is not held then get the lock now
|
|
if (!TypeMutexHeld) {
|
|
ObpEnterObjectTypeMutex(ObjectType);
|
|
}
|
|
|
|
// Make sure that the object has a zero handle count, has a non
|
|
// empty name buffer, and is not a permanent object
|
|
if ((ObjectHeader->HandleCount == 0) &&
|
|
(NameInfo != NULL) &&
|
|
(NameInfo->Name.Length != 0) &&
|
|
(!(ObjectHeader->Flags & OB_FLAG_PERMANENT_OBJECT))) {
|
|
// Give up the lock on the object type and instead
|
|
// get the lock on the object directories
|
|
ObpLeaveObjectTypeMutex(ObjectType);
|
|
ObpEnterRootDirectoryMutex();
|
|
DirObject = NULL;
|
|
|
|
// Check that the object we is still in the directory otherwise
|
|
// then is nothing for us to remove
|
|
if (Object == ObpLookupDirectoryEntry(NameInfo->Directory, &NameInfo->Name, 0)) {
|
|
// Now reacquire the lock on the object type and
|
|
// check check the handle count again. If it is still
|
|
// zero then we can do the actual delete name operation
|
|
ObpEnterObjectTypeMutex(ObjectType);
|
|
|
|
if (ObjectHeader->HandleCount == 0) {
|
|
KIRQL SaveIrql;
|
|
|
|
// Delete the directory entry
|
|
ObpDeleteDirectoryEntry(NameInfo->Directory);
|
|
|
|
// If security is not required invoke the
|
|
// security callback to delete the security descriptor
|
|
if (!ObjectType->TypeInfo.SecurityRequired) {
|
|
ObpBeginTypeSpecificCallOut(SaveIrql);
|
|
(ObjectType->TypeInfo.SecurityProcedure)(Object,
|
|
DeleteSecurityDescriptor,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&ObjectHeader->SecurityDescriptor,
|
|
ObjectType->TypeInfo.PoolType,
|
|
NULL);
|
|
ObpEndTypeSpecificCallOut(SaveIrql, "Security", ObjectType, Object);
|
|
}
|
|
|
|
// If this is a symbolic link object then we also need to
|
|
// delete the symbolic link
|
|
if (ObjectType == ObpSymbolicLinkObjectType) {
|
|
ObpDeleteSymbolicLinkName((POBJECT_SYMBOLIC_LINK)Object);
|
|
}
|
|
|
|
// Free the name buffer and zero out the name data fields
|
|
ExFreePool(NameInfo->Name.Buffer);
|
|
|
|
NameInfo->Name.Buffer = NULL;
|
|
NameInfo->Name.Length = 0;
|
|
NameInfo->Name.MaximumLength = 0;
|
|
|
|
DirObject = NameInfo->Directory;
|
|
NameInfo->Directory = NULL;
|
|
}
|
|
|
|
ObpLeaveObjectTypeMutex(ObjectType);
|
|
}
|
|
|
|
ObpLeaveRootDirectoryMutex();
|
|
|
|
// If there is a directory object for the name then decrement
|
|
// its reference count for it and for the object
|
|
if (DirObject != NULL) {
|
|
ObDereferenceObject(DirObject);
|
|
ObDereferenceObject(Object);
|
|
}
|
|
} else {
|
|
// Otherwise don't do any work but simply release the object type
|
|
// lock and return to our caller
|
|
ObpLeaveObjectTypeMutex(ObjectType);
|
|
}
|
|
}
|
|
|
|
|
|
// Thunks to support standard call callers
|
|
|
|
|
|
#ifdef ObDereferenceObject
|
|
#undef ObDereferenceObject
|
|
#endif
|
|
|
|
VOID ObDereferenceObject(IN PVOID Object)
|
|
/*++
|
|
Routine Description:
|
|
This is really just a thunk for the Obf version of the dereference routine
|
|
Arguments:
|
|
Object - Supplies a pointer to the body of the object being dereferenced
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
ObfDereferenceObject(Object);
|
|
}
|