1706 lines
56 KiB
C
1706 lines
56 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
objsup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the object support routine for the NT I/O system.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Darryl E. Havens (darrylh) 30-May-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "iomgr.h"
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopSetDeviceSecurityDescriptors(
|
|||
|
IN PDEVICE_OBJECT OriginalDeviceObject,
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopSetDeviceSecurityDescriptor(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, IopCloseFile)
|
|||
|
#pragma alloc_text(PAGE, IopDeleteFile)
|
|||
|
#pragma alloc_text(PAGE, IopDeleteDevice)
|
|||
|
#pragma alloc_text(PAGE, IopDeleteDriver)
|
|||
|
#pragma alloc_text(PAGE, IopGetSetSecurityObject)
|
|||
|
#pragma alloc_text(PAGE, IopSetDeviceSecurityDescriptors)
|
|||
|
#pragma alloc_text(PAGE, IopSetDeviceSecurityDescriptor)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
IopCloseFile(
|
|||
|
IN PEPROCESS Process OPTIONAL,
|
|||
|
IN PVOID Object,
|
|||
|
IN ULONG GrantedAccess,
|
|||
|
IN ULONG ProcessHandleCount,
|
|||
|
IN ULONG SystemHandleCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked whenever a handle to a file is deleted. If the
|
|||
|
handle being deleted is the last handle to the file (the ProcessHandleCount
|
|||
|
parameter is one), then all locks for the file owned by the specified
|
|||
|
process must be released.
|
|||
|
|
|||
|
Likewise, if the SystemHandleCount is one then this is the last handle
|
|||
|
for this for file object across all processes. For this case, the file
|
|||
|
system is notified so that it can perform any necessary cleanup on the
|
|||
|
file.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Process - A pointer to the process that closed the handle.
|
|||
|
|
|||
|
Object - A pointer to the file object that the handle referenced.
|
|||
|
|
|||
|
GrantedAccess - Access that was granted to the object through the handle.
|
|||
|
|
|||
|
ProcessHandleCount - Count of handles outstanding to the object for the
|
|||
|
process specified by the Process argument. If the count is one
|
|||
|
then this is the last handle to this file by that process.
|
|||
|
|
|||
|
SystemHandleCount - Count of handles outstanding to the object for the
|
|||
|
entire system. If the count is one then this is the last handle
|
|||
|
to this file in the system.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PFAST_IO_DISPATCH fastIoDispatch;
|
|||
|
NTSTATUS status;
|
|||
|
KEVENT event;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( Process );
|
|||
|
UNREFERENCED_PARAMETER( GrantedAccess );
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// If the handle count is not one then this is not the last close of
|
|||
|
// this file for the specified process so there is nothing to do.
|
|||
|
//
|
|||
|
|
|||
|
if (ProcessHandleCount != 1) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
fileObject = (PFILE_OBJECT) Object;
|
|||
|
|
|||
|
if (fileObject->LockOperation && SystemHandleCount != 1) {
|
|||
|
|
|||
|
IO_STATUS_BLOCK localIoStatus;
|
|||
|
|
|||
|
//
|
|||
|
// This is the last handle for the specified process and the process
|
|||
|
// called the NtLockFile or NtUnlockFile system services at least once.
|
|||
|
// Also, this is not the last handle for this file object system-wide
|
|||
|
// so unlock all of the pending locks for this process. Note that
|
|||
|
// this check causes an optimization so that if this is the last
|
|||
|
// system-wide handle to this file object the cleanup code will take
|
|||
|
// care of releasing any locks on the file rather than having to
|
|||
|
// send the file system two different packets to get them shut down.
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the target device object and the Fast I/O dispatch
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
} else {
|
|||
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|||
|
}
|
|||
|
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
|||
|
|
|||
|
//
|
|||
|
// If this file is open for synchronous I/O, wait until this thread
|
|||
|
// owns it exclusively since there may still be a thread using it.
|
|||
|
// This occurs when a system service owns the file because it owns
|
|||
|
// the semaphore, but the I/O completion code has already dereferenced
|
|||
|
// the file object itself. Without waiting here for the same semaphore
|
|||
|
// there would be a race condition in the service who owns it now. The
|
|||
|
// service needs to be able to access the object w/o it going away after
|
|||
|
// its wait for the file event is satisfied.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
&interrupted );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Turbo unlock support. If the fast Io Dispatch specifies a fast lock
|
|||
|
// routine then we'll first try and calling it with the specified lock
|
|||
|
// parameters. If this is all successful then we do not need to do
|
|||
|
// the Irp based unlock all call.
|
|||
|
//
|
|||
|
|
|||
|
if (fastIoDispatch &&
|
|||
|
fastIoDispatch->FastIoUnlockAll &&
|
|||
|
fastIoDispatch->FastIoUnlockAll( fileObject,
|
|||
|
PsGetCurrentProcess(),
|
|||
|
&localIoStatus,
|
|||
|
deviceObject )) {
|
|||
|
|
|||
|
NOTHING;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the local event that will be used to synchronize access
|
|||
|
// to the driver completing this I/O operation.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
// Reset the event in the file object.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize the I/O Request Packet (IRP) for this
|
|||
|
// operation.
|
|||
|
//
|
|||
|
|
|||
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|||
|
irp->RequestorMode = KernelMode;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the service independent parameters in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
irp->UserEvent = &event;
|
|||
|
irp->UserIosb = &irp->IoStatus;
|
|||
|
irp->Flags = IRP_SYNCHRONOUS_API;
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This will
|
|||
|
// be used to pass the original function codes and parameters. No
|
|||
|
// function-specific parameters are required for this operation.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
irpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|||
|
irpSp->MinorFunction = IRP_MN_UNLOCK_ALL;
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the fileobject again for the IRP (cleared on completion)
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject( fileObject );
|
|||
|
|
|||
|
//
|
|||
|
// Insert the packet at the head of the IRP list for the thread.
|
|||
|
//
|
|||
|
|
|||
|
IopQueueThreadIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Invoke the driver at its appropriate dispatch entry with the IRP.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver( deviceObject, irp );
|
|||
|
|
|||
|
//
|
|||
|
// If no error was incurred, wait for the I/O operation to complete.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject( &event,
|
|||
|
UserRequest,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER) NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this operation was a synchronous I/O operation, release the
|
|||
|
// semaphore so that the file can be used by other threads.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
IopReleaseFileObjectLock( fileObject );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (SystemHandleCount == 1) {
|
|||
|
|
|||
|
//
|
|||
|
// The last handle to this file object for all of the processes in the
|
|||
|
// system has just been closed, so invoke the driver's "cleanup" handler
|
|||
|
// for this file. This is the file system's opportunity to remove any
|
|||
|
// share access information for the file, to indicate that if the file
|
|||
|
// is opened for a caching operation and this is the last file object
|
|||
|
// to the file, then it can do whatever it needs with memory management
|
|||
|
// to cleanup any information.
|
|||
|
//
|
|||
|
// Begin by getting the address of the target device object.
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
} else {
|
|||
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Ensure that the I/O system believes that this file has a handle
|
|||
|
// associated with it in case it doesn't actually get one from the
|
|||
|
// Object Manager. This is done because sometimes the Object Manager
|
|||
|
// actually creates a handle, but the I/O system never finds out
|
|||
|
// about it so it attempts to send two cleanups for the same file.
|
|||
|
//
|
|||
|
|
|||
|
fileObject->Flags |= FO_HANDLE_CREATED;
|
|||
|
|
|||
|
//
|
|||
|
// If this file is open for synchronous I/O, wait until this thread
|
|||
|
// owns it exclusively since there may still be a thread using it.
|
|||
|
// This occurs when a system service owns the file because it owns
|
|||
|
// the semaphore, but the I/O completion code has already dereferenced
|
|||
|
// the file object itself. Without waiting here for the same semaphore
|
|||
|
// there would be a race condition in the service who owns it now. The
|
|||
|
// service needs to be able to access the object w/o it going away after
|
|||
|
// its wait for the file event is satisfied.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
&interrupted );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the local event that will be used to synchronize access
|
|||
|
// to the driver completing this I/O operation.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
// Reset the event in the file object.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize the I/O Request Packet (IRP) for this
|
|||
|
// operation.
|
|||
|
//
|
|||
|
|
|||
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|||
|
irp->RequestorMode = KernelMode;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the service independent parameters in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
irp->UserEvent = &event;
|
|||
|
irp->UserIosb = &irp->IoStatus;
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
|
|||
|
irp->Flags = IRP_SYNCHRONOUS_API | IRP_CLOSE_OPERATION;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This will
|
|||
|
// be used to pass the original function codes and parameters. No
|
|||
|
// function-specific parameters are required for this operation.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
irpSp->MajorFunction = IRP_MJ_CLEANUP;
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Insert the packet at the head of the IRP list for the thread.
|
|||
|
//
|
|||
|
|
|||
|
IopQueueThreadIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Update the operation count statistic for the current process for
|
|||
|
// operations other than read and write.
|
|||
|
//
|
|||
|
|
|||
|
IopUpdateOtherOperationCount();
|
|||
|
|
|||
|
//
|
|||
|
// Invoke the driver at its appropriate dispatch entry with the IRP.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver( deviceObject, irp );
|
|||
|
|
|||
|
//
|
|||
|
// If no error was incurred, wait for the I/O operation to complete.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject( &event,
|
|||
|
UserRequest,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER) NULL );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The following code tears down the IRP by hand since it may not
|
|||
|
// either be possible to it to be completed (because this code was
|
|||
|
// invoked as APC_LEVEL in the first place - or because the reference
|
|||
|
// count on the object cannot be incremented due to this routine
|
|||
|
// being invoked by the delete file procedure below). Cleanup IRPs
|
|||
|
// therefore use close sematics (the close operation flag is set
|
|||
|
// in the IRP) so that the I/O complete request routine itself sets
|
|||
|
// the event to the Signaled state.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql( APC_LEVEL, &irql );
|
|||
|
IopDequeueThreadIrp( irp );
|
|||
|
KeLowerIrql( irql );
|
|||
|
|
|||
|
//
|
|||
|
// Also, free the IRP.
|
|||
|
//
|
|||
|
|
|||
|
IoFreeIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// If this operation was a synchronous I/O operation, release the
|
|||
|
// semaphore so that the file can be used by other threads.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
IopReleaseFileObjectLock( fileObject );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
IopDeleteFile(
|
|||
|
IN PVOID Object
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked when the last handle to a specific file handle is
|
|||
|
being closed and the file object is going away. It is the responsibility
|
|||
|
of this routine to perform the following functions:
|
|||
|
|
|||
|
o Notify the device driver that the file object is open on that the
|
|||
|
file is being closed.
|
|||
|
|
|||
|
o Dereference the user's error port for the file object, if there
|
|||
|
is one associated with the file object.
|
|||
|
|
|||
|
o Decrement the device object reference count.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Pointer to the file object being deleted.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PDEVICE_OBJECT fsDevice = NULL;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
KIRQL irql;
|
|||
|
NTSTATUS status;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
KEVENT event;
|
|||
|
PVPB vpb;
|
|||
|
BOOLEAN referenceCountDecremented;
|
|||
|
|
|||
|
//
|
|||
|
// Obtain a pointer to the file object.
|
|||
|
//
|
|||
|
|
|||
|
fileObject = (PFILE_OBJECT) Object;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the first device driver which should be notified that
|
|||
|
// this file is going away. If the device driver field is NULL, then this
|
|||
|
// file is being shut down due to an error attempting to get it open in the
|
|||
|
// first place, so do not do any further processing.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->DeviceObject) {
|
|||
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
} else {
|
|||
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this file has never had a file handle created for it, and yet
|
|||
|
// it exists, invoke the close file procedure so that the file system
|
|||
|
// gets the cleanup IRP it is expecting before sending the close IRP.
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_HANDLE_CREATED)) {
|
|||
|
IopCloseFile( (PEPROCESS) NULL,
|
|||
|
Object,
|
|||
|
0,
|
|||
|
1,
|
|||
|
1 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this file is open for synchronous I/O, wait until this thread
|
|||
|
// owns it exclusively since there may still be a thread using it.
|
|||
|
// This occurs when a system service owns the file because it owns
|
|||
|
// the semaphore, but the I/O completion code has already dereferenced
|
|||
|
// the file object itself. Without waiting here for the same semaphore
|
|||
|
// there would be a race condition in the service who owns it now. The
|
|||
|
// service needs to be able to access the object w/o it going away after
|
|||
|
// its wait for the file event is satisfied.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
(VOID) IopAcquireFileObjectLock( fileObject,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
&interrupted );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reset a local event that can be used to wait for the device driver
|
|||
|
// to close the file.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
// Reset the event in the file object.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an I/O Request Packet (IRP) to be used in communicating with
|
|||
|
// the appropriate device driver that the file is being closed. Notice
|
|||
|
// that the allocation of this packet is done without charging quota so
|
|||
|
// that the operation will not fail. This is done because there is no
|
|||
|
// way to return an error to the caller at this point.
|
|||
|
//
|
|||
|
|
|||
|
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
|
|||
|
if (!irp) {
|
|||
|
irp = IopAllocateIrpMustSucceed( deviceObject->StackSize );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This is
|
|||
|
// where the function codes and parameters are placed.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the IRP, indicating that this file object is being deleted.
|
|||
|
//
|
|||
|
|
|||
|
irpSp->MajorFunction = IRP_MJ_CLOSE;
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
irp->UserIosb = &ioStatusBlock;
|
|||
|
irp->UserEvent = &event;
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|||
|
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
|
|||
|
irp->Flags = IRP_CLOSE_OPERATION | IRP_SYNCHRONOUS_API;
|
|||
|
|
|||
|
//
|
|||
|
// Place this packet in the thread's I/O pending queue.
|
|||
|
//
|
|||
|
|
|||
|
IopQueueThreadIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the reference count on the VPB, if necessary. We
|
|||
|
// have to do this BEFORE handing the Irp to the file system
|
|||
|
// because of a trick the file systems play with close, and
|
|||
|
// believe me, you really don't want to know what it is.
|
|||
|
//
|
|||
|
// Since there is not a error path here (close cannot fail),
|
|||
|
// and the file system is the only ome who can actually synchronize
|
|||
|
// with the actual completion of close processing, the file system
|
|||
|
// is the one responsible for Vpb deletion.
|
|||
|
//
|
|||
|
|
|||
|
vpb = fileObject->Vpb;
|
|||
|
|
|||
|
|
|||
|
if (vpb && !(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
IopInterlockedDecrementUlong( LockQueueIoVpbLock,
|
|||
|
&vpb->ReferenceCount );
|
|||
|
|
|||
|
//
|
|||
|
// Bump the handle count of the filesystem volume device object.
|
|||
|
// This will prevent the filesystem filter stack from being torn down
|
|||
|
// until after the close IRP completes.
|
|||
|
//
|
|||
|
|
|||
|
fsDevice = vpb->DeviceObject;
|
|||
|
if (fsDevice) {
|
|||
|
IopInterlockedIncrementUlong( LockQueueIoDatabaseLock,
|
|||
|
&fsDevice->ReferenceCount );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this device object has stated for a fact that it knows it will
|
|||
|
// never have the final non-zero reference count among the other
|
|||
|
// device objects associated with our driver object, then decrement
|
|||
|
// our reference count here BEFORE calling the file system. This
|
|||
|
// is required because for a special class of device objects, the
|
|||
|
// file system may delete them.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->DeviceObject->Flags & DO_NEVER_LAST_DEVICE) {
|
|||
|
IopInterlockedDecrementUlong( LockQueueIoDatabaseLock,
|
|||
|
&fileObject->DeviceObject->ReferenceCount );
|
|||
|
|
|||
|
referenceCountDecremented = TRUE;
|
|||
|
} else {
|
|||
|
referenceCountDecremented = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Give the device driver the packet. If this request does not work,
|
|||
|
// there is nothing that can be done about it. This is unfortunate
|
|||
|
// because the driver may have had problems that it was about to
|
|||
|
// report about other operations (e.g., write behind failures, etc.)
|
|||
|
// that it can no longer report. The reason is that this routine
|
|||
|
// is really initially invoked by NtClose, which has already closed
|
|||
|
// the caller's handle, and that's what the return status from close
|
|||
|
// indicates: the handle has successfully been closed.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver( deviceObject, irp );
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject( &event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER) NULL );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Perform any completion operations that need to be performed on
|
|||
|
// the IRP that was used for this request. This is done here as
|
|||
|
// as opposed to in normal completion code because there is a race
|
|||
|
// condition between when this routine executes if it was invoked
|
|||
|
// from a special kernel APC (e.g., some IRP was just completed and
|
|||
|
// dereferenced this file object for the last time), and when the
|
|||
|
// special kernel APC because of this packet's completion executing.
|
|||
|
//
|
|||
|
// This problem is solved by not having to queue a special kernel
|
|||
|
// APC routine for completion of this packet. Rather, it is treated
|
|||
|
// much like a synchronous paging I/O operation, except that the
|
|||
|
// packet is not even freed during I/O completion. This is because
|
|||
|
// the packet is still in this thread's queue, and there is no way
|
|||
|
// to get it out except at APC_LEVEL. Unfortunately, the part of
|
|||
|
// I/O completion that needs to dequeue the packet is running at
|
|||
|
// DISPATCH_LEVEL.
|
|||
|
//
|
|||
|
// Hence, the packet must be removed from the queue (synchronized,
|
|||
|
// of course), and then it must be freed.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql( APC_LEVEL, &irql );
|
|||
|
IopDequeueThreadIrp( irp );
|
|||
|
KeLowerIrql( irql );
|
|||
|
|
|||
|
IoFreeIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Free the file name string buffer if there was one.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->FileName.Length != 0) {
|
|||
|
ExFreePool( fileObject->FileName.Buffer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there was an completion port associated w/this file object, dereference
|
|||
|
// it now, and deallocate the completion context pool.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->CompletionContext) {
|
|||
|
ObDereferenceObject( fileObject->CompletionContext->Port );
|
|||
|
ExFreePool( fileObject->CompletionContext );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the file context control structure if it exists.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION) {
|
|||
|
FsRtlPTeardownPerFileObjectContexts(fileObject);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the real device object so its reference count
|
|||
|
// can be decremented.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject = fileObject->DeviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the reference count on the device object. Note that
|
|||
|
// if the driver has been marked for an unload operation, and the
|
|||
|
// reference count goes to zero, then the driver may need to be
|
|||
|
// unloaded at this point.
|
|||
|
//
|
|||
|
// Note: only do this if the reference count was not already done
|
|||
|
// above. The device object may be gone in this case.
|
|||
|
//
|
|||
|
|
|||
|
if (!referenceCountDecremented) {
|
|||
|
|
|||
|
IopDecrementDeviceObjectRef(
|
|||
|
deviceObject,
|
|||
|
FALSE,
|
|||
|
!ObIsObjectDeletionInline(Object)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the filesystem's volume device object handle count
|
|||
|
// so that deletes can proceed.
|
|||
|
//
|
|||
|
|
|||
|
if (fsDevice && vpb) {
|
|||
|
IopDecrementDeviceObjectRef(fsDevice,
|
|||
|
FALSE,
|
|||
|
!ObIsObjectDeletionInline(Object)
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
IopDeleteDriver(
|
|||
|
IN PVOID Object
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked when the reference count for a driver object
|
|||
|
becomes zero. That is, the last reference for the driver has gone away.
|
|||
|
This routine ensures that the object is cleaned up and the driver
|
|||
|
unloaded.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Pointer to the driver object whose reference count has gone
|
|||
|
to zero.
|
|||
|
|
|||
|
Return value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDRIVER_OBJECT driverObject = (PDRIVER_OBJECT) Object;
|
|||
|
PIO_CLIENT_EXTENSION extension;
|
|||
|
PIO_CLIENT_EXTENSION nextExtension;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT( !driverObject->DeviceObject );
|
|||
|
|
|||
|
//
|
|||
|
// Free any client driver object extensions.
|
|||
|
//
|
|||
|
|
|||
|
extension = driverObject->DriverExtension->ClientDriverExtension;
|
|||
|
while (extension != NULL) {
|
|||
|
|
|||
|
nextExtension = extension->NextExtension;
|
|||
|
ExFreePool( extension );
|
|||
|
extension = nextExtension;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is a driver section then unload the driver.
|
|||
|
//
|
|||
|
|
|||
|
if (driverObject->DriverSection != NULL) {
|
|||
|
//
|
|||
|
// Make sure any DPC's that may be running inside the driver have completed
|
|||
|
//
|
|||
|
#if !defined(NT_UP)
|
|||
|
KeFlushQueuedDpcs ();
|
|||
|
#endif
|
|||
|
MmUnloadSystemImage( driverObject->DriverSection );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the pool associated with the name of the driver.
|
|||
|
//
|
|||
|
|
|||
|
if (driverObject->DriverName.Buffer) {
|
|||
|
ExFreePool( driverObject->DriverName.Buffer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the pool associated with the service key name of the driver.
|
|||
|
//
|
|||
|
|
|||
|
if (driverObject->DriverExtension->ServiceKeyName.Buffer) {
|
|||
|
ExFreePool( driverObject->DriverExtension->ServiceKeyName.Buffer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the pool associated with the FsFilterCallbacks structure.
|
|||
|
//
|
|||
|
|
|||
|
if (driverObject->DriverExtension->FsFilterCallbacks) {
|
|||
|
ExFreePool( driverObject->DriverExtension->FsFilterCallbacks );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
IopDeleteDevice(
|
|||
|
IN PVOID Object
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked when the reference count for a device object
|
|||
|
becomes zero. That is, the last reference for the device has gone away.
|
|||
|
This routine ensures that the object is cleaned up and the driver object
|
|||
|
is dereferenced.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Pointer to the driver object whose reference count has gone
|
|||
|
to zero.
|
|||
|
|
|||
|
Return value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_OBJECT deviceObject = (PDEVICE_OBJECT) Object;
|
|||
|
PVPB vpb = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
IopDestroyDeviceNode(deviceObject->DeviceObjectExtension->DeviceNode);
|
|||
|
|
|||
|
//
|
|||
|
// If there's still a VPB attached then free it.
|
|||
|
//
|
|||
|
|
|||
|
vpb = InterlockedExchangePointer(&(deviceObject->Vpb), vpb);
|
|||
|
|
|||
|
if(vpb != NULL) {
|
|||
|
|
|||
|
ASSERTMSG("Unreferenced device object to be deleted is still in use",
|
|||
|
((vpb->Flags & (VPB_MOUNTED | VPB_LOCKED)) == 0));
|
|||
|
|
|||
|
ASSERT(vpb->ReferenceCount == 0);
|
|||
|
ExFreePool(vpb);
|
|||
|
}
|
|||
|
if (deviceObject->DriverObject != NULL) {
|
|||
|
ObDereferenceObject( deviceObject->DriverObject );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDEVICE_OBJECT
|
|||
|
IopGetDevicePDO(
|
|||
|
IN PDEVICE_OBJECT DeviceObject
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Call this routine to obtain the Base PDO for a device object
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - pointer to device object to get PDO for
|
|||
|
|
|||
|
ReturnValue:
|
|||
|
|
|||
|
PDO if DeviceObject is attached to a PDO, otherwise NULL
|
|||
|
The returned PDO is reference-counted
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_OBJECT deviceBaseObject;
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
ASSERT(DeviceObject);
|
|||
|
|
|||
|
irql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock );
|
|||
|
deviceBaseObject = IopGetDeviceAttachmentBase(DeviceObject);
|
|||
|
if ((deviceBaseObject->Flags & DO_BUS_ENUMERATED_DEVICE) != 0) {
|
|||
|
//
|
|||
|
// we have determined that this is attached to a PDO
|
|||
|
//
|
|||
|
ObReferenceObject( deviceBaseObject );
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// not a PDO
|
|||
|
//
|
|||
|
deviceBaseObject = NULL;
|
|||
|
}
|
|||
|
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql );
|
|||
|
|
|||
|
return deviceBaseObject;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopSetDeviceSecurityDescriptor(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the security descriptor on a single device object
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - pointer to base device object
|
|||
|
|
|||
|
SecurityInformation - Fields of SD to change
|
|||
|
SecurityDescriptor - New security descriptor
|
|||
|
PoolType - Pool type for alloctions
|
|||
|
GenericMapping - Generic mapping for this object
|
|||
|
|
|||
|
ReturnValue:
|
|||
|
|
|||
|
success, or error while setting the descriptor for the device object.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
PSECURITY_DESCRIPTOR OldDescriptor;
|
|||
|
PSECURITY_DESCRIPTOR NewDescriptor;
|
|||
|
PSECURITY_DESCRIPTOR CachedDescriptor;
|
|||
|
NTSTATUS Status;
|
|||
|
PKTHREAD CurrentThread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// In order to preserve some protected fields in the SD (like the SACL) we need to make sure that only one
|
|||
|
// thread updates it at any one time. If we didn't do this another modification could wipe out a SACL
|
|||
|
// an administrator was adding.
|
|||
|
//
|
|||
|
CurrentThread = KeGetCurrentThread ();
|
|||
|
while (1) {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the security descriptor
|
|||
|
//
|
|||
|
|
|||
|
KeEnterCriticalRegionThread(CurrentThread);
|
|||
|
ExAcquireResourceSharedLite( &IopSecurityResource, TRUE );
|
|||
|
|
|||
|
OldDescriptor = DeviceObject->SecurityDescriptor;
|
|||
|
if (OldDescriptor != NULL) {
|
|||
|
ObReferenceSecurityDescriptor( OldDescriptor, 1 );
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseResourceLite( &IopSecurityResource );
|
|||
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|||
|
|
|||
|
NewDescriptor = OldDescriptor;
|
|||
|
|
|||
|
Status = SeSetSecurityDescriptorInfo( NULL,
|
|||
|
SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
&NewDescriptor,
|
|||
|
PoolType,
|
|||
|
GenericMapping );
|
|||
|
//
|
|||
|
// If we successfully set the new security descriptor then we
|
|||
|
// need to log it in our database and get yet another pointer
|
|||
|
// to the finaly security descriptor
|
|||
|
//
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
Status = ObLogSecurityDescriptor( NewDescriptor,
|
|||
|
&CachedDescriptor,
|
|||
|
1 );
|
|||
|
ExFreePool( NewDescriptor );
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
//
|
|||
|
// Now we need to see if anyone else update this security descriptor inside the
|
|||
|
// gap where we didn't hold the lock. If they did then we just try it all again.
|
|||
|
//
|
|||
|
KeEnterCriticalRegionThread(CurrentThread);
|
|||
|
ExAcquireResourceExclusiveLite( &IopSecurityResource, TRUE );
|
|||
|
|
|||
|
if (DeviceObject->SecurityDescriptor == OldDescriptor) {
|
|||
|
//
|
|||
|
// Do the swap
|
|||
|
//
|
|||
|
DeviceObject->SecurityDescriptor = CachedDescriptor;
|
|||
|
|
|||
|
ExReleaseResourceLite( &IopSecurityResource );
|
|||
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|||
|
|
|||
|
//
|
|||
|
// If there was an original object then we need to work out how many
|
|||
|
// cached references there were (if any) and return them.
|
|||
|
//
|
|||
|
ObDereferenceSecurityDescriptor( OldDescriptor, 2 );
|
|||
|
break;
|
|||
|
} else {
|
|||
|
|
|||
|
ExReleaseResourceLite( &IopSecurityResource );
|
|||
|
KeLeaveCriticalRegionThread(CurrentThread);
|
|||
|
|
|||
|
ObDereferenceSecurityDescriptor( OldDescriptor, 1 );
|
|||
|
ObDereferenceSecurityDescriptor( CachedDescriptor, 1);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Dereference old SecurityDescriptor
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceSecurityDescriptor( OldDescriptor, 1 );
|
|||
|
break;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Dereference old SecurityDescriptor
|
|||
|
//
|
|||
|
if (OldDescriptor != NULL) {
|
|||
|
ObDereferenceSecurityDescriptor( OldDescriptor, 1 );
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopSetDeviceSecurityDescriptors(
|
|||
|
IN PDEVICE_OBJECT OriginalDeviceObject,
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets the security descriptor on all the devices on the
|
|||
|
device stack for a PNP device. Ideally when the object manager asks the
|
|||
|
IO manager to set the security descriptor of a device object the IO manager
|
|||
|
should set the descriptor only on that device object. This is the classical
|
|||
|
behaviour.
|
|||
|
Unfortunately for PNP devices there may be multiple devices on a device
|
|||
|
stack with names.
|
|||
|
If the descriptor is applied to only one of the devices on the stack its
|
|||
|
opens up a security hole as there may be other devices with name on the
|
|||
|
stack which can be opened by a random program.
|
|||
|
To protect against this the descriptor is applied to all device objects on
|
|||
|
the stack.
|
|||
|
Its important that to protect compatibility we need to return the same
|
|||
|
status as what would have been returned if only the requested device
|
|||
|
object's descriptor was set.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
OriginalDeviceObject - Pointer to the device object passed by the object manager
|
|||
|
DeviceObject - pointer to base device object (first one to set)
|
|||
|
SecurityInformation )_ passed directly from IopGetSetSecurityObject
|
|||
|
SecurityDescriptor )
|
|||
|
PoolType )
|
|||
|
GenericMapping )
|
|||
|
|
|||
|
ReturnValue:
|
|||
|
|
|||
|
success, or error while setting the descriptor for the original device object.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_OBJECT NewDeviceObject = NULL;
|
|||
|
PSECURITY_DESCRIPTOR OldSecurityDescriptor, NewSecurityDescriptor, CachedSecurityDescriptor;
|
|||
|
KIRQL irql;
|
|||
|
NTSTATUS status;
|
|||
|
NTSTATUS returnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
ASSERT(DeviceObject);
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// pre-reference this object to match the dereference later
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject( DeviceObject );
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Reference the existing security descriptor so it can't be reused.
|
|||
|
// We will only do the final security change if nobody else changes
|
|||
|
// the security while we don't hold the lock. Doing this prevents
|
|||
|
// privileged information being lost like the SACL.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Save away and return the device status only for the main device object
|
|||
|
// For example if OldSecurityDescriptor is NULL the IO manager should
|
|||
|
// return STATUS_NO_SECURITY_ON_OBJECT.
|
|||
|
//
|
|||
|
|
|||
|
status = IopSetDeviceSecurityDescriptor( DeviceObject,
|
|||
|
SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
PoolType,
|
|||
|
GenericMapping );
|
|||
|
|
|||
|
|
|||
|
if (DeviceObject == OriginalDeviceObject) {
|
|||
|
returnStatus = status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// We don't need to acquire the database lock because
|
|||
|
// we have a handle to this device stack.
|
|||
|
//
|
|||
|
|
|||
|
NewDeviceObject = DeviceObject->AttachedDevice;
|
|||
|
if ( NewDeviceObject != NULL ) {
|
|||
|
ObReferenceObject( NewDeviceObject );
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject( DeviceObject );
|
|||
|
DeviceObject = NewDeviceObject;
|
|||
|
|
|||
|
} while (NewDeviceObject);
|
|||
|
|
|||
|
return returnStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
IopGetSetSecurityObject(
|
|||
|
IN PVOID Object,
|
|||
|
IN SECURITY_OPERATION_CODE OperationCode,
|
|||
|
IN PSECURITY_INFORMATION SecurityInformation,
|
|||
|
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|||
|
IN OUT PULONG CapturedLength,
|
|||
|
IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN PGENERIC_MAPPING GenericMapping
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked to either query or set the security descriptor
|
|||
|
for a file, directory, volume, or device. It implements these functions
|
|||
|
by either performing an in-line check if the file is a device or a
|
|||
|
volume, or an I/O Request Packet (IRP) is generated and given to the
|
|||
|
driver to perform the operation.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Object - Pointer to the file or device object representing the open object.
|
|||
|
|
|||
|
SecurityInformation - Information about what is being done to or obtained
|
|||
|
from the object's security descriptor.
|
|||
|
|
|||
|
SecurityDescriptor - Supplies the base security descriptor and returns
|
|||
|
the final security descriptor. Note that if this buffer is coming
|
|||
|
from user space, it has already been probed by the object manager
|
|||
|
to length "CapturedLength", otherwise it points to kernel space and
|
|||
|
should not be probed. It must, however, be referenced in a try
|
|||
|
clause.
|
|||
|
|
|||
|
CapturedLength - For a query operation this specifies the size, in
|
|||
|
bytes, of the output security descriptor buffer and on return
|
|||
|
contains the number of bytes needed to store the complete security
|
|||
|
descriptor. If the length needed is greater than the length
|
|||
|
supplied the operation will fail. This parameter is ignored for
|
|||
|
the set and delete operations. It is expected to point into
|
|||
|
system space, ie, it need not be probed and it will not change.
|
|||
|
|
|||
|
ObjectsSecurityDescriptor - Supplies and returns the object's security
|
|||
|
descriptor.
|
|||
|
|
|||
|
PoolType - Specifies from which type of pool memory is to be allocated.
|
|||
|
|
|||
|
GenericMapping - Supplies the generic mapping for the object type.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The final status of the operation is returned as the function value.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PDEVICE_OBJECT devicePDO = NULL;
|
|||
|
BOOLEAN synchronousIo;
|
|||
|
PSECURITY_DESCRIPTOR oldSecurityDescriptor, CachedSecurityDescriptor;
|
|||
|
PETHREAD CurrentThread;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( ObjectsSecurityDescriptor );
|
|||
|
UNREFERENCED_PARAMETER( PoolType );
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Begin by determining whether the security operation is to be performed
|
|||
|
// in this routine or by the driver. This is based upon whether the
|
|||
|
// object represents a device object, or it represents a file object
|
|||
|
// to a device, or a file on the device. If the open is a direct device
|
|||
|
// open then use the device object.
|
|||
|
//
|
|||
|
|
|||
|
if (((PDEVICE_OBJECT) (Object))->Type == IO_TYPE_DEVICE) {
|
|||
|
deviceObject = (PDEVICE_OBJECT) Object;
|
|||
|
fileObject = (PFILE_OBJECT) NULL;
|
|||
|
} else {
|
|||
|
fileObject = (PFILE_OBJECT) Object;
|
|||
|
deviceObject = fileObject->DeviceObject;
|
|||
|
}
|
|||
|
|
|||
|
if (!fileObject ||
|
|||
|
(!fileObject->FileName.Length && !fileObject->RelatedFileObject) ||
|
|||
|
(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
|
|||
|
//
|
|||
|
// This security operation is for the device itself, either through
|
|||
|
// a file object, or directly to the device object. For the latter
|
|||
|
// case, assignment operations are also possible. Also note that
|
|||
|
// this may be a stream file object, which do not have security.
|
|||
|
// The security for a stream file is actually represented by the
|
|||
|
// security descriptor on the file itself, or the volume, or the
|
|||
|
// device.
|
|||
|
//
|
|||
|
|
|||
|
if (OperationCode == AssignSecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// Simply assign the security descriptor to the device object,
|
|||
|
// if this is a device object.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
if (fileObject == NULL || !(fileObject->Flags & FO_STREAM_FILE)) {
|
|||
|
|
|||
|
status = ObLogSecurityDescriptor( SecurityDescriptor,
|
|||
|
&CachedSecurityDescriptor,
|
|||
|
1 );
|
|||
|
ExFreePool (SecurityDescriptor);
|
|||
|
if (NT_SUCCESS( status )) {
|
|||
|
|
|||
|
CurrentThread = PsGetCurrentThread ();
|
|||
|
KeEnterCriticalRegionThread(&CurrentThread->Tcb);
|
|||
|
ExAcquireResourceExclusiveLite( &IopSecurityResource, TRUE );
|
|||
|
|
|||
|
deviceObject->SecurityDescriptor = CachedSecurityDescriptor;
|
|||
|
|
|||
|
ExReleaseResourceLite( &IopSecurityResource );
|
|||
|
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else if (OperationCode == SetSecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a set operation. The SecurityInformation parameter
|
|||
|
// determines what part of the SecurityDescriptor is going to
|
|||
|
// be applied to the ObjectsSecurityDescriptor.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// if this deviceObject is attached to a PDO then we want
|
|||
|
// to modify the security on the PDO and apply it up the
|
|||
|
// device chain. This applies to PNP device objects only. See
|
|||
|
// comment in IopSetDeviceSecurityDescriptors
|
|||
|
//
|
|||
|
devicePDO = IopGetDevicePDO(deviceObject);
|
|||
|
|
|||
|
if (devicePDO) {
|
|||
|
|
|||
|
//
|
|||
|
// set PDO and all attached device objects
|
|||
|
//
|
|||
|
|
|||
|
status = IopSetDeviceSecurityDescriptors(
|
|||
|
deviceObject,
|
|||
|
devicePDO,
|
|||
|
SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
PoolType,
|
|||
|
GenericMapping );
|
|||
|
|
|||
|
ObDereferenceObject( devicePDO );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// set this device object only
|
|||
|
//
|
|||
|
|
|||
|
status = IopSetDeviceSecurityDescriptor( deviceObject,
|
|||
|
SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
PoolType,
|
|||
|
GenericMapping );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else if (OperationCode == QuerySecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a get operation. The SecurityInformation parameter
|
|||
|
// determines what part of the SecurityDescriptor is going to
|
|||
|
// be returned from the ObjectsSecurityDescriptor.
|
|||
|
//
|
|||
|
|
|||
|
CurrentThread = PsGetCurrentThread ();
|
|||
|
KeEnterCriticalRegionThread(&CurrentThread->Tcb);
|
|||
|
ExAcquireResourceSharedLite( &IopSecurityResource, TRUE );
|
|||
|
|
|||
|
oldSecurityDescriptor = deviceObject->SecurityDescriptor;
|
|||
|
if (oldSecurityDescriptor != NULL) {
|
|||
|
ObReferenceSecurityDescriptor( oldSecurityDescriptor, 1 );
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseResourceLite( &IopSecurityResource );
|
|||
|
KeLeaveCriticalRegionThread(&CurrentThread->Tcb);
|
|||
|
|
|||
|
status = SeQuerySecurityDescriptorInfo( SecurityInformation,
|
|||
|
SecurityDescriptor,
|
|||
|
CapturedLength,
|
|||
|
&oldSecurityDescriptor );
|
|||
|
|
|||
|
if (oldSecurityDescriptor != NULL) {
|
|||
|
ObDereferenceSecurityDescriptor( oldSecurityDescriptor, 1 );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a delete operation. Simply indicate that everything
|
|||
|
// worked just fine.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else if (OperationCode == DeleteSecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a delete operation for the security descriptor on a file
|
|||
|
// object. This function will be performed by the file system once
|
|||
|
// the FCB itself is deleted. Simply indicate that the operation
|
|||
|
// was successful.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
PIRP irp;
|
|||
|
IO_STATUS_BLOCK localIoStatus;
|
|||
|
KEVENT event;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
KPROCESSOR_MODE requestorMode;
|
|||
|
|
|||
|
//
|
|||
|
// This file object does not refer to the device itself. Rather, it
|
|||
|
// refers to either a file or a directory on the device. This means
|
|||
|
// that the request must be passed to the file system for processing.
|
|||
|
// Note that the only requests that are passed through in this manner
|
|||
|
// are SET or QUERY security operations. DELETE operations have
|
|||
|
// already been taken care of above since the file system which just
|
|||
|
// drop the storage on the floor when it really needs to, and ASSIGN
|
|||
|
// operations are irrelevant to file systems since they never
|
|||
|
// generate one because they never assign the security descriptor
|
|||
|
// to the object in the first place, they just assign it to the FCB.
|
|||
|
//
|
|||
|
|
|||
|
CurrentThread = PsGetCurrentThread ();
|
|||
|
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
|||
|
|
|||
|
//
|
|||
|
// Begin by referencing the object by pointer. Note that the object
|
|||
|
// handle has already been checked for the appropriate access by the
|
|||
|
// object system caller. This reference must be performed because
|
|||
|
// standard I/O completion will dereference the object.
|
|||
|
//
|
|||
|
|
|||
|
ObReferenceObject( fileObject );
|
|||
|
|
|||
|
//
|
|||
|
// Make a special check here to determine whether this is a synchronous
|
|||
|
// I/O operation. If it is, then wait here until the file is owned by
|
|||
|
// the current thread. If this is not a (serialized) synchronous I/O
|
|||
|
// operation, then initialize the local event.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
status = IopAcquireFileObjectLock( fileObject,
|
|||
|
requestorMode,
|
|||
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|||
|
&interrupted );
|
|||
|
if (interrupted) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
synchronousIo = TRUE;
|
|||
|
} else {
|
|||
|
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
|
|||
|
synchronousIo = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the file object to the Not-Signaled state.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the target device object.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize the I/O Request Packet (IRP) for this
|
|||
|
// operation. The allocation is performed with an exception handler
|
|||
|
// in case the caller does not have enough quota to allocate the packet.
|
|||
|
|
|||
|
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
|
|||
|
if (!irp) {
|
|||
|
|
|||
|
//
|
|||
|
// An IRP could not be allocated. Cleanup and return an
|
|||
|
// appropriate error status code.
|
|||
|
//
|
|||
|
|
|||
|
IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = CurrentThread;
|
|||
|
irp->RequestorMode = requestorMode;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the service independent parameters in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
irp->UserEvent = (PKEVENT) NULL;
|
|||
|
} else {
|
|||
|
irp->UserEvent = &event;
|
|||
|
irp->Flags = IRP_SYNCHRONOUS_API;
|
|||
|
}
|
|||
|
irp->UserIosb = &localIoStatus;
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This will
|
|||
|
// be used to pass the original function codes and parameters.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Now determine whether this is a set or a query operation.
|
|||
|
//
|
|||
|
|
|||
|
if (OperationCode == QuerySecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// This is a query operation. Fill in the appropriate fields in
|
|||
|
// the stack location for the packet, as well as the fixed part
|
|||
|
// of the packet. Note that each of these parameters has been
|
|||
|
// captured as well, so there is no need to perform any probing.
|
|||
|
// The only exception is the UserBuffer memory may change, but
|
|||
|
// that is the file system's responsibility to check. Note that
|
|||
|
// it has already been probed, so the pointer is at least not
|
|||
|
// in an address space that the caller should not be accessing
|
|||
|
// because of mode.
|
|||
|
//
|
|||
|
|
|||
|
irpSp->MajorFunction = IRP_MJ_QUERY_SECURITY;
|
|||
|
irpSp->Parameters.QuerySecurity.SecurityInformation = *SecurityInformation;
|
|||
|
irpSp->Parameters.QuerySecurity.Length = *CapturedLength;
|
|||
|
irp->UserBuffer = SecurityDescriptor;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a set operation. Fill in the appropriate fields in
|
|||
|
// the stack location for the packet. Note that access to the
|
|||
|
// SecurityInformation parameter is safe, as the parameter was
|
|||
|
// captured by the caller. Likewise, the SecurityDescriptor
|
|||
|
// refers to a captured copy of the descriptor.
|
|||
|
//
|
|||
|
|
|||
|
irpSp->MajorFunction = IRP_MJ_SET_SECURITY;
|
|||
|
irpSp->Parameters.SetSecurity.SecurityInformation = *SecurityInformation;
|
|||
|
irpSp->Parameters.SetSecurity.SecurityDescriptor = SecurityDescriptor;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Insert the packet at the head of the IRP list for the thread.
|
|||
|
//
|
|||
|
|
|||
|
IopQueueThreadIrp( irp );
|
|||
|
|
|||
|
//
|
|||
|
// Update the operation count statistic for the current process for
|
|||
|
// operations other than read and write.
|
|||
|
//
|
|||
|
|
|||
|
IopUpdateOtherOperationCount();
|
|||
|
|
|||
|
//
|
|||
|
// Everything has been properly set up, so simply invoke the driver.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCallDriver( deviceObject, irp );
|
|||
|
|
|||
|
//
|
|||
|
// If this operation was a synchronous I/O operation, check the return
|
|||
|
// status to determine whether or not to wait on the file object. If
|
|||
|
// the file object is to be waited on, wait for the operation to be
|
|||
|
// completed and obtain the final status from the file object itself.
|
|||
|
//
|
|||
|
|
|||
|
if (synchronousIo) {
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject( &fileObject->Event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER) NULL );
|
|||
|
status = fileObject->FinalStatus;
|
|||
|
}
|
|||
|
IopReleaseFileObjectLock( fileObject );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a normal synchronous I/O operation, as opposed to a
|
|||
|
// serialized synchronous I/O operation. For this case, wait
|
|||
|
// for the local event and return the final status information
|
|||
|
// back to the caller.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
(VOID) KeWaitForSingleObject( &event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
(PLARGE_INTEGER) NULL );
|
|||
|
status = localIoStatus.Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this operation was just attempted on a file system or a device
|
|||
|
// driver of some kind that does not implement security, then return
|
|||
|
// a normal null security descriptor.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_INVALID_DEVICE_REQUEST) {
|
|||
|
|
|||
|
//
|
|||
|
// The file system does not implement a security policy. Determine
|
|||
|
// what type of operation this was and implement the correct
|
|||
|
// semantics for the file system.
|
|||
|
//
|
|||
|
|
|||
|
if (OperationCode == QuerySecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// The operation is a query. If the caller's buffer is too
|
|||
|
// small, then indicate that this is the case and let him know
|
|||
|
// what size buffer is required. Otherwise, attempt to return
|
|||
|
// a null security descriptor.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
status = SeAssignWorldSecurityDescriptor(
|
|||
|
SecurityDescriptor,
|
|||
|
CapturedLength,
|
|||
|
SecurityInformation
|
|||
|
);
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
//
|
|||
|
// An exception was incurred while attempting to
|
|||
|
// access the caller's buffer. Clean everything
|
|||
|
// up and return an appropriate status code.
|
|||
|
//
|
|||
|
|
|||
|
status = GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This was an operation other than a query. Simply indicate
|
|||
|
// that the operation was successful.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
} else if (OperationCode == QuerySecurityDescriptor) {
|
|||
|
|
|||
|
//
|
|||
|
// The final return status from the file system was something
|
|||
|
// other than invalid device request. This means that the file
|
|||
|
// system actually implemented the query. Copy the size of the
|
|||
|
// returned data, or the size of the buffer required in order
|
|||
|
// to query the security descriptor. Note that once again the
|
|||
|
// assignment is performed inside of an exception handler in case
|
|||
|
// the caller's buffer is inaccessible. Also note that in order
|
|||
|
// for the Information field of the I/O status block to be set,
|
|||
|
// the file system must return a warning status. Return the
|
|||
|
// status that the caller expects if the buffer really is too
|
|||
|
// small.
|
|||
|
//
|
|||
|
|
|||
|
if (status == STATUS_BUFFER_OVERFLOW) {
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
*CapturedLength = (ULONG) localIoStatus.Information;
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
status = GetExceptionCode();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|