From 9ccb394b04728a442e32c85a6027dfbdb8fdc157 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 4 Jun 2016 23:46:29 -0700 Subject: [PATCH] dll: POSIX interop --- build/VStudio/winfsp_dll.vcxproj | 1 + build/VStudio/winfsp_dll.vcxproj.filters | 3 + inc/winfsp/winfsp.h | 13 + src/dll/posix.c | 365 +++++++++++++++++++++++ src/dll/security.c | 3 +- 5 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 src/dll/posix.c diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj index 29f4208c..2965d711 100644 --- a/build/VStudio/winfsp_dll.vcxproj +++ b/build/VStudio/winfsp_dll.vcxproj @@ -37,6 +37,7 @@ + diff --git a/build/VStudio/winfsp_dll.vcxproj.filters b/build/VStudio/winfsp_dll.vcxproj.filters index 90c8237b..6782b92e 100644 --- a/build/VStudio/winfsp_dll.vcxproj.filters +++ b/build/VStudio/winfsp_dll.vcxproj.filters @@ -100,6 +100,9 @@ Source\fuse + + Source + diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 21d90a2d..59264a2d 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -838,6 +838,19 @@ NTSTATUS FspAccessCheck(FSP_FILE_SYSTEM *FileSystem, 0); } +/* + * POSIX Interop + */ +FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid); +FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid); +FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)()); +FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PUINT32 PUid, PUINT32 PGid, PUINT32 PMode); + /* * Path Handling */ diff --git a/src/dll/posix.c b/src/dll/posix.c new file mode 100644 index 00000000..1d7cd880 --- /dev/null +++ b/src/dll/posix.c @@ -0,0 +1,365 @@ +/** + * @file dll/posix.c + * POSIX Interop. + * + * This file provides routines for Windows/POSIX interoperability. It is based + * on "Services for UNIX" and Cygwin. See the following documents: + * + * [PERM] + * https://technet.microsoft.com/en-us/library/bb463216.aspx + * [IDMAP] + * https://cygwin.com/cygwin-ug-net/ntsec.html + * [WKSID] + * https://support.microsoft.com/en-us/kb/243330 + * [NAME] + * https://www.cygwin.com/cygwin-ug-net/using-specialnames.html + * + * @copyright 2015-2016 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the + * GNU Affero General Public License version 3 as published by the + * Free Software Foundation. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * software. + */ + +#include +#define _NTDEF_ +#include + +static PISID FspPosixCreateSid(BYTE Authority, ULONG Count, ...); + +static INIT_ONCE FspPosixInitOnceV = INIT_ONCE_STATIC_INIT; +static PISID FspAccountDomainSid, FspPrimaryDomainSid; + +VOID FspPosixInitialize(BOOLEAN Dynamic) +{ +} + +VOID FspPosixFinalize(BOOLEAN Dynamic) +{ + /* + * This function is called during DLL_PROCESS_DETACH. We must therefore keep + * finalization tasks to a minimum. + */ + + if (Dynamic) + { + MemFree(FspAccountDomainSid); + MemFree(FspPrimaryDomainSid); + } +} + +static BOOL WINAPI FspPosixInitOnceF( + PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) +{ + static LSA_OBJECT_ATTRIBUTES Obja; + LSA_HANDLE PolicyHandle = 0; + PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = 0; + PPOLICY_DNS_DOMAIN_INFO PrimaryDomainInfo = 0; + BYTE Count; + ULONG Size; + NTSTATUS Result; + + Result = LsaOpenPolicy(0, &Obja, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, + &AccountDomainInfo); + if (NT_SUCCESS(Result) && 0 != AccountDomainInfo && 0 != AccountDomainInfo->DomainSid) + { + Count = *GetSidSubAuthorityCount(AccountDomainInfo->DomainSid); + Size = sizeof(SID) - sizeof(DWORD) + (Count * sizeof(DWORD)); + FspAccountDomainSid = MemAlloc(Size); + if (0 != FspAccountDomainSid) + memcpy(FspAccountDomainSid, AccountDomainInfo->DomainSid, Size); + } + + Result = LsaQueryInformationPolicy(PolicyHandle, PolicyDnsDomainInformation, + &PrimaryDomainInfo); + if (NT_SUCCESS(Result) && 0 != PrimaryDomainInfo && 0 != PrimaryDomainInfo->Sid) + { + Count = *GetSidSubAuthorityCount(PrimaryDomainInfo->Sid); + Size = sizeof(SID) - sizeof(DWORD) + (Count * sizeof(DWORD)); + FspPrimaryDomainSid = MemAlloc(Size); + if (0 != FspPrimaryDomainSid) + memcpy(FspPrimaryDomainSid, PrimaryDomainInfo->Sid, Size); + } + +exit: + if (0 != PrimaryDomainInfo) + LsaFreeMemory(PrimaryDomainInfo); + + if (0 != AccountDomainInfo) + LsaFreeMemory(AccountDomainInfo); + + if (0 != PolicyHandle) + LsaClose(PolicyHandle); + + return TRUE; +} + +FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid) +{ + *PSid = 0; + + /* + * UID namespace partitioning (from [IDMAP] rules): + * + * 0x000000 + RID S-1-5-RID,S-1-5-32-RID + * 0x000ffe OtherSession + * 0x000fff CurrentSession + * 0x001000 * X + RID S-1-5-X-RID ([WKSID]: X=1-15,17-21,32,64,80,83) + * 0x010000 + 0x100 * X + Y S-1-X-Y ([WKSID]: X=1,2,3,4,5,9,16) + * 0x030000 + RID S-1-5-21-X-Y-Z-RID + * 0x060000 + RID S-1-16-RID + * 0x100000 + RID S-1-5-21-X-Y-Z-RID + */ + + /* [IDMAP] + * Well-known SIDs in the NT_AUTHORITY domain of the S-1-5-RID type, + * or aliases of the S-1-5-32-RID type are mapped to the uid/gid value RID. + * Examples: + * "SYSTEM" S-1-5-18 <=> uid/gid: 18 + * "Users" S-1-5-32-545 <=> uid/gid: 545 + */ + if (0x200 > Uid || 1000 == Uid) + *PSid = FspPosixCreateSid(5, 1, Uid); + else if (1000 > Uid) + *PSid = FspPosixCreateSid(5, 2, 32, Uid); + + /* [IDMAP] + * Logon SIDs: The LogonSid of the current user's session is converted + * to the fixed uid 0xfff == 4095 and named "CurrentSession". Any other + * LogonSid is converted to the fixed uid 0xffe == 4094 and named + * "OtherSession". + */ + else if (0xfff == Uid || 0xffe == Uid) + { + /* + * Actually we do not support Logon SID's for translation. + * We need an access token to find its Logon SID and we do not have one. + */ + } + + /* [IDMAP] + * Accounts from the local machine's user DB (SAM): + * S-1-5-21-X-Y-Z-RID <=> uid/gid: 0x30000 + RID + * + * Accounts from the machine's primary domain: + * S-1-5-21-X-Y-Z-RID <=> uid/gid: 0x100000 + RID + * + * Accounts from a trusted domain of the machine's primary domain: + * S-1-5-21-X-Y-Z-RID <=> uid/gid: trustPosixOffset(domain) + RID + */ + else if (0x30000 <= Uid && Uid < 0x40000) + { + if (5 == FspAccountDomainSid->IdentifierAuthority.Value[5] && + 4 == FspAccountDomainSid->SubAuthorityCount) + { + *PSid = FspPosixCreateSid(5, 5, + 21, + FspAccountDomainSid->SubAuthority[1], + FspAccountDomainSid->SubAuthority[2], + FspAccountDomainSid->SubAuthority[3], + Uid - 0x30000); + } + } + else if (0x100000 <= Uid && Uid < 0x200000) + { + if (5 == FspPrimaryDomainSid->IdentifierAuthority.Value[5] && + 4 == FspPrimaryDomainSid->SubAuthorityCount) + { + *PSid = FspPosixCreateSid(5, 5, + 21, + FspPrimaryDomainSid->SubAuthority[1], + FspPrimaryDomainSid->SubAuthority[2], + FspPrimaryDomainSid->SubAuthority[3], + Uid - 0x100000); + } + } + /* + * I am sorry, I am not going to bother with all that trustPosixOffset stuff. + * But if you need it, I accept patches :) + */ + + /* [IDMAP] + * Mandatory Labels: + * S-1-16-RID <=> uid/gid: 0x60000 + RID + */ + else if (0x60000 <= Uid && Uid < 0x70000) + *PSid = FspPosixCreateSid(5, 2, 16, Uid - 0x60000); + + /* [IDMAP] + * Other well-known SIDs: + * S-1-X-Y <=> uid/gid: 0x10000 + 0x100 * X + Y + */ + else if (0x10000 <= Uid && Uid < 0x11000) + *PSid = FspPosixCreateSid(5, 2, (Uid - 0x10000) >> 8, (Uid - 0x10000) & 0xff); + + /* [IDMAP] + * Other well-known SIDs in the NT_AUTHORITY domain (S-1-5-X-RID): + * S-1-5-X-RID <=> uid/gid: 0x1000 * X + RID + */ + else if (0x1000 <= Uid && Uid < 0x100000) + *PSid = FspPosixCreateSid(5, 5, Uid >> 12, Uid & 0xfff); + + if (0 == *PSid) + return STATUS_NONE_MAPPED; + + return STATUS_SUCCESS; +} + +FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid) +{ + BYTE Authority; + BYTE Count; + UINT32 SubAuthority0, Rid; + + *PUid = -1; + + if (!IsValidSid(Sid) || 0 == (Count = *GetSidSubAuthorityCount(Sid))) + return STATUS_INVALID_SID; + + Authority = GetSidIdentifierAuthority(Sid)->Value[5]; + SubAuthority0 = 2 <= Count ? *GetSidSubAuthority(Sid, 0) : 0; + Rid = *GetSidSubAuthority(Sid, Count - 1); + + if (5 == Authority) + { + /* [IDMAP] + * Well-known SIDs in the NT_AUTHORITY domain of the S-1-5-RID type, + * or aliases of the S-1-5-32-RID type are mapped to the uid/gid value RID. + * Examples: + * "SYSTEM" S-1-5-18 <=> uid/gid: 18 + * "Users" S-1-5-32-545 <=> uid/gid: 545 + */ + if (1 == Count) + *PUid = Rid; + else if (2 == Count && 32 == SubAuthority0) + *PUid = Rid; + + /* [IDMAP] + * Logon SIDs: The LogonSid of the current user's session is converted + * to the fixed uid 0xfff == 4095 and named "CurrentSession". Any other + * LogonSid is converted to the fixed uid 0xffe == 4094 and named + * "OtherSession". + */ + else if (3 <= Count && 5 == SubAuthority0) + { + /* + * Actually we do not support Logon SID's for translation. + * We need an access token to find its Logon SID and we do not have one. + */ + } + + /* [IDMAP] + * Accounts from the local machine's user DB (SAM): + * S-1-5-21-X-Y-Z-RID <=> uid/gid: 0x30000 + RID + * + * Accounts from the machine's primary domain: + * S-1-5-21-X-Y-Z-RID <=> uid/gid: 0x100000 + RID + * + * Accounts from a trusted domain of the machine's primary domain: + * S-1-5-21-X-Y-Z-RID <=> uid/gid: trustPosixOffset(domain) + RID + */ + else if (3 <= Count && 21 == SubAuthority0) + { + InitOnceExecuteOnce(&FspPosixInitOnceV, FspPosixInitOnceF, 0, 0); + + /* + * The order is important! A server that is also a domain controller + * has PrimaryDomainSid == AccountDomainSid. + */ + + BOOL EqualDomains = FALSE; + if (0 != FspPrimaryDomainSid && + EqualDomainSid(FspPrimaryDomainSid, Sid, &EqualDomains) && EqualDomains) + *PUid = 0x100000 + Rid; + else if (0 != FspAccountDomainSid && + EqualDomainSid(FspAccountDomainSid, Sid, &EqualDomains) && EqualDomains) + *PUid = 0x30000 + Rid; + + /* + * I am sorry, I am not going to bother with all that trustPosixOffset stuff. + * But if you need it, I accept patches :) + */ + } + + /* [IDMAP] + * Other well-known SIDs in the NT_AUTHORITY domain (S-1-5-X-RID): + * S-1-5-X-RID <=> uid/gid: 0x1000 * X + RID + */ + else if (3 == Count) + { + *PUid = 0x1000 * SubAuthority0 + Rid; + } + } + else if (16 == Authority) + { + /* [IDMAP] + * Mandatory Labels: + * S-1-16-RID <=> uid/gid: 0x60000 + RID + */ + *PUid = 0x60000 + Rid; + } + else + { + /* [IDMAP] + * Other well-known SIDs: + * S-1-X-Y <=> uid/gid: 0x10000 + 0x100 * X + Y + */ + *PUid = 0x10000 + 0x100 * Authority + Rid; + } + + if (-1 == *PUid) + return STATUS_NONE_MAPPED; + + return STATUS_SUCCESS; +} + +static PISID FspPosixCreateSid(BYTE Authority, ULONG Count, ...) +{ + PISID Sid; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + va_list ap; + + Sid = MemAlloc(sizeof(SID) - sizeof(DWORD) + (Count * sizeof(DWORD))); + if (0 == Sid) + return 0; + + memset(&IdentifierAuthority, 0, sizeof IdentifierAuthority); + IdentifierAuthority.Value[5] = Authority; + + InitializeSid(Sid, &IdentifierAuthority, (BYTE)Count); + va_start(ap, Count); + for (ULONG Index = 0; Count > Index; Index++) + Sid->SubAuthority[Index] = va_arg(ap, DWORD); + va_end(ap); + + return Sid; +} + +FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)()) +{ + if ((NTSTATUS (*)())FspPosixMapUidToSid == CreateFunc) + MemFree(Sid); +} + +FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR *PSecurityDescriptor) +{ +} + +FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PUINT32 PUid, PUINT32 PGid, PUINT32 PMode) +{ +} diff --git a/src/dll/security.c b/src/dll/security.c index f74768bc..2b4dcda6 100644 --- a/src/dll/security.c +++ b/src/dll/security.c @@ -362,7 +362,8 @@ FSP_API NTSTATUS FspSetSecurityDescriptor(FSP_FILE_SYSTEM *FileSystem, FSP_API VOID FspDeleteSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor, NTSTATUS (*CreateFunc)()) { - if ((NTSTATUS (*)())FspAccessCheckEx == CreateFunc) + if ((NTSTATUS (*)())FspAccessCheckEx == CreateFunc || + (NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc) MemFree(SecurityDescriptor); else if ((NTSTATUS (*)())FspCreateSecurityDescriptor == CreateFunc ||