From 495fc7a5dc8017d46d5f72b8db99a5c024fe269a Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 27 Sep 2016 14:53:59 -0700 Subject: [PATCH] sys: implement named stream open/close and related delete/share access issues --- src/sys/cleanup.c | 9 ++++ src/sys/close.c | 2 +- src/sys/create.c | 58 +++++++++++++++++++++-- src/sys/driver.h | 17 ++++++- src/sys/file.c | 83 +++++++++++++++++++++++++++++---- src/sys/fileinfo.c | 7 +++ src/sys/util.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 274 insertions(+), 15 deletions(-) diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c index 83e621d6..af7b878e 100644 --- a/src/sys/cleanup.c +++ b/src/sys/cleanup.c @@ -167,12 +167,21 @@ static VOID FspFsvolCleanupRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Co PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; + FSP_FILE_DESC *FileDesc = FileObject->FsContext2; + HANDLE MainStreamHandle; + + ASSERT(FileNode == FileDesc->FileNode); FspFileNodeReleaseOwner(FileNode, Pgio, Request); FspFileNodeCleanupComplete(FileNode, FileObject); + MainStreamHandle = FileDesc->MainStreamHandle; + FileDesc->MainStreamHandle = 0; + FspFileNodeReleaseOwner(FileNode, Main, Request); + + FspMainStreamClose(MainStreamHandle, 0); } NTSTATUS FspCleanup( diff --git a/src/sys/close.c b/src/sys/close.c index db8d437b..5f35ba1b 100644 --- a/src/sys/close.c +++ b/src/sys/close.c @@ -77,7 +77,7 @@ static NTSTATUS FspFsvolClose( FspFileNodeClose(FileNode, FileObject); /* delete the FileDesc and deref the FileNode; order is important (FileDesc has FileNode ref) */ - FspFileDescDelete(FileDesc); + FspFileDescDelete(FileDesc); /* this will also close the MainStreamObject if any */ FspFileNodeDereference(FileNode); /* diff --git a/src/sys/create.c b/src/sys/create.c index 7840ca6c..4deebf6f 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -134,7 +134,6 @@ static NTSTATUS FspFsvolCreateNoLock( PFILE_OBJECT FileObject = IrpSp->FileObject; PFILE_OBJECT RelatedFileObject = FileObject->RelatedFileObject; UNICODE_STRING FileName = FileObject->FileName; - UNICODE_STRING MainStreamName = { 0 }, StreamPart = { 0 }; /* open the volume object? */ if ((0 == RelatedFileObject || !FspFileNodeIsValid(RelatedFileObject->FsContext)) && @@ -148,6 +147,7 @@ static NTSTATUS FspFsvolCreateNoLock( return STATUS_SUCCESS; } + UNICODE_STRING MainStreamName = { 0 }, StreamPart = { 0 }; PACCESS_STATE AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; ULONG CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0xff; ULONG CreateOptions = IrpSp->Parameters.Create.Options; @@ -163,6 +163,10 @@ static NTSTATUS FspFsvolCreateNoLock( ULONG Flags = IrpSp->Flags; KPROCESSOR_MODE RequestorMode = FlagOn(Flags, SL_FORCE_ACCESS_CHECK) ? UserMode : Irp->RequestorMode; + BOOLEAN CaseSensitiveRequested = + BooleanFlagOn(Flags, SL_CASE_SENSITIVE); + BOOLEAN CaseSensitive = + CaseSensitiveRequested || FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch; BOOLEAN HasTraversePrivilege = BooleanFlagOn(AccessState->Flags, TOKEN_HAS_TRAVERSE_PRIVILEGE); FSP_FILE_NODE *FileNode, *RelatedFileNode; @@ -347,6 +351,52 @@ static NTSTATUS FspFsvolCreateNoLock( return Result; } + /* if we have a non-empty stream part, open the main stream */ + if (0 != StreamPart.Buffer) + { + Result = FspMainStreamOpen(FsvolDeviceObject, + &MainStreamName, CaseSensitive, + CreateDisposition, + &FileDesc->MainStreamHandle, + &FileDesc->MainStreamObject); + if (!NT_SUCCESS(Result)) + goto main_stream_exit; + + /* check that the main stream is one we recognize */ + if (!FspFileNodeIsValid(FileDesc->MainStreamObject->FsContext)) + { + Result = STATUS_OBJECT_NAME_NOT_FOUND; + goto main_stream_exit; + } + + /* named streams can never be directories (even when attached to directories) */ + if (FlagOn(CreateOptions, FILE_DIRECTORY_FILE)) + { + Result = STATUS_NOT_A_DIRECTORY; + goto main_stream_exit; + } + + /* cannot open target directory of a named stream */ + if (FlagOn(Flags, SL_OPEN_TARGET_DIRECTORY)) + { + Result = STATUS_OBJECT_NAME_INVALID; + goto main_stream_exit; + } + + /* remember the the main stream node */ + FileNode->MainStreamFileNode = FileDesc->MainStreamObject->FsContext; + + Result = STATUS_SUCCESS; + + main_stream_exit: + if (!NT_SUCCESS(Result)) + { + FspFileDescDelete(FileDesc); + FspFileNodeDereference(FileNode); + return Result; + } + } + /* create the user-mode file system request */ Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, SecurityDescriptorSize, FspFsvolCreateRequestFini, &Request); @@ -369,9 +419,7 @@ static NTSTATUS FspFsvolCreateNoLock( * delete the Request and any associated resources. */ FileDesc->FileNode = FileNode; - FileDesc->CaseSensitive = - 0 != FsvolDeviceExtension->VolumeParams.CaseSensitiveSearch || - BooleanFlagOn(Flags, SL_CASE_SENSITIVE); + FileDesc->CaseSensitive = CaseSensitive; FileDesc->HasTraversePrivilege = HasTraversePrivilege; FspFsvolDeviceFileRenameSetOwner(FsvolDeviceObject, Request); FspIopRequestContext(Request, RequestDeviceObject) = FsvolDeviceObject; @@ -393,7 +441,7 @@ static NTSTATUS FspFsvolCreateNoLock( Request->Req.Create.UserMode = UserMode == RequestorMode; Request->Req.Create.HasTraversePrivilege = HasTraversePrivilege; Request->Req.Create.OpenTargetDirectory = BooleanFlagOn(Flags, SL_OPEN_TARGET_DIRECTORY); - Request->Req.Create.CaseSensitive = BooleanFlagOn(Flags, SL_CASE_SENSITIVE); + Request->Req.Create.CaseSensitive = CaseSensitiveRequested; /* copy the security descriptor (if any) into the request */ if (0 != SecurityDescriptorSize) diff --git a/src/sys/driver.h b/src/sys/driver.h index 05b21ce1..009d9d85 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -431,6 +431,15 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject); +NTSTATUS FspMainStreamOpen( + PDEVICE_OBJECT DeviceObject, + PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive, + ULONG Disposition, + PHANDLE PMainStreamHandle, + PFILE_OBJECT *PMainStreamObject); +NTSTATUS FspMainStreamClose( + HANDLE MainStreamHandle, + PFILE_OBJECT MainStreamObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation); @@ -896,7 +905,7 @@ typedef struct UINT64 DirInfo; UINT64 StreamInfo; } FSP_FILE_NODE_NONPAGED; -typedef struct +typedef struct FSP_FILE_NODE { FSRTL_ADVANCED_FCB_HEADER Header; FSP_FILE_NODE_NONPAGED *NonPaged; @@ -907,6 +916,8 @@ typedef struct LONG OpenCount; /* ContextTable ref count */ LONG HandleCount; /* HANDLE count (CREATE/CLEANUP) */ SHARE_ACCESS ShareAccess; + ULONG MainStreamDenyDeleteCount; /* number of times main stream is denying delete */ + ULONG StreamDenyDeleteCount; /* number of open streams that are denying delete */ FSP_DEVICE_CONTEXT_BY_NAME_TABLE_ELEMENT ContextByNameElementStorage; /* locked under FSP_FSVOL_DEVICE_EXTENSION::FileRenameResource or Header.Resource */ UNICODE_STRING FileName; @@ -941,6 +952,7 @@ typedef struct UINT64 IndexNumber; BOOLEAN IsDirectory; BOOLEAN IsRootDirectory; + struct FSP_FILE_NODE *MainStreamFileNode; /* this becomes invalid after our last desc close */ WCHAR FileNameBuf[]; } FSP_FILE_NODE; typedef struct @@ -955,6 +967,9 @@ typedef struct UINT64 DirectoryOffset; UINT64 DirInfo; ULONG DirInfoCacheHint; + /* stream support */ + HANDLE MainStreamHandle; + PFILE_OBJECT MainStreamObject; } FSP_FILE_DESC; NTSTATUS FspFileNodeCopyList(PDEVICE_OBJECT DeviceObject, FSP_FILE_NODE ***PFileNodes, PULONG PFileNodeCount); diff --git a/src/sys/file.c b/src/sys/file.c index 29bdf0f9..1b17b408 100644 --- a/src/sys/file.c +++ b/src/sys/file.c @@ -420,12 +420,37 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PAGED_CODE(); PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; - FSP_FILE_NODE *OpenedFileNode; + FSP_FILE_NODE *OpenedFileNode = 0; BOOLEAN Inserted, DeletePending; NTSTATUS Result; FspFsvolDeviceLockContextTable(FsvolDeviceObject); + /* + * If this is a named stream we must also check with our main stream. + * Note that FileNode->MainStreamFileNode and OpenedFileNode->MainStreamFileNode + * will always be the same. + */ + if (0 != FileNode->MainStreamFileNode) + { + DeletePending = 0 != FileNode->MainStreamFileNode->DeletePending; + MemoryBarrier(); + if (DeletePending) + { + Result = STATUS_DELETE_PENDING; + goto exit; + } + + if (0 < FileNode->MainStreamFileNode->MainStreamDenyDeleteCount) + { + if (FlagOn(GrantedAccess, DELETE)) + { + Result = STATUS_SHARING_VIOLATION; + goto exit; + } + } + } + OpenedFileNode = FspFsvolDeviceInsertContextByName(FsvolDeviceObject, &FileNode->FileName, FileNode, &FileNode->ContextByNameElementStorage, &Inserted); ASSERT(0 != OpenedFileNode); @@ -449,6 +474,7 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, * opening a prior FileNode that we found in the table. */ ASSERT(OpenedFileNode != FileNode); + ASSERT(OpenedFileNode->MainStreamFileNode == FileNode->MainStreamFileNode); DeletePending = 0 != OpenedFileNode->DeletePending; MemoryBarrier(); @@ -458,6 +484,19 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, goto exit; } + /* if this is a main stream check whether there is a named stream that denies delete */ + if (0 < OpenedFileNode->StreamDenyDeleteCount) + { + /* we must be the main stream! */ + ASSERT(0 == OpenedFileNode->MainStreamFileNode); + + if (FlagOn(GrantedAccess, DELETE)) + { + Result = STATUS_SHARING_VIOLATION; + goto exit; + } + } + /* * FastFat says to do the following on Vista and above. * @@ -478,15 +517,33 @@ FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, /* share access check */ Result = IoCheckShareAccess(GrantedAccess, ShareAccess, FileObject, &OpenedFileNode->ShareAccess, TRUE); - - exit: if (!NT_SUCCESS(Result)) - { - if (0 != PResult) - *PResult = Result; + goto exit; + } - OpenedFileNode = 0; - } + /* + * No more failures allowed at this point! + * This is because we have potentially inserted a new FileNode into the Context table. + * We also updated OpenedFileNode->ShareAccess in IoSetShareAccess/IoCheckShareAccess. + */ + + if (FileObject->DeleteAccess && !FileObject->SharedDelete) + { + if (0 == OpenedFileNode->MainStreamFileNode) + OpenedFileNode->MainStreamDenyDeleteCount++; + else + OpenedFileNode->MainStreamFileNode->StreamDenyDeleteCount++; + } + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + { + if (0 != PResult) + *PResult = Result; + + OpenedFileNode = 0; } if (0 != OpenedFileNode) @@ -556,6 +613,14 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject FspFsvolDeviceLockContextTable(FsvolDeviceObject); + if (FileObject->DeleteAccess && !FileObject->SharedDelete) + { + if (0 == FileNode->MainStreamFileNode) + FileNode->MainStreamDenyDeleteCount--; + else + FileNode->MainStreamFileNode->StreamDenyDeleteCount--; + } + IoRemoveShareAccess(FileObject, &FileNode->ShareAccess); if (0 == --FileNode->HandleCount) @@ -1088,6 +1153,8 @@ VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc) { PAGED_CODE(); + FspMainStreamClose(FileDesc->MainStreamHandle, FileDesc->MainStreamObject); + if (0 != FileDesc->DirectoryPattern.Buffer && FspFileDescDirectoryPatternMatchAll != FileDesc->DirectoryPattern.Buffer) { diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 082dba14..c4301209 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -449,6 +449,9 @@ static NTSTATUS FspFsvolQueryStreamInformation( { PAGED_CODE(); + if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.NamedStreams) + return STATUS_INVALID_PARAMETER; + NTSTATUS Result; PFILE_OBJECT FileObject = IrpSp->FileObject; FSP_FILE_NODE *FileNode = FileObject->FsContext; @@ -499,6 +502,10 @@ static NTSTATUS FspFsvolQueryStreamInformationSuccess( { PAGED_CODE(); + if (!FspFsvolDeviceExtension(IoGetCurrentIrpStackLocation(Irp)->DeviceObject)-> + VolumeParams.NamedStreams) + return STATUS_INVALID_PARAMETER; + NTSTATUS Result; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PFILE_OBJECT FileObject = IrpSp->FileObject; diff --git a/src/sys/util.c b/src/sys/util.c index 6cffe1d0..28a1a52a 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -23,6 +23,15 @@ VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE NTSTATUS FspCreateGuid(GUID *Guid); NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject); +NTSTATUS FspMainStreamOpen( + PDEVICE_OBJECT DeviceObject, + PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive, + ULONG Disposition, + PHANDLE PMainStreamHandle, + PFILE_OBJECT *PMainStreamObject); +NTSTATUS FspMainStreamClose( + HANDLE MainStreamHandle, + PFILE_OBJECT MainStreamObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); static NTSTATUS FspSendSetInformationIrpCompletion( @@ -93,6 +102,8 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #pragma alloc_text(PAGE, FspUnicodePathSuffix) #pragma alloc_text(PAGE, FspCreateGuid) #pragma alloc_text(PAGE, FspGetDeviceObjectPointer) +#pragma alloc_text(PAGE, FspMainStreamOpen) +#pragma alloc_text(PAGE, FspMainStreamClose) #pragma alloc_text(PAGE, FspSendSetInformationIrp) #pragma alloc_text(PAGE, FspBufferUserBuffer) #pragma alloc_text(PAGE, FspLockUserBuffer) @@ -373,6 +384,108 @@ NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK Desir return Result; } +NTSTATUS FspMainStreamOpen( + PDEVICE_OBJECT DeviceObject, + PUNICODE_STRING MainStreamName, BOOLEAN CaseSensitive, + ULONG Disposition, + PHANDLE PMainStreamHandle, + PFILE_OBJECT *PMainStreamObject) +{ + PAGED_CODE(); + + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatus; + HANDLE MainStreamHandle; + PFILE_OBJECT MainStreamObject; + + /* assert that the supplied name is actually a main stream name */ + ASSERT(FspUnicodePathIsValid(MainStreamName, 0)); + + *PMainStreamHandle = 0; + *PMainStreamObject = 0; + + switch (Disposition) + { + case FILE_CREATE: + case FILE_OPEN_IF: + case FILE_OVERWRITE_IF: + Disposition = FILE_OPEN_IF; + break; + case FILE_OPEN: + case FILE_OVERWRITE: + case FILE_SUPERSEDE: + Disposition = FILE_OPEN; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + InitializeObjectAttributes( + &ObjectAttributes, + MainStreamName, + OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK | (CaseSensitive ? 0 : OBJ_CASE_INSENSITIVE), + 0/*RootDirectory*/, + 0/*SecurityDescriptor*/); + + IoStatus.Status = IoCreateFileSpecifyDeviceObjectHint( + &MainStreamHandle, + FILE_READ_ATTRIBUTES, + &ObjectAttributes, + &IoStatus, + 0/*AllocationSize*/, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + Disposition, + FILE_OPEN_REPARSE_POINT, + 0/*EaBuffer*/, + 0/*EaLength*/, + CreateFileTypeNone, + 0/*InternalParameters*/, + IO_FORCE_ACCESS_CHECK, + DeviceObject); + if (!NT_SUCCESS(IoStatus.Status)) + return IoStatus.Status; + + IoStatus.Status = ObReferenceObjectByHandle( + MainStreamHandle, + 0/*DesiredAccess*/, + *IoFileObjectType, + KernelMode, + &MainStreamObject, + 0/*HandleInformation*/); + if (!NT_SUCCESS(IoStatus.Status)) + { + ObCloseHandle(MainStreamHandle, KernelMode); + return IoStatus.Status; + } + + *PMainStreamHandle = MainStreamHandle; + *PMainStreamObject = MainStreamObject; + + return STATUS_SUCCESS; +} + +NTSTATUS FspMainStreamClose( + HANDLE MainStreamHandle, + PFILE_OBJECT MainStreamObject) +{ + PAGED_CODE(); + + NTSTATUS Result = STATUS_SUCCESS; + + if (0 != MainStreamObject) + ObDereferenceObject(MainStreamObject); + + if (0 != MainStreamHandle) + { + Result = ObCloseHandle(MainStreamHandle, KernelMode); + if (!NT_SUCCESS(Result)) + DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result)); + } + + return Result; +} + typedef struct { IO_STATUS_BLOCK IoStatus;