From 9d5efe5f988e92d63564bbbec1aadaa38d5e0275 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 25 Jan 2021 16:54:59 -0800 Subject: [PATCH] dll: fuse: FileSecurity option --- inc/winfsp/winfsp.h | 4 ++ src/dll/fuse/fuse.c | 53 +++++++++++++++++- src/dll/fuse/fuse_intf.c | 10 ++-- src/dll/fuse/library.h | 4 ++ src/dll/security.c | 3 +- src/shared/ku/posix.c | 100 ++++++++++++++++++++++++++++++++++ tst/winfsp-tests/posix-test.c | 93 ++++++++++++++++++++++++++++++- 7 files changed, 257 insertions(+), 10 deletions(-) diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 06f77227..b1952142 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1826,6 +1826,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)()); FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor( UINT32 Uid, UINT32 Gid, UINT32 Mode, PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions( PSECURITY_DESCRIPTOR SecurityDescriptor, PUINT32 PUid, PUINT32 PGid, PUINT32 PMode); diff --git a/src/dll/fuse/fuse.c b/src/dll/fuse/fuse.c index 1414b6a6..678a3f37 100644 --- a/src/dll/fuse/fuse.c +++ b/src/dll/fuse/fuse.c @@ -20,6 +20,7 @@ */ #include +#include struct fuse_chan { @@ -105,6 +106,8 @@ static struct fuse_opt fsp_fuse_core_opts[] = FUSE_OPT_KEY("ExactFileSystemName=", 'E'), FUSE_OPT_KEY("--ExactFileSystemName=", 'E'), + FUSE_OPT_KEY("FileSecurity=", 's'), + FUSE_OPT_KEY("--FileSecurity=", 's'), FSP_FUSE_CORE_OPT("UserName=", set_uid, 1), FUSE_OPT_KEY("UserName=", 'u'), FSP_FUSE_CORE_OPT("--UserName=", set_uid, 1), @@ -266,6 +269,29 @@ FSP_FUSE_API int fsp_fuse_is_lib_option(struct fsp_fuse_env *env, return fsp_fuse_opt_match(env, fsp_fuse_core_opts, opt); } +static int fsp_fuse_sddl_to_security(const char *Sddl, PUINT8 Security, PULONG PSecuritySize) +{ + PSECURITY_DESCRIPTOR SecurityDescriptor; + ULONG SecurityDescriptorSize; + int res = -1; + + if (ConvertStringSecurityDescriptorToSecurityDescriptorA( + Sddl, SDDL_REVISION_1, &SecurityDescriptor, &SecurityDescriptorSize)) + { + if (*PSecuritySize >= SecurityDescriptorSize) + { + memcpy(Security, SecurityDescriptor, SecurityDescriptorSize); + *PSecuritySize = SecurityDescriptorSize; + + res = 0; + } + + LocalFree(SecurityDescriptor); + } + + return res; +} + static int fsp_fuse_username_to_uid(const char *username, int *puid) { union @@ -374,6 +400,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key, FspServiceLog(EVENTLOG_ERROR_TYPE, L"" FSP_FUSE_LIBRARY_NAME " options:\n" " -o umask=MASK set file permissions (octal)\n" + " -o FileSecurity=SDDL set file DACL (SDDL format)\n" " -o create_umask=MASK set newly created file permissions (octal)\n" " -o create_file_umask=MASK for files only\n" " -o create_dir_umask=MASK for directories only\n" @@ -460,6 +487,18 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key, opt_data->VolumeParams.FileSystemName [sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0'; return 0; + case 's': + if ('F' == arg[0]) + arg += sizeof "FileSecurity=" - 1; + else if ('F' == arg[2]) + arg += sizeof "--FileSecurity=" - 1; + opt_data->FileSecuritySize = sizeof opt_data->FileSecurityBuf; + if (-1 == fsp_fuse_sddl_to_security(arg, opt_data->FileSecurityBuf, &opt_data->FileSecuritySize)) + { + opt_data->FileSecuritySize = (ULONG)-1; + return -1; + } + return 0; case 'u': if ('U' == arg[0]) arg += sizeof "UserName=" - 1; @@ -527,7 +566,12 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, if (-1 == fsp_fuse_core_opt_parse(env, args, &opt_data, /*help=*/1)) { - if (-1 == opt_data.username_to_uid_result) + if ((ULONG)-1 == opt_data.FileSecuritySize) + { + ErrorMessage = L": invalid file security."; + goto fail; + } + else if (-1 == opt_data.username_to_uid_result) { ErrorMessage = L": invalid user or group name."; goto fail; @@ -596,7 +640,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, if (L'\0' == opt_data.VolumeParams.FileSystemName[0]) memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR)); - f = fsp_fuse_obj_alloc(env, sizeof *f); + f = fsp_fuse_obj_alloc(env, sizeof *f + opt_data.FileSecuritySize); if (0 == f) goto fail; @@ -616,6 +660,11 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env, memcpy(&f->VolumeParams, &opt_data.VolumeParams, sizeof opt_data.VolumeParams); f->VolumeLabelLength = opt_data.VolumeLabelLength; memcpy(&f->VolumeLabel, &opt_data.VolumeLabel, opt_data.VolumeLabelLength); + if (0 != opt_data.FileSecuritySize) + { + memcpy(f->FileSecurityBuf, opt_data.FileSecurityBuf, opt_data.FileSecuritySize); + f->FileSecurity = f->FileSecurityBuf; + } Size = (lstrlenW(ch->MountPoint) + 1) * sizeof(WCHAR); f->MountPoint = fsp_fuse_obj_alloc(env, Size); diff --git a/src/dll/fuse/fuse_intf.c b/src/dll/fuse/fuse_intf.c index 6cc8bc33..b13c8541 100644 --- a/src/dll/fuse/fuse_intf.c +++ b/src/dll/fuse/fuse_intf.c @@ -472,7 +472,8 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem, if (0 != PSecurityDescriptorSize) { - Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor); + Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity, + &SecurityDescriptor); if (!NT_SUCCESS(Result)) goto exit; @@ -498,7 +499,7 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem, exit: if (0 != SecurityDescriptor) FspDeleteSecurityDescriptor(SecurityDescriptor, - FspPosixMapPermissionsToSecurityDescriptor); + FspPosixMergePermissionsToSecurityDescriptor); return Result; } @@ -1682,7 +1683,8 @@ static NTSTATUS fsp_fuse_intf_SetSecurity(FSP_FILE_SYSTEM *FileSystem, if (!NT_SUCCESS(Result)) goto exit; - Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor); + Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity, + &SecurityDescriptor); if (!NT_SUCCESS(Result)) goto exit; @@ -1729,7 +1731,7 @@ exit: if (0 != SecurityDescriptor) FspDeleteSecurityDescriptor(SecurityDescriptor, - FspPosixMapPermissionsToSecurityDescriptor); + FspPosixMergePermissionsToSecurityDescriptor); return Result; } diff --git a/src/dll/fuse/library.h b/src/dll/fuse/library.h index 7d40ddb7..2b3d71c9 100644 --- a/src/dll/fuse/library.h +++ b/src/dll/fuse/library.h @@ -72,6 +72,8 @@ struct fuse FSP_FILE_SYSTEM *FileSystem; volatile int exited; struct fuse3 *fuse3; + PSECURITY_DESCRIPTOR FileSecurity; + FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[]; }; struct fsp_fuse_context_header { @@ -156,6 +158,8 @@ struct fsp_fuse_core_opt_data FSP_FSCTL_VOLUME_PARAMS VolumeParams; UINT16 VolumeLabelLength; WCHAR VolumeLabel[sizeof ((FSP_FSCTL_VOLUME_INFO *)0)->VolumeLabel / sizeof(WCHAR)]; + ULONG FileSecuritySize; + FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[1024]; }; FSP_FSCTL_STATIC_ASSERT( sizeof ((struct fuse *)0)->VolumeLabel == sizeof ((struct fsp_fuse_core_opt_data *)0)->VolumeLabel, diff --git a/src/dll/security.c b/src/dll/security.c index 6222525b..eb0d5542 100644 --- a/src/dll/security.c +++ b/src/dll/security.c @@ -488,7 +488,8 @@ FSP_API VOID FspDeleteSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor return; if ((NTSTATUS (*)())FspAccessCheckEx == CreateFunc || - (NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc) + (NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc || + (NTSTATUS (*)())FspPosixMergePermissionsToSecurityDescriptor == CreateFunc) MemFree(SecurityDescriptor); else if ((NTSTATUS (*)())FspCreateSecurityDescriptor == CreateFunc || diff --git a/src/shared/ku/posix.c b/src/shared/ku/posix.c index f53d8fd5..a744d1f7 100644 --- a/src/shared/ku/posix.c +++ b/src/shared/ku/posix.c @@ -41,6 +41,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)()); FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor( UINT32 Uid, UINT32 Gid, UINT32 Mode, PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions( PSECURITY_DESCRIPTOR SecurityDescriptor, PUINT32 PUid, PUINT32 PGid, PUINT32 PMode); @@ -59,6 +63,7 @@ FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size); #pragma alloc_text(PAGE, FspPosixCreateSid) #pragma alloc_text(PAGE, FspDeleteSid) #pragma alloc_text(PAGE, FspPosixMapPermissionsToSecurityDescriptor) +#pragma alloc_text(PAGE, FspPosixMergePermissionsToSecurityDescriptor) #pragma alloc_text(PAGE, FspPosixMapSecurityDescriptorToPermissions) #pragma alloc_text(PAGE, FspPosixMapWindowsToPosixPathEx) #pragma alloc_text(PAGE, FspPosixMapPosixToWindowsPathEx) @@ -898,6 +903,101 @@ lasterror: goto exit; } +FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor) +{ + FSP_KU_CODE; + + if (0 == ExistingSecurityDescriptor) + return FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, PSecurityDescriptor); + + PSID ExistingOwnerSid = 0, ExistingGroupSid = 0; + BOOL Defaulted, ExistingDaclPresent; + PACL ExistingAcl = 0; + PSID OwnerSid = 0, GroupSid = 0; + SECURITY_DESCRIPTOR SecurityDescriptor; + PSECURITY_DESCRIPTOR RelativeSecurityDescriptor = 0; + ULONG Size; + NTSTATUS Result; + + *PSecurityDescriptor = 0; + + if (!GetSecurityDescriptorOwner(ExistingSecurityDescriptor, &ExistingOwnerSid, &Defaulted)) + goto lasterror; + if (!GetSecurityDescriptorGroup(ExistingSecurityDescriptor, &ExistingGroupSid, &Defaulted)) + goto lasterror; + if (!GetSecurityDescriptorDacl(ExistingSecurityDescriptor, &ExistingDaclPresent, &ExistingAcl, &Defaulted)) + goto lasterror; + + if (0 == ExistingOwnerSid) + { + Result = FspPosixMapUidToSid(Uid, &OwnerSid); + if (!NT_SUCCESS(Result)) + goto exit; + ExistingOwnerSid = OwnerSid; + } + + if (0 == ExistingGroupSid) + { + Result = FspPosixMapUidToSid(Gid, &GroupSid); + if (!NT_SUCCESS(Result)) + goto exit; + ExistingGroupSid = GroupSid; + } + + if (!InitializeSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) + goto lasterror; + + if (!SetSecurityDescriptorOwner(&SecurityDescriptor, ExistingOwnerSid, FALSE)) + goto lasterror; + if (!SetSecurityDescriptorGroup(&SecurityDescriptor, ExistingGroupSid, FALSE)) + goto lasterror; + if (0 != ExistingAcl) + { + if (!SetSecurityDescriptorControl(&SecurityDescriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED)) + goto lasterror; + if (!SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, ExistingAcl, FALSE)) + goto lasterror; + } + + Size = 0; + if (!MakeSelfRelativeSD(&SecurityDescriptor, 0, &Size) && + ERROR_INSUFFICIENT_BUFFER != GetLastError()) + goto lasterror; + + RelativeSecurityDescriptor = MemAlloc(Size); + if (0 == RelativeSecurityDescriptor) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + if (!MakeSelfRelativeSD(&SecurityDescriptor, RelativeSecurityDescriptor, &Size)) + goto lasterror; + + *PSecurityDescriptor = RelativeSecurityDescriptor; + + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result)) + MemFree(RelativeSecurityDescriptor); + + if (0 != GroupSid) + FspDeleteSid(GroupSid, FspPosixMapUidToSid); + + if (0 != OwnerSid) + FspDeleteSid(OwnerSid, FspPosixMapUidToSid); + + return Result; + +lasterror: + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; +} + static inline ACCESS_MASK FspPosixCanonicalizeAccessMask(ACCESS_MASK AccessMask) { PGENERIC_MAPPING Mapping = FspGetFileGenericMapping(); diff --git a/tst/winfsp-tests/posix-test.c b/tst/winfsp-tests/posix-test.c index 4a0e7e3f..7910647d 100644 --- a/tst/winfsp-tests/posix-test.c +++ b/tst/winfsp-tests/posix-test.c @@ -25,7 +25,7 @@ #include "winfsp-tests.h" -void posix_map_sid_test(void) +static void posix_map_sid_test(void) { struct { @@ -171,7 +171,7 @@ void posix_map_sid_test(void) LocalFree(map[sizeof map / sizeof map[0] - 1].SidStr); } -void posix_map_sd_test(void) +static void posix_map_sd_test(void) { struct { @@ -237,7 +237,93 @@ void posix_map_sd_test(void) } } -void posix_map_path_test(void) +static void posix_merge_sd_test(void) +{ + struct + { + UINT32 Uid, Gid, Mode; + PWSTR ExistingSddl; + PWSTR Sddl; + } map[] = + { + { + 18, 544, 00000, + 0, + L"O:SYG:BAD:P(A;;0x1f0198;;;SY)(A;;0x120088;;;BA)(A;;0x120088;;;WD)" + }, + { + 18, 544, 00000, + L"", + L"O:SYG:BA" + }, + { + 18, 544, 00000, + L"O:WD", + L"O:WDG:BA" + }, + { + 18, 544, 00000, + L"G:WD", + L"O:SYG:WD" + }, + { + 18, 544, 00000, + L"O:WDG:WD", + L"O:WDG:WD" + }, + { + 18, 544, 00000, + L"D:P", + L"O:SYG:BAD:P" + }, + { + 18, 544, 00000, + L"D:P(A;;FA;;;SY)", + L"O:SYG:BAD:P(A;;FA;;;SY)" + }, + { + 18, 544, 00000, + L"O:WDG:WDD:P(A;;FA;;;SY)", + L"O:WDG:WDD:P(A;;FA;;;SY)" + }, + }; + NTSTATUS Result; + BOOL Success; + PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, SecurityDescriptor; + PWSTR Sddl; + + for (size_t i = 0; sizeof map / sizeof map[0] > i; i++) + { + if (0 != map[i].ExistingSddl) + { + Success = ConvertStringSecurityDescriptorToSecurityDescriptorW( + map[i].ExistingSddl, SDDL_REVISION_1, + &ExistingSecurityDescriptor, 0); + ASSERT(Success); + } + else + ExistingSecurityDescriptor = 0; + + Result = FspPosixMergePermissionsToSecurityDescriptor( + map[i].Uid, map[i].Gid, map[i].Mode, ExistingSecurityDescriptor, &SecurityDescriptor); + ASSERT(NT_SUCCESS(Result)); + + Success = ConvertSecurityDescriptorToStringSecurityDescriptorW( + SecurityDescriptor, SDDL_REVISION_1, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + &Sddl, 0); + ASSERT(Success); + ASSERT(0 == wcscmp(map[i].Sddl, Sddl)); + LocalFree(Sddl); + + FspDeleteSecurityDescriptor(SecurityDescriptor, + FspPosixMergePermissionsToSecurityDescriptor); + + LocalFree(ExistingSecurityDescriptor); + } +} + +static void posix_map_path_test(void) { struct { @@ -274,5 +360,6 @@ void posix_tests(void) TEST(posix_map_sid_test); TEST(posix_map_sd_test); + TEST(posix_merge_sd_test); TEST(posix_map_path_test); }