From af2cc10c158d75c30affe5951ca9c3f9971fbe12 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 10 Sep 2016 23:00:55 -0700 Subject: [PATCH] sys,dll: reparse point testing --- inc/winfsp/fsctl.h | 2 +- src/dll/fsop.c | 73 +++++++++++++------------------ src/dll/fuse/fuse_intf.c | 19 +++------ src/sys/create.c | 92 +++++++++++++++++++++++++++------------- src/sys/driver.h | 2 + src/sys/fsctl.c | 41 ++++++++---------- src/sys/util.c | 52 +++++++++++++++++++++++ tst/memfs/memfs.cpp | 2 +- 8 files changed, 171 insertions(+), 112 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index d54c7fcc..95f9efd1 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -298,7 +298,7 @@ typedef struct UINT64 UserContext2; UINT32 FsControlCode; FSP_FSCTL_TRANSACT_BUF Buffer; - UINT32 TargetOnFileSystem:1;/* the target of the symbolic link is on this file system */ + UINT16 TargetOnFileSystem; /* the target of the symbolic link is on this file system */ } FileSystemControl; struct { diff --git a/src/dll/fsop.c b/src/dll/fsop.c index f3e11cf0..92997925 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -1060,21 +1060,21 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) { WCHAR c, *p, *lastp; - PWSTR TargetPath, TargetPathEnd, TargetLink; + PWSTR TargetPath, TargetPathEnd, ReparseTargetPath; union { REPARSE_DATA_BUFFER V; UINT8 B[FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX]; } ReparseDataBuf; PREPARSE_DATA_BUFFER ReparseData = &ReparseDataBuf.V; - SIZE_T Size, MaxTries = 32; + SIZE_T Size, ReparseTargetPathLength, MaxTries = 32; NTSTATUS Result; TargetPath = Buffer; TargetPathEnd = TargetPath + *PSize / sizeof(WCHAR); Size = (lstrlenW(FileName) + 1) * sizeof(WCHAR); if (Size > *PSize) - return STATUS_OBJECT_NAME_INVALID; + return STATUS_REPARSE_POINT_NOT_RESOLVED; memcpy(TargetPath, FileName, Size); p = TargetPath + ReparsePointIndex; @@ -1143,59 +1143,44 @@ FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, /* found a reparse point */ + if (IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag) + /* not a symlink; return the full reparse point! */ + goto reparse_data_exit; + + ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); + ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; + + if (ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0]) + /* absolute symlink; return the full reparse point! */ + goto reparse_data_exit; + if (0 == --MaxTries) return STATUS_REPARSE_POINT_NOT_RESOLVED; - if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag) - { - TargetLink = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + - ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); - Size = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; - } - else - { - /* not a symlink; return the full reparse point! */ - if (Size > *PSize) - return STATUS_IO_REPARSE_DATA_INVALID; - memcpy(Buffer, ReparseData, Size); - - goto no_symlink_exit; - } - - if (Size > 4 * sizeof(WCHAR) && - '\\' == TargetLink[0] && - '?' == TargetLink[1] && - '?' == TargetLink[2] && - '\\' == TargetLink[3]) - { - /* absolute symlink; replace whole path */ - if (Size > *PSize) - return STATUS_OBJECT_NAME_INVALID; - memcpy(TargetPath, TargetLink, Size); - p = TargetPath + Size / sizeof(WCHAR); - - /* absolute symlinks on Windows are in NT namespace; send to FSD for further processing */ - goto exit; - } - else - { - /* relative symlink; replace last path component seen */ - if (Size + sizeof(WCHAR) > (TargetPathEnd - lastp) * sizeof(WCHAR)) - return STATUS_OBJECT_NAME_INVALID; - memcpy(lastp, TargetLink, Size); - lastp[Size / sizeof(WCHAR)] = L'\0'; - p = lastp; - } + /* relative symlink; replace last path component seen */ + if (ReparseTargetPathLength + sizeof(WCHAR) > (TargetPathEnd - lastp) * sizeof(WCHAR)) + return STATUS_REPARSE_POINT_NOT_RESOLVED; + memcpy(lastp, ReparseTargetPath, ReparseTargetPathLength); + lastp[ReparseTargetPathLength / sizeof(WCHAR)] = L'\0'; + p = lastp; } exit: *PSize = (PUINT8)p - (PUINT8)TargetPath; + PIoStatus->Status = STATUS_REPARSE; PIoStatus->Information = 0/*IO_REPARSE*/; return STATUS_REPARSE; -no_symlink_exit: +reparse_data_exit: + if (Size > *PSize) + return IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag ? + STATUS_IO_REPARSE_DATA_INVALID : STATUS_REPARSE_POINT_NOT_RESOLVED; + *PSize = Size; + memcpy(Buffer, ReparseData, Size); + PIoStatus->Status = STATUS_REPARSE; PIoStatus->Information = ReparseData->ReparseTag; return STATUS_REPARSE; diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index e757a017..cc865a09 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -2005,27 +2005,20 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; /* is this an absolute path? */ - if (ReparseTargetPathLength > 4 * sizeof(WCHAR) && - '\\' == ReparseTargetPath[0] && - '?' == ReparseTargetPath[1] && - '?' == ReparseTargetPath[2] && - '\\' == ReparseTargetPath[3]) + if (ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0]) { /* we do not support absolute paths that point outside this file system */ - if (!Request->Req.FileSystemControl.TargetOnFileSystem) + if (0 == Request->Req.FileSystemControl.TargetOnFileSystem) return STATUS_ACCESS_DENIED; - } - /* the first path component is assumed to be the device name; skip it! */ - ReparseTargetPath += 4; - ReparseTargetPathLength -= 4 * sizeof(WCHAR); - while (0 < ReparseTargetPathLength && L'\\' != *ReparseTargetPath) - ReparseTargetPathLength--, ReparseTargetPath++; + ReparseTargetPath += Request->Req.FileSystemControl.TargetOnFileSystem / sizeof(WCHAR); + ReparseTargetPathLength -= Request->Req.FileSystemControl.TargetOnFileSystem; + } } else { ReparseTargetPath = (PVOID)(ReparseData->GenericReparseBuffer.DataBuffer + 8); - ReparseTargetPathLength = ReparseData->ReparseDataLength - sizeof(UINT64); + ReparseTargetPathLength = ReparseData->ReparseDataLength - 8; } memcpy(TargetPath, ReparseTargetPath, ReparseTargetPathLength); diff --git a/src/sys/create.c b/src/sys/create.c index 1f9f1a23..269f115f 100644 --- a/src/sys/create.c +++ b/src/sys/create.c @@ -486,8 +486,8 @@ NTSTATUS FspFsvolCreateComplete( FSP_FILE_DESC *FileDesc = FspIopRequestContext(Request, RequestFileDesc); FSP_FILE_NODE *FileNode = FileDesc->FileNode; FSP_FILE_NODE *OpenedFileNode; - UNICODE_STRING ReparseFileName; PREPARSE_DATA_BUFFER ReparseData; + UNICODE_STRING ReparseTargetPrefix, ReparseTargetPath; if (FspFsctlTransactCreateKind == Request->Kind) { @@ -505,32 +505,74 @@ NTSTATUS FspFsvolCreateComplete( if (IO_REMOUNT == Response->IoStatus.Information) { Irp->IoStatus.Information = IO_REMOUNT; - Result = STATUS_REPARSE; - FSP_RETURN(); + FSP_RETURN(Result = STATUS_REPARSE); } - else if (IO_REPARSE == Response->IoStatus.Information) + else + if (IO_REPARSE == Response->IoStatus.Information || + IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information) { - ReparseFileName.Buffer = - (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset); - ReparseFileName.Length = ReparseFileName.MaximumLength = - Response->Rsp.Create.Reparse.FileName.Size; + /* + * IO_REPARSE means that the user-mode file system has returned a device-relative + * path. Prefix it with our device name and send it to the IO Manager. + * + * IO_REPARSE_TAG_SYMLINK means that the user-mode file system has returned a full + * symbolic link reparse buffer. In this case send the target path to the IO Manager + * without prefixing it with our device name as it is expected to be absolute in the + * NT namespace. + */ - if ((PUINT8)ReparseFileName.Buffer + ReparseFileName.Length > - (PUINT8)Response + Response->Size || - 0 == ReparseFileName.Length) - FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); - - if (ReparseFileName.Length > FileObject->FileName.MaximumLength) + if (IO_REPARSE == Response->IoStatus.Information) { - PVOID Buffer = FspAllocExternal(ReparseFileName.Length); + RtlCopyMemory(&ReparseTargetPrefix, &FsvolDeviceExtension->VolumeName, + sizeof ReparseTargetPrefix); + + ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = + Response->Rsp.Create.Reparse.FileName.Size; + ReparseTargetPath.Buffer = + (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset); + + if ((PUINT8)ReparseTargetPath.Buffer + ReparseTargetPath.Length > + (PUINT8)Response + Response->Size || 0 == ReparseTargetPath.Length) + FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED); + } + else + { + ASSERT(IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information); + + ReparseData = (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.Data.Offset); + + if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Data.Size > + (PUINT8)Response + Response->Size) + FSP_RETURN(Result = STATUS_IO_REPARSE_DATA_INVALID); + + Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Data.Size, + ReparseData); + if (!NT_SUCCESS(Result)) + FSP_RETURN(); + + RtlZeroMemory(&ReparseTargetPrefix, sizeof ReparseTargetPrefix); + + ReparseTargetPath.Buffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); + ReparseTargetPath.Length = ReparseTargetPath.MaximumLength = + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; + } + + if (ReparseTargetPrefix.Length + ReparseTargetPath.Length > + FileObject->FileName.MaximumLength) + { + PVOID Buffer = FspAllocExternal( + ReparseTargetPrefix.Length + ReparseTargetPath.Length); if (0 == Buffer) FSP_RETURN(Result = STATUS_INSUFFICIENT_RESOURCES); FspFreeExternal(FileObject->FileName.Buffer); - FileObject->FileName.MaximumLength = ReparseFileName.Length; + FileObject->FileName.MaximumLength = + ReparseTargetPrefix.Length + ReparseTargetPath.Length; FileObject->FileName.Buffer = Buffer; } FileObject->FileName.Length = 0; - RtlCopyUnicodeString(&FileObject->FileName, &ReparseFileName); + RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix); + RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPath); /* * The RelatedFileObject does not need to be changed according to: @@ -548,8 +590,7 @@ NTSTATUS FspFsvolCreateComplete( */ Irp->IoStatus.Information = IO_REPARSE; - Result = STATUS_REPARSE; - FSP_RETURN(); + FSP_RETURN(Result = STATUS_REPARSE); } else { @@ -557,10 +598,7 @@ NTSTATUS FspFsvolCreateComplete( if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Data.Size > (PUINT8)Response + Response->Size) - { - Result = STATUS_IO_REPARSE_DATA_INVALID; - FSP_RETURN(); - } + FSP_RETURN(Result = STATUS_IO_REPARSE_DATA_INVALID); Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Data.Size, ReparseData); @@ -571,17 +609,13 @@ NTSTATUS FspFsvolCreateComplete( Irp->Tail.Overlay.AuxiliaryBuffer = FspAllocNonPagedExternal( Response->Rsp.Create.Reparse.Data.Size); if (0 == Irp->Tail.Overlay.AuxiliaryBuffer) - { - Result = STATUS_INSUFFICIENT_RESOURCES; - FSP_RETURN(); - } + FSP_RETURN(Result = STATUS_INSUFFICIENT_RESOURCES); RtlCopyMemory(Irp->Tail.Overlay.AuxiliaryBuffer, ReparseData, Response->Rsp.Create.Reparse.Data.Size); Irp->IoStatus.Information = ReparseData->ReparseTag; - Result = STATUS_REPARSE; - FSP_RETURN(); + FSP_RETURN(Result = STATUS_REPARSE); } } diff --git a/src/sys/driver.h b/src/sys/driver.h index c5941ffc..0a9da600 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -427,6 +427,8 @@ PVOID FspAllocateIrpMustSucceed(CCHAR StackSize); BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams); VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspCreateGuid(GUID *Guid); +NTSTATUS FspGetDeviceObjectByName(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, + PULONG PFileNameIndex, PDEVICE_OBJECT *PDeviceObject); 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); diff --git a/src/sys/fsctl.c b/src/sys/fsctl.c index 62da25ec..a13a30b2 100644 --- a/src/sys/fsctl.c +++ b/src/sys/fsctl.c @@ -109,8 +109,9 @@ static NTSTATUS FspFsvolFileSystemControlReparsePoint( ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; PREPARSE_DATA_BUFFER ReparseData; - PWSTR PathBuffer; - BOOLEAN TargetOnFileSystem = FALSE; + PWSTR ReparseTargetPath; + USHORT ReparseTargetPathLength; + UINT16 TargetOnFileSystem = 0; FSP_FSCTL_TRANSACT_REQ *Request; ASSERT(FileNode == FileDesc->FileNode); @@ -139,36 +140,28 @@ static NTSTATUS FspFsvolFileSystemControlReparsePoint( return STATUS_PRIVILEGE_NOT_HELD; } - /* determine if target resides on same device as link (convenience for user mode) */ - PathBuffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + + ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); - if (ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength > 4 * sizeof(WCHAR) && - '\\' == PathBuffer[0] && - '?' == PathBuffer[1] && - '?' == PathBuffer[2] && - '\\' == PathBuffer[3]) + ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength; + + /* is this an absolute path? determine if target resides on same device as link */ + if (ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0]) { - UNICODE_STRING TargetDeviceName; - PFILE_OBJECT TargetDeviceFile; + UNICODE_STRING TargetObjectName; PDEVICE_OBJECT TargetDeviceObject; + ULONG TargetFileNameIndex; - RtlInitEmptyUnicodeString(&TargetDeviceName, - PathBuffer, - ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength); + TargetObjectName.Length = TargetObjectName.MaximumLength = ReparseTargetPathLength; + TargetObjectName.Buffer = ReparseTargetPath; - /* the first path component is assumed to be the device name */ - TargetDeviceName.Length = 4 * sizeof(WCHAR); - while (TargetDeviceName.Length < TargetDeviceName.MaximumLength && - '\\' != TargetDeviceName.Buffer[TargetDeviceName.Length / sizeof(WCHAR)]) - TargetDeviceName.Length += sizeof(WCHAR); - - Result = IoGetDeviceObjectPointer(&TargetDeviceName, - FILE_READ_ATTRIBUTES, &TargetDeviceFile, &TargetDeviceObject); + Result = FspGetDeviceObjectByName(&TargetObjectName, FILE_READ_DATA, + &TargetFileNameIndex, &TargetDeviceObject); if (!NT_SUCCESS(Result)) goto target_check_exit; - TargetOnFileSystem = IoGetRelatedDeviceObject(FileObject) == TargetDeviceObject; - ObDereferenceObject(TargetDeviceFile); + TargetOnFileSystem = IoGetRelatedDeviceObject(FileObject) == TargetDeviceObject ? + (UINT16)TargetFileNameIndex : 0; + ObDereferenceObject(TargetDeviceObject); target_check_exit: ; diff --git a/src/sys/util.c b/src/sys/util.c index 8b2945ce..93c9e05b 100644 --- a/src/sys/util.c +++ b/src/sys/util.c @@ -20,6 +20,8 @@ BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams); VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix); NTSTATUS FspCreateGuid(GUID *Guid); +NTSTATUS FspGetDeviceObjectByName(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, + PULONG PFileNameIndex, PDEVICE_OBJECT *PDeviceObject); NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length); static NTSTATUS FspSendSetInformationIrpCompletion( @@ -88,6 +90,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); #pragma alloc_text(PAGE, FspUnicodePathIsValid) #pragma alloc_text(PAGE, FspUnicodePathSuffix) #pragma alloc_text(PAGE, FspCreateGuid) +#pragma alloc_text(PAGE, FspGetDeviceObjectByName) #pragma alloc_text(PAGE, FspSendSetInformationIrp) #pragma alloc_text(PAGE, FspBufferUserBuffer) #pragma alloc_text(PAGE, FspLockUserBuffer) @@ -243,6 +246,55 @@ NTSTATUS FspCreateGuid(GUID *Guid) return Result; } +NTSTATUS FspGetDeviceObjectByName(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess, + PULONG PFileNameIndex, PDEVICE_OBJECT *PDeviceObject) +{ + UNICODE_STRING PartialName; + PFILE_OBJECT FileObject; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE Handle; + NTSTATUS Result; + + PartialName.Length = 0; + PartialName.MaximumLength = ObjectName->Length; + PartialName.Buffer = ObjectName->Buffer; + + Result = STATUS_NO_SUCH_DEVICE; + while (PartialName.MaximumLength > PartialName.Length) + { + while (PartialName.MaximumLength > PartialName.Length && + L'\\' == PartialName.Buffer[PartialName.Length / sizeof(WCHAR)]) + PartialName.Length += sizeof(WCHAR); + while (PartialName.MaximumLength > PartialName.Length && + L'\\' != PartialName.Buffer[PartialName.Length / sizeof(WCHAR)]) + PartialName.Length += sizeof(WCHAR); + + Result = IoGetDeviceObjectPointer(&PartialName, DesiredAccess, &FileObject, PDeviceObject); + if (NT_SUCCESS(Result)) + { + *PFileNameIndex = PartialName.Length; + ObReferenceObject(*PDeviceObject); + ObDereferenceObject(FileObject); + break; + } + + InitializeObjectAttributes(&ObjectAttributes, &PartialName, OBJ_KERNEL_HANDLE, 0, 0); + Result = ZwOpenDirectoryObject(&Handle, 0, &ObjectAttributes); + if (!NT_SUCCESS(Result)) + { + Result = ZwOpenSymbolicLinkObject(&Handle, 0, &ObjectAttributes); + if (!NT_SUCCESS(Result)) + { + Result = STATUS_NO_SUCH_DEVICE; + break; + } + } + ZwClose(Handle); + } + + return Result; +} + typedef struct { IO_STATUS_BLOCK IoStatus; diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp index 092a23f3..3192a4ff 100644 --- a/tst/memfs/memfs.cpp +++ b/tst/memfs/memfs.cpp @@ -1153,7 +1153,7 @@ NTSTATUS MemfsCreate( VolumeParams.UnicodeOnDisk = 1; VolumeParams.PersistentAcls = 1; VolumeParams.ReparsePoints = 1; - VolumeParams.ReparsePointsAccessCheck = 1; + VolumeParams.ReparsePointsAccessCheck = 0; if (0 != VolumePrefix) wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);