From 71867f677948185a57d9a93932299cd16576c5d9 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Tue, 24 May 2016 21:41:08 -0700 Subject: [PATCH] sys, dll: IRP_MJ_SET_INFORMATION: perform access checks when replacing file during rename --- inc/winfsp/fsctl.h | 2 +- src/dll/debug.c | 8 ++--- src/dll/fsop.c | 62 ++++++++++++++++++++++++++++++++- src/sys/driver.c | 1 + src/sys/driver.h | 1 + src/sys/fileinfo.c | 86 ++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 152 insertions(+), 8 deletions(-) diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h index 7e7cdb36..874c29cb 100644 --- a/inc/winfsp/fsctl.h +++ b/inc/winfsp/fsctl.h @@ -258,7 +258,7 @@ typedef struct struct { FSP_FSCTL_TRANSACT_BUF NewFileName; - UINT32 ReplaceIfExists:1; + UINT64 AccessToken; /* request access token (HANDLE) */ } Rename; } Info; } SetInformation; diff --git a/src/dll/debug.c b/src/dll/debug.c index 162d1b0f..40eab253 100644 --- a/src/dll/debug.c +++ b/src/dll/debug.c @@ -194,7 +194,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request) &Sddl, 0); FspDebugLog("%S[TID=%04lx]: %p: >>Create [%c%c%c%c] \"%S\", " "%s, CreateOptions=%lx, FileAttributes=%lx, Security=%s%s%s, " - "AllocationSize=%lx:%lx, AccessToken=%lx, DesiredAccess=%lx, ShareAccess=%lx\n", + "AllocationSize=%lx:%lx, AccessToken=%p, DesiredAccess=%lx, ShareAccess=%lx\n", FspDiagIdent(), GetCurrentThreadId(), Request->Hint, Request->Req.Create.UserMode ? 'U' : 'K', Request->Req.Create.HasTraversePrivilege ? 'T' : '-', @@ -347,7 +347,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request) break; case 10/*FileRenameInformation*/: FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [Rename] %s%S%s%s, " - "NewFileName=\"%S\"%s\n", + "NewFileName=\"%S\", AccessToken=%p\n", FspDiagIdent(), GetCurrentThreadId(), Request->Hint, Request->FileName.Size ? "\"" : "", Request->FileName.Size ? (PWSTR)Request->Buffer : L"", @@ -356,7 +356,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request) Request->Req.SetInformation.UserContext, Request->Req.SetInformation.UserContext2, UserContextBuf), (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset), - Request->Req.SetInformation.Info.Rename.ReplaceIfExists ? " ReplaceIfExists" : ""); + Request->Req.SetInformation.Info.Rename.AccessToken); break; default: FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [INVALID] %s%S%s%s\n", @@ -452,7 +452,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request) DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, &Sddl, 0); FspDebugLog("%S[TID=%04lx]: %p: >>SetSecurity %s%S%s%s, " - "SecurityInformation=%lx, AccessToken=%lx, Security=%s%s%s\n", + "SecurityInformation=%lx, AccessToken=%p, Security=%s%s%s\n", FspDiagIdent(), GetCurrentThreadId(), Request->Hint, Request->FileName.Size ? "\"" : "", Request->FileName.Size ? (PWSTR)Request->Buffer : L"", diff --git a/src/dll/fsop.c b/src/dll/fsop.c index de5903ec..4e34a3f6 100644 --- a/src/dll/fsop.c +++ b/src/dll/fsop.c @@ -111,6 +111,56 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem, return Result; } +static inline +NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request) +{ + NTSTATUS Result; + FSP_FSCTL_TRANSACT_REQ *CreateRequest = 0; + UINT32 GrantedAccess; + + /* + * RenameCheck consists of checking the new file name for DELETE access. + * + * The following assumptions are being made here for a file that is going + * to be replaced: + * - The new file is in the same directory as the old one. In that case + * there is no need for traverse access checks as they have been already + * performed (if necessary) when opening the file under the existing file + * name. + * - The new file is in a different directory than the old one. In that case + * NTOS called us with SL_OPEN_TARGET_DIRECTORY and we performed any + * necessary traverse access checks at that time. + * + * FspAccessCheckEx only works on Create requests, so we have to build + * a fake one just for that purpose. Sigh! + */ + + CreateRequest = MemAlloc(sizeof *CreateRequest + + Request->Req.SetInformation.Info.Rename.NewFileName.Size); + if (0 == CreateRequest) + return STATUS_INSUFFICIENT_RESOURCES; + + memset(CreateRequest, 0, sizeof *CreateRequest); + CreateRequest->Size = sizeof CreateRequest + + Request->Req.SetInformation.Info.Rename.NewFileName.Size; + CreateRequest->Kind = FspFsctlTransactCreateKind; + CreateRequest->Req.Create.CreateOptions = FILE_DELETE_ON_CLOSE; /* force read-only check! */ + CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken; + CreateRequest->Req.Create.UserMode = TRUE; + CreateRequest->FileName.Offset = 0; + CreateRequest->FileName.Size = Request->Req.SetInformation.Info.Rename.NewFileName.Size; + memcpy(CreateRequest->Buffer, + Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset, + Request->Req.SetInformation.Info.Rename.NewFileName.Size); + + Result = FspAccessCheck(FileSystem, CreateRequest, FALSE, FALSE, DELETE, &GrantedAccess); + + MemFree(CreateRequest); + + return Result; +} + static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) { @@ -599,11 +649,21 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, break; case 10/*FileRenameInformation*/: if (0 != FileSystem->Interface->Rename) + { + if (0 != Request->Req.SetInformation.Info.Rename.AccessToken) + { + Result = FspFileSystemRenameCheck(FileSystem, Request); + if (!NT_SUCCESS(Result) && + STATUS_OBJECT_PATH_NOT_FOUND != Result && + STATUS_OBJECT_NAME_NOT_FOUND != Result) + break; + } Result = FileSystem->Interface->Rename(FileSystem, Request, (PVOID)Request->Req.SetInformation.UserContext, (PWSTR)Request->Buffer, (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset), - Request->Req.SetInformation.Info.Rename.ReplaceIfExists); + 0 != Request->Req.SetInformation.Info.Rename.AccessToken); + } break; } diff --git a/src/sys/driver.c b/src/sys/driver.c index 5bd5a19b..6c31d095 100644 --- a/src/sys/driver.c +++ b/src/sys/driver.c @@ -105,6 +105,7 @@ NTSTATUS DriverEntry( FspIopPrepareFunction[IRP_MJ_WRITE] = FspFsvolWritePrepare; FspIopCompleteFunction[IRP_MJ_WRITE] = FspFsvolWriteComplete; FspIopCompleteFunction[IRP_MJ_QUERY_INFORMATION] = FspFsvolQueryInformationComplete; + FspIopPrepareFunction[IRP_MJ_SET_INFORMATION] = FspFsvolSetInformationPrepare; FspIopCompleteFunction[IRP_MJ_SET_INFORMATION] = FspFsvolSetInformationComplete; FspIopCompleteFunction[IRP_MJ_QUERY_EA] = FspFsvolQueryEaComplete; FspIopCompleteFunction[IRP_MJ_SET_EA] = FspFsvolSetEaComplete; diff --git a/src/sys/driver.h b/src/sys/driver.h index 510f73e2..684a56de 100644 --- a/src/sys/driver.h +++ b/src/sys/driver.h @@ -316,6 +316,7 @@ FSP_IOCMPL_DISPATCH FspFsvolQueryVolumeInformationComplete; FSP_IOPREP_DISPATCH FspFsvolReadPrepare; FSP_IOCMPL_DISPATCH FspFsvolReadComplete; FSP_IOCMPL_DISPATCH FspFsvolSetEaComplete; +FSP_IOPREP_DISPATCH FspFsvolSetInformationPrepare; FSP_IOCMPL_DISPATCH FspFsvolSetInformationComplete; FSP_IOPREP_DISPATCH FspFsvolSetSecurityPrepare; FSP_IOCMPL_DISPATCH FspFsvolSetSecurityComplete; diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c index 10cffc10..b3816acb 100644 --- a/src/sys/fileinfo.c +++ b/src/sys/fileinfo.c @@ -63,6 +63,7 @@ static NTSTATUS FspFsvolSetRenameInformationSuccess( PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response); static NTSTATUS FspFsvolSetInformation( PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp); +FSP_IOPREP_DISPATCH FspFsvolSetInformationPrepare; FSP_IOCMPL_DISPATCH FspFsvolSetInformationComplete; static FSP_IOP_REQUEST_FINI FspFsvolSetInformationRequestFini; FSP_DRIVER_DISPATCH FspQueryInformation; @@ -89,6 +90,7 @@ FSP_DRIVER_DISPATCH FspSetInformation; #pragma alloc_text(PAGE, FspFsvolSetRenameInformation) #pragma alloc_text(PAGE, FspFsvolSetRenameInformationSuccess) #pragma alloc_text(PAGE, FspFsvolSetInformation) +#pragma alloc_text(PAGE, FspFsvolSetInformationPrepare) #pragma alloc_text(PAGE, FspFsvolSetInformationComplete) #pragma alloc_text(PAGE, FspFsvolSetInformationRequestFini) #pragma alloc_text(PAGE, FspQueryInformation) @@ -106,6 +108,9 @@ enum /* SetInformation */ //RequestFileNode = 0, RequestDeviceObject = 1, + /* Rename */ + RequestAccessToken = 2, + RequestProcess = 3, }; static NTSTATUS FspFsvolQueryAllInformation(PFILE_OBJECT FileObject, @@ -845,7 +850,6 @@ static NTSTATUS FspFsvolSetRenameInformation( PFILE_OBJECT TargetFileObject = IrpSp->Parameters.SetFile.FileObject; PFILE_RENAME_INFORMATION Info = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer; ULONG Length = IrpSp->Parameters.SetFile.Length; - BOOLEAN ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists; FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_DESC *FileDesc = FileObject->FsContext2; FSP_FILE_NODE *TargetFileNode = 0 != TargetFileObject ? @@ -922,7 +926,6 @@ static NTSTATUS FspFsvolSetRenameInformation( Request->Req.SetInformation.FileInformationClass = FileRenameInformation; Request->Req.SetInformation.Info.Rename.NewFileName.Offset = Request->FileName.Size; Request->Req.SetInformation.Info.Rename.NewFileName.Size = NewFileName.Length + sizeof(WCHAR); - Request->Req.SetInformation.Info.Rename.ReplaceIfExists = ReplaceIfExists; FspFsvolDeviceFileRenameSetOwner(FsvolDeviceObject, Request); FspFileNodeSetOwner(FileNode, Full, Request); @@ -1095,6 +1098,59 @@ static NTSTATUS FspFsvolSetInformation( return FSP_STATUS_IOQ_POST; } +NTSTATUS FspFsvolSetInformationPrepare( + PIRP Irp, FSP_FSCTL_TRANSACT_REQ *Request) +{ + PAGED_CODE(); + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + + if (FileRenameInformation != IrpSp->Parameters.SetFile.FileInformationClass || + !IrpSp->Parameters.SetFile.ReplaceIfExists) + return STATUS_SUCCESS; + + NTSTATUS Result; + SECURITY_SUBJECT_CONTEXT SecuritySubjectContext; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; + SECURITY_CLIENT_CONTEXT SecurityClientContext; + HANDLE UserModeAccessToken; + PEPROCESS Process; + + /* duplicate the subject context access token into an impersonation token */ + SecurityQualityOfService.Length = sizeof SecurityQualityOfService; + SecurityQualityOfService.ImpersonationLevel = SecurityIdentification; + SecurityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING; + SecurityQualityOfService.EffectiveOnly = FALSE; + SeCaptureSubjectContext(&SecuritySubjectContext); + SeLockSubjectContext(&SecuritySubjectContext); + Result = SeCreateClientSecurityFromSubjectContext(&SecuritySubjectContext, + &SecurityQualityOfService, FALSE, &SecurityClientContext); + SeUnlockSubjectContext(&SecuritySubjectContext); + SeReleaseSubjectContext(&SecuritySubjectContext); + if (!NT_SUCCESS(Result)) + return Result; + + ASSERT(TokenImpersonation == SeTokenType(SecurityClientContext.ClientToken)); + + /* get a user-mode handle to the impersonation token */ + Result = ObOpenObjectByPointer(SecurityClientContext.ClientToken, + 0, 0, TOKEN_QUERY, *SeTokenObjectType, UserMode, &UserModeAccessToken); + SeDeleteClientSecurity(&SecurityClientContext); + if (!NT_SUCCESS(Result)) + return Result; + + /* get a pointer to the current process so that we can close the impersonation token later */ + Process = PsGetCurrentProcess(); + ObReferenceObject(Process); + + /* send the user-mode handle to the user-mode file system */ + FspIopRequestContext(Request, RequestAccessToken) = UserModeAccessToken; + FspIopRequestContext(Request, RequestProcess) = Process; + Request->Req.SetInformation.Info.Rename.AccessToken = (UINT_PTR)UserModeAccessToken; + + return STATUS_SUCCESS; +} + NTSTATUS FspFsvolSetInformationComplete( PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response) { @@ -1154,12 +1210,38 @@ static VOID FspFsvolSetInformationRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P FSP_FILE_NODE *FileNode = Context[RequestFileNode]; PDEVICE_OBJECT FsvolDeviceObject = Context[RequestDeviceObject]; + HANDLE AccessToken = Context[RequestAccessToken]; + PEPROCESS Process = Context[RequestProcess]; if (0 != FileNode) FspFileNodeReleaseOwner(FileNode, Full, Request); if (0 != FsvolDeviceObject) FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, Request); + + if (0 != AccessToken) + { + KAPC_STATE ApcState; + BOOLEAN Attach; + + ASSERT(0 != Process); + Attach = Process != PsGetCurrentProcess(); + + if (Attach) + KeStackAttachProcess(Process, &ApcState); +#if DBG + NTSTATUS Result0; + Result0 = ObCloseHandle(AccessToken, UserMode); + if (!NT_SUCCESS(Result0)) + DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result0)); +#else + ObCloseHandle(AccessToken, UserMode); +#endif + if (Attach) + KeUnstackDetachProcess(&ApcState); + + ObDereferenceObject(Process); + } } NTSTATUS FspQueryInformation(