winfsp/src/shared/ku/posix.c
2021-12-07 14:40:28 +00:00

1418 lines
48 KiB
C

/**
* @file shared/ku/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:
*
* [PERMS]
* https://technet.microsoft.com/en-us/library/bb463216.aspx
* [WKSID]
* https://support.microsoft.com/en-us/kb/243330
* [IDMAP]
* https://cygwin.com/cygwin-ug-net/ntsec.html
* [SNAME]
* https://www.cygwin.com/cygwin-ug-net/using-specialnames.html
*
* @copyright 2015-2021 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this software
* in accordance with the commercial license agreement provided in
* conjunction with the software. The terms and conditions of any such
* commercial license agreement shall govern, supersede, and render
* ineffective any application of the GPLv3 license to this software,
* notwithstanding of any reference thereto in the software or
* associated repository.
*/
#include <shared/ku/library.h>
FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid);
FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid);
static PISID FspPosixCreateSid(BYTE Authority, ULONG Count, ...);
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);
FSP_API NTSTATUS FspPosixMapWindowsToPosixPathEx(PWSTR WindowsPath, char **PPosixPath,
BOOLEAN Translate);
FSP_API NTSTATUS FspPosixMapPosixToWindowsPathEx(const char *PosixPath, PWSTR *PWindowsPath,
BOOLEAN Translate);
FSP_API VOID FspPosixDeletePath(void *Path);
FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size);
FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size);
#if defined(_KERNEL_MODE)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspPosixMapUidToSid)
#pragma alloc_text(PAGE, FspPosixMapSidToUid)
#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)
#pragma alloc_text(PAGE, FspPosixDeletePath)
#pragma alloc_text(PAGE, FspPosixEncodeWindowsPath)
#pragma alloc_text(PAGE, FspPosixDecodeWindowsPath)
#endif
#endif
static union
{
SID V;
UINT8 B[sizeof(SID) - sizeof(DWORD) + (1 * sizeof(DWORD))];
} FspWorldSidBuf =
{
/* S-1-1-0 */
.V.Revision = SID_REVISION,
.V.SubAuthorityCount = 1,
.V.IdentifierAuthority.Value[5] = 1,
.V.SubAuthority[0] = 0,
};
static union
{
SID V;
UINT8 B[sizeof(SID) - sizeof(DWORD) + (1 * sizeof(DWORD))];
} FspAuthUsersSidBuf =
{
/* S-1-5-11 */
.V.Revision = SID_REVISION,
.V.SubAuthorityCount = 1,
.V.IdentifierAuthority.Value[5] = 5,
.V.SubAuthority[0] = 11,
};
static union
{
SID V;
UINT8 B[sizeof(SID) - sizeof(DWORD) + (1 * sizeof(DWORD))];
} FspUnmappedSidBuf =
{
/* S-1-0-65534 */
.V.Revision = SID_REVISION,
.V.SubAuthorityCount = 1,
.V.IdentifierAuthority.Value[5] = 0,
.V.SubAuthority[0] = 65534,
};
#define FspWorldSid (&FspWorldSidBuf.V)
#define FspAuthUsersSid (&FspAuthUsersSidBuf.V)
#define FspUnmappedSid (&FspUnmappedSidBuf.V)
#define FspUnmappedUid (65534)
static PISID FspAccountDomainSid, FspPrimaryDomainSid;
static struct
{
PSID DomainSid;
PWSTR NetbiosDomainName;
PWSTR DnsDomainName;
ULONG TrustPosixOffset;
} *FspTrustedDomains;
static ULONG FspTrustedDomainCount;
static BOOLEAN FspDistinctPermsForSameOwnerGroup;
static INIT_ONCE FspPosixInitOnce = INIT_ONCE_STATIC_INIT;
#if !defined(_KERNEL_MODE)
static ULONG FspPosixInitializeTrustPosixOffsets(VOID)
{
PVOID Ldap = 0;
PWSTR DefaultNamingContext = 0;
PWSTR TrustPosixOffsetString = 0;
ULONG LdapResult;
LdapResult = FspLdapConnect(0/* default LDAP server */, &Ldap);
if (0 != LdapResult)
goto exit;
LdapResult = FspLdapGetDefaultNamingContext(Ldap, &DefaultNamingContext);
if (0 != LdapResult)
goto exit;
/* get the "trustPosixOffset" for each trusted domain */
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
{
MemFree(TrustPosixOffsetString);
LdapResult = FspLdapGetTrustPosixOffset(Ldap,
DefaultNamingContext, FspTrustedDomains[I].DnsDomainName, &TrustPosixOffsetString);
if (0 == LdapResult)
FspTrustedDomains[I].TrustPosixOffset = wcstouint(TrustPosixOffsetString, 0, 10, 1);
}
LdapResult = 0;
exit:
MemFree(TrustPosixOffsetString);
MemFree(DefaultNamingContext);
if (0 != Ldap)
FspLdapClose(Ldap);
/* if the "trustPosixOffset" looks wrong, fix it up using Cygwin magic value 0xfe500000 */
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
{
if (0x100000 > FspTrustedDomains[I].TrustPosixOffset)
FspTrustedDomains[I].TrustPosixOffset = 0xfe500000;
}
return LdapResult;
}
static VOID FspPosixInitializeFromRegistry(VOID)
{
DWORD DistinctPermsForSameOwnerGroup;
DWORD Size;
LONG Result;
DistinctPermsForSameOwnerGroup = 0;
Size = sizeof DistinctPermsForSameOwnerGroup;
Result = RegGetValueW(HKEY_LOCAL_MACHINE, L"" FSP_FSCTL_PRODUCT_FULL_REGKEY,
L"DistinctPermsForSameOwnerGroup",
RRF_RT_REG_DWORD, 0, &DistinctPermsForSameOwnerGroup, &Size);
if (ERROR_SUCCESS == Result)
FspDistinctPermsForSameOwnerGroup = !!DistinctPermsForSameOwnerGroup;
}
static BOOL WINAPI FspPosixInitialize(
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;
PDS_DOMAIN_TRUSTSW TrustedDomains = 0;
ULONG TrustedDomainCount, RealTrustedDomainCount;
BYTE Count;
ULONG Size, Temp;
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);
if (ERROR_SUCCESS == DsEnumerateDomainTrustsW(
0, DS_DOMAIN_DIRECT_INBOUND | DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_IN_FOREST,
&TrustedDomains, &TrustedDomainCount))
{
Size = 0;
RealTrustedDomainCount = 0;
for (ULONG I = 0; TrustedDomainCount > I; I++)
{
if (0 == TrustedDomains[I].DomainSid ||
(0 == TrustedDomains[I].NetbiosDomainName &&
0 == TrustedDomains[I].DnsDomainName) ||
EqualSid(TrustedDomains[I].DomainSid, FspPrimaryDomainSid))
continue;
if (0 != TrustedDomains[I].DomainSid)
{
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(Size);
Size += GetLengthSid(TrustedDomains[I].DomainSid);
}
if (0 != TrustedDomains[I].NetbiosDomainName)
{
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
Size += (lstrlenW(TrustedDomains[I].NetbiosDomainName) + 1) * sizeof(WCHAR);
}
if (0 != TrustedDomains[I].DnsDomainName)
{
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
Size += (lstrlenW(TrustedDomains[I].DnsDomainName) + 1) * sizeof(WCHAR);
}
RealTrustedDomainCount++;
}
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof FspTrustedDomains[0] * RealTrustedDomainCount) + Size;
if (0 < RealTrustedDomainCount)
{
FspTrustedDomains = MemAlloc(Size);
if (0 != FspTrustedDomains)
{
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof FspTrustedDomains[0] * RealTrustedDomainCount);
for (ULONG I = 0, J = 0; TrustedDomainCount > I; I++)
{
if (0 == TrustedDomains[I].DomainSid ||
(0 == TrustedDomains[I].NetbiosDomainName &&
0 == TrustedDomains[I].DnsDomainName) ||
EqualSid(TrustedDomains[I].DomainSid, FspPrimaryDomainSid))
continue;
FspTrustedDomains[J].DomainSid = 0;
FspTrustedDomains[J].NetbiosDomainName = 0;
FspTrustedDomains[J].DnsDomainName = 0;
FspTrustedDomains[J].TrustPosixOffset = 0;
if (0 != TrustedDomains[I].DomainSid)
{
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(Size);
FspTrustedDomains[J].DomainSid =
(PVOID)((PUINT8)FspTrustedDomains + Size);
Size += (Temp = GetLengthSid(TrustedDomains[I].DomainSid));
memcpy(FspTrustedDomains[J].DomainSid,
TrustedDomains[I].DomainSid, Temp);
}
if (0 != TrustedDomains[I].NetbiosDomainName)
{
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
FspTrustedDomains[J].NetbiosDomainName =
(PVOID)((PUINT8)FspTrustedDomains + Size);
Size += (Temp = (lstrlenW(TrustedDomains[I].NetbiosDomainName) + 1) * sizeof(WCHAR));
memcpy(FspTrustedDomains[J].NetbiosDomainName,
TrustedDomains[I].NetbiosDomainName, Temp);
}
if (0 != TrustedDomains[I].DnsDomainName)
{
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
FspTrustedDomains[J].DnsDomainName =
(PVOID)((PUINT8)FspTrustedDomains + Size);
Size += (Temp = (lstrlenW(TrustedDomains[I].DnsDomainName) + 1) * sizeof(WCHAR));
memcpy(FspTrustedDomains[J].DnsDomainName,
TrustedDomains[I].DnsDomainName, Temp);
}
if (0 == FspTrustedDomains[J].NetbiosDomainName)
FspTrustedDomains[J].NetbiosDomainName =
FspTrustedDomains[J].DnsDomainName;
else
if (0 == FspTrustedDomains[J].DnsDomainName)
FspTrustedDomains[J].DnsDomainName =
FspTrustedDomains[J].NetbiosDomainName;
J++;
}
FspTrustedDomainCount = RealTrustedDomainCount;
}
}
}
}
if (0 < FspTrustedDomainCount)
FspPosixInitializeTrustPosixOffsets();
FspPosixInitializeFromRegistry();
exit:
if (0 != TrustedDomains)
NetApiBufferFree(TrustedDomains);
if (0 != PrimaryDomainInfo)
LsaFreeMemory(PrimaryDomainInfo);
if (0 != AccountDomainInfo)
LsaFreeMemory(AccountDomainInfo);
if (0 != PolicyHandle)
LsaClose(PolicyHandle);
return TRUE;
}
VOID FspPosixFinalize(BOOLEAN Dynamic)
{
/*
* This function is called during DLL_PROCESS_DETACH. We must therefore keep
* finalization tasks to a minimum.
*/
if (Dynamic)
{
MemFree(FspTrustedDomains);
MemFree(FspAccountDomainSid);
MemFree(FspPrimaryDomainSid);
}
}
#else
ULONG NTAPI FspPosixInitialize(
PRTL_RUN_ONCE RunOnce, PVOID Parameter, PVOID *Context)
{
static union
{
SID V;
UINT8 B[SECURITY_MAX_SID_SIZE];
} FspAccountDomainSidBuf;
static union
{
SID V;
UINT8 B[SECURITY_MAX_SID_SIZE];
} FspPrimaryDomainSidBuf;
UNICODE_STRING Path;
UNICODE_STRING Name;
union
{
KEY_VALUE_PARTIAL_INFORMATION V;
UINT8 B[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + SECURITY_MAX_SID_SIZE];
} Value;
ULONG Length;
NTSTATUS Result;
RtlInitUnicodeString(&Path, L"\\Registry\\Machine\\SECURITY\\Policy\\PolAcDmS");
RtlZeroMemory(&Name, sizeof Name);
Length = sizeof Value;
Result = FspRegistryGetValue(&Path, &Name, &Value.V, &Length);
if (NT_SUCCESS(Result) && REG_NONE == Value.V.Type &&
sizeof(SID) <= Value.V.DataLength && RtlValidSid((PSID)&Value.V.Data))
{
RtlCopyMemory(&FspAccountDomainSidBuf.V, &Value.V.Data, Value.V.DataLength);
FspAccountDomainSid = &FspAccountDomainSidBuf.V;
}
RtlInitUnicodeString(&Path, L"\\Registry\\Machine\\SECURITY\\Policy\\PolPrDmS");
RtlZeroMemory(&Name, sizeof Name);
Length = sizeof Value;
Result = FspRegistryGetValue(&Path, &Name, &Value.V, &Length);
if (NT_SUCCESS(Result) && REG_NONE == Value.V.Type &&
sizeof(SID) <= Value.V.DataLength && RtlValidSid((PSID)&Value.V.Data))
{
RtlCopyMemory(&FspPrimaryDomainSidBuf.V, &Value.V.Data, Value.V.DataLength);
FspPrimaryDomainSid = &FspPrimaryDomainSidBuf.V;
}
/* always enable permissive permissions for same owner group in kernel mode */
FspDistinctPermsForSameOwnerGroup = TRUE;
return TRUE;
}
#endif
static inline BOOLEAN FspPosixIsRelativeSid(PISID Sid1, PISID Sid2)
{
if (Sid1->Revision != Sid2->Revision)
return FALSE;
if (Sid1->IdentifierAuthority.Value[0] != Sid2->IdentifierAuthority.Value[0] ||
Sid1->IdentifierAuthority.Value[1] != Sid2->IdentifierAuthority.Value[1] ||
Sid1->IdentifierAuthority.Value[2] != Sid2->IdentifierAuthority.Value[2] ||
Sid1->IdentifierAuthority.Value[3] != Sid2->IdentifierAuthority.Value[3] ||
Sid1->IdentifierAuthority.Value[4] != Sid2->IdentifierAuthority.Value[4] ||
Sid1->IdentifierAuthority.Value[5] != Sid2->IdentifierAuthority.Value[5])
return FALSE;
if (Sid1->SubAuthorityCount + 1 != Sid2->SubAuthorityCount)
return FALSE;
for (ULONG I = 0; Sid1->SubAuthorityCount > I; I++)
if (Sid1->SubAuthority[I] != Sid2->SubAuthority[I])
return FALSE;
return TRUE;
}
FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid)
{
FSP_KU_CODE;
InitOnceExecuteOnce(&FspPosixInitOnce, FspPosixInitialize, 0, 0);
*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 (0 != FspAccountDomainSid &&
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 < 0xff000000)
{
if ((Uid < 0x300000 || 0 == FspTrustedDomainCount) &&
0 != FspPrimaryDomainSid &&
5 == FspPrimaryDomainSid->IdentifierAuthority.Value[5] &&
4 == FspPrimaryDomainSid->SubAuthorityCount)
{
*PSid = FspPosixCreateSid(5, 5,
21,
FspPrimaryDomainSid->SubAuthority[1],
FspPrimaryDomainSid->SubAuthority[2],
FspPrimaryDomainSid->SubAuthority[3],
Uid - 0x100000);
}
else
{
PISID DomainSid = 0;
ULONG TrustPosixOffset = 0;
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
{
if (FspTrustedDomains[I].TrustPosixOffset <= Uid &&
FspTrustedDomains[I].TrustPosixOffset > TrustPosixOffset)
{
DomainSid = FspTrustedDomains[I].DomainSid;
TrustPosixOffset = FspTrustedDomains[I].TrustPosixOffset;
}
}
if (0 != DomainSid)
{
*PSid = FspPosixCreateSid(5, 5,
21,
DomainSid->SubAuthority[1],
DomainSid->SubAuthority[2],
DomainSid->SubAuthority[3],
Uid - TrustPosixOffset);
}
}
}
/* [IDMAP]
* Mandatory Labels:
* S-1-16-RID <=> uid/gid: 0x60000 + RID
*/
else if (0x60000 <= Uid && Uid < 0x70000)
*PSid = FspPosixCreateSid(16, 1, 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((BYTE)((Uid - 0x10000) >> 8), 1, (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 (FspUnmappedUid != Uid && 0x1000 <= Uid && Uid < 0x100000)
*PSid = FspPosixCreateSid(5, 2, Uid >> 12, Uid & 0xfff);
if (0 == *PSid)
*PSid = FspUnmappedSid;
return STATUS_SUCCESS;
}
FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid)
{
FSP_KU_CODE;
InitOnceExecuteOnce(&FspPosixInitOnce, FspPosixInitialize, 0, 0);
BYTE Authority;
BYTE Count;
UINT32 SubAuthority0, Rid;
*PUid = (UINT32)-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 (2 <= 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 (5 <= Count && 21 == SubAuthority0)
{
/*
* The order is important! A server that is also a domain controller
* has PrimaryDomainSid == AccountDomainSid.
*/
if (0 != FspPrimaryDomainSid &&
FspPosixIsRelativeSid(FspPrimaryDomainSid, Sid))
*PUid = 0x100000 + Rid;
else if (0 != FspAccountDomainSid &&
FspPosixIsRelativeSid(FspAccountDomainSid, Sid))
*PUid = 0x30000 + Rid;
else
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
{
if (FspPosixIsRelativeSid(FspTrustedDomains[I].DomainSid, Sid))
{
*PUid = FspTrustedDomains[I].TrustPosixOffset + Rid;
break;
}
}
}
/* [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 (2 == Count)
{
*PUid = 0x1000 * SubAuthority0 + Rid;
}
}
else if (16 == Authority)
{
/* [IDMAP]
* Mandatory Labels:
* S-1-16-RID <=> uid/gid: 0x60000 + RID
*/
*PUid = 0x60000 + Rid;
}
else if (
FspUnmappedSid->IdentifierAuthority.Value[5] != Authority ||
FspUnmappedSid->SubAuthority[0] != Rid)
{
/* [IDMAP]
* Other well-known SIDs:
* S-1-X-Y <=> uid/gid: 0x10000 + 0x100 * X + Y
*/
*PUid = 0x10000 + 0x100 * Authority + Rid;
}
if (-1 == *PUid)
*PUid = FspUnmappedUid;
return STATUS_SUCCESS;
}
static PISID FspPosixCreateSid(BYTE Authority, ULONG Count, ...)
{
FSP_KU_CODE;
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)())
{
FSP_KU_CODE;
if (FspUnmappedSid == Sid)
;
else if ((NTSTATUS (*)())FspPosixMapUidToSid == CreateFunc)
MemFree(Sid);
}
/* [PERMS]
* By default, all access-allowed ACEs will contain the following Windows access rights.
*/
#define FspPosixDefaultPerm \
(SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA)
/* [PERMS]
* There are some additional Windows access rights that are always set in the
* access-allowed ACE for the file's owner.
*/
#define FspPosixOwnerDefaultPerm \
(FspPosixDefaultPerm | DELETE | WRITE_DAC | WRITE_OWNER | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)
static inline ACCESS_MASK FspPosixMapPermissionToAccessMask(UINT32 Mode, UINT32 Perm)
{
/*
* We use only the 0040000 (directory) and 0001000 (sticky) bits from Mode.
* If this is a directory and it does not have the sticky bit set (and the
* write permission is enabled) we add FILE_DELETE_CHILD access.
*
* When calling this function for computing the Owner access mask, we always
* pass Mode & ~0001000 to remove the sticky bit and thus add FILE_DELETE_CHILD
* access if it is a directory. For Group and World permissions we do not
* remove the sticky bit as we do not want FILE_DELETE_CHILD access in these
* cases.
*/
ACCESS_MASK DeleteChild = 0040000 == (Mode & 0041000) ? FILE_DELETE_CHILD : 0;
/* [PERMS]
* Additionally, if the UNIX read permission bit is set, then the Windows
* File_Read access right is added to the ACE. When enabled on directories,
* this allows them to be searched. When enabled on files, it allows the data
* to be viewed. If the UNIX execute permission bit is set, then the Windows
* File_Execute access right is added to the ACE. On directories this enables
* the directory to be traversed. On files it allows the file to be executed.
*
* If the UNIX write permission bit is set then the following Windows access
* rights are added: Write_Data, Write_Attributes, Append_Data, Delete_Child.
*
* Notice how Windows has four separate access rights to UNIX's single "write"
* permission. In UNIX, the write permission bit on a directory permits both
* the creation and removal of new files or sub-directories in the directory.
* On Windows, the Write_Data access right controls the creation of new
* sub-files and the Delete_Child access right controls the deletion. The
* Delete_Child access right is not always present in all ACEs. In the case
* where the UNIX sticky-bit is enabled, the Delete_Child bit will be set only
* in the file owner ACE and no other ACEs. This will permit only the directory
* owner to remove any files or sub-directories from this directory regardless
* of the ownership on these sub-files. Other users will be allowed to delete
* files or sub-directories only if they are granted the Delete access right
* in an ACE of the file or sub-directory itself.
*/
return
((Perm & 4) ? FILE_READ_DATA : 0) |
((Perm & 2) ? FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_APPEND_DATA | DeleteChild : 0) |
((Perm & 1) ? FILE_EXECUTE : 0);
}
FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
UINT32 Uid, UINT32 Gid, UINT32 Mode,
PSECURITY_DESCRIPTOR *PSecurityDescriptor)
{
FSP_KU_CODE;
PSID OwnerSid = 0, GroupSid = 0;
UINT32 OwnerPerm, OwnerDeny, GroupPerm, GroupDeny, WorldPerm;
PACL Acl = 0;
SECURITY_DESCRIPTOR SecurityDescriptor;
PSECURITY_DESCRIPTOR RelativeSecurityDescriptor = 0;
ULONG Size;
NTSTATUS Result;
*PSecurityDescriptor = 0;
Result = FspPosixMapUidToSid(Uid, &OwnerSid);
if (!NT_SUCCESS(Result))
goto exit;
Result = FspPosixMapUidToSid(Gid, &GroupSid);
if (!NT_SUCCESS(Result))
goto exit;
OwnerPerm = (Mode & 0700) >> 6;
GroupPerm = (Mode & 0070) >> 3;
WorldPerm = (Mode & 0007);
/* [PERMS]
* What about the case where both owner and group are the same SID and
* a chmod(1) request is made where the owner and the group permission
* bits are different?. In this case, the most restrictive permissions
* are chosen and assigned to both ACEs.
*/
if (!FspDistinctPermsForSameOwnerGroup && EqualSid(OwnerSid, GroupSid))
OwnerPerm = GroupPerm = OwnerPerm & GroupPerm;
/* [PERMS]
* There are situations where one or two access-denied ACEs must be added
* to the DACL. If you recall, the UNIX file access algorithm makes a
* distinction between owner, group and other such that each is unique
* and the ID used in an access request can only match one of them.
* However, the Windows file access algorithm makes no such distinction
* while scanning the DACL. If the ID in the request is granted permission
* in any of the access-allowed ACEs then the request is permitted. This
* is a problem when the owner permissions are specified to be more
* restrictive than say the group or the other permissions (eg. when a
* "chmod 577 foobar" is executed) So, to support UNIX semantics we must
* examine the permissions granted to Everyone and if they are more
* permissive than those in the group permissions then a special
* access-denied ACE must be created for the group. And similarly, if
* either the group or other permissions are more permissive than the
* owner permissions, then an access-denied ACE must be created for the owner.
*/
OwnerDeny = (OwnerPerm ^ GroupPerm) & GroupPerm;
OwnerDeny |= (OwnerPerm ^ WorldPerm) & WorldPerm;
GroupDeny = (GroupPerm ^ WorldPerm) & WorldPerm;
Size = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) * 3 +
sizeof(ACCESS_DENIED_ACE) * (!!OwnerDeny + !!GroupDeny);
Size += GetLengthSid(OwnerSid) - sizeof(DWORD);
Size += GetLengthSid(GroupSid) - sizeof(DWORD);
Size += GetLengthSid(FspWorldSid) - sizeof(DWORD);
if (OwnerDeny)
Size += GetLengthSid(OwnerSid) - sizeof(DWORD);
if (GroupDeny)
Size += GetLengthSid(GroupSid) - sizeof(DWORD);
Size += sizeof(DWORD) - 1;
Size &= ~(sizeof(DWORD) - 1);
Acl = MemAlloc(Size);
if (0 == Acl)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (!InitializeAcl(Acl, Size, ACL_REVISION))
goto lasterror;
if (!AddAccessAllowedAce(Acl, ACL_REVISION,
FspPosixOwnerDefaultPerm | FspPosixMapPermissionToAccessMask(Mode & ~001000, OwnerPerm),
OwnerSid))
goto lasterror;
if (OwnerDeny)
{
if (!AddAccessDeniedAce(Acl, ACL_REVISION,
~FILE_WRITE_ATTRIBUTES & FspPosixMapPermissionToAccessMask(Mode & ~001000, OwnerDeny),
OwnerSid))
goto lasterror;
}
if (!AddAccessAllowedAce(Acl, ACL_REVISION,
FspPosixDefaultPerm | FspPosixMapPermissionToAccessMask(Mode, GroupPerm),
GroupSid))
goto lasterror;
if (GroupDeny)
{
if (!AddAccessDeniedAce(Acl, ACL_REVISION,
FspPosixMapPermissionToAccessMask(Mode, GroupDeny),
GroupSid))
goto lasterror;
}
if (!AddAccessAllowedAce(Acl, ACL_REVISION,
FspPosixDefaultPerm | FspPosixMapPermissionToAccessMask(Mode, WorldPerm),
FspWorldSid))
goto lasterror;
if (!InitializeSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
goto lasterror;
if (!SetSecurityDescriptorControl(&SecurityDescriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED))
goto lasterror;
if (!SetSecurityDescriptorOwner(&SecurityDescriptor, OwnerSid, FALSE))
goto lasterror;
if (!SetSecurityDescriptorGroup(&SecurityDescriptor, GroupSid, FALSE))
goto lasterror;
if (!SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, Acl, 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);
MemFree(Acl);
if (0 != GroupSid)
FspDeleteSid(GroupSid, FspPosixMapUidToSid);
if (0 != OwnerSid)
FspDeleteSid(OwnerSid, FspPosixMapUidToSid);
return Result;
lasterror:
Result = FspNtStatusFromWin32(GetLastError());
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();
if (AccessMask & GENERIC_READ)
AccessMask |= Mapping->GenericRead;
if (AccessMask & GENERIC_WRITE)
AccessMask |= Mapping->GenericWrite;
if (AccessMask & GENERIC_EXECUTE)
AccessMask |= Mapping->GenericExecute;
if (AccessMask & GENERIC_ALL)
AccessMask |= Mapping->GenericAll;
return AccessMask & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}
static inline UINT32 FspPosixMapAccessMaskToPermission(ACCESS_MASK AccessMask)
{
/* [PERMS]
* Once all the granted Windows access right bits have been collected,
* then the UNIX permission bits are assembled. For each class, if the
* Read_Data bit is granted, then the corresponding "r" permission bit
* is set. If both the Write_Data and Append_Data access rights are
* granted then the "w" permission bit is set. And finally, if the
* Execute access right is granted, then the "x" permission bit is set.
*/
return
((AccessMask & FILE_READ_DATA) ? 4 : 0) |
((FILE_WRITE_DATA | FILE_APPEND_DATA) ==
(AccessMask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ? 2 : 0) |
((AccessMask & FILE_EXECUTE) ? 1 : 0);
}
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
PSECURITY_DESCRIPTOR SecurityDescriptor,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode)
{
FSP_KU_CODE;
BOOLEAN OwnerOptional = (UINT_PTR)PUid & 1;
PUid = (PVOID)((UINT_PTR)PUid & ~1);
UINT32 OrigUid = *PUid;
BOOLEAN GroupOptional = (UINT_PTR)PGid & 1;
PGid = (PVOID)((UINT_PTR)PGid & ~1);
UINT32 OrigGid = *PGid;
PSID OwnerSid = 0, GroupSid = 0;
BOOL Defaulted, DaclPresent;
PACL Acl = 0;
ACL_SIZE_INFORMATION AclSizeInfo;
PACE_HEADER Ace;
PSID AceSid;
DWORD AceAccessMask;
DWORD OwnerAllow, OwnerDeny, GroupAllow, GroupDeny, WorldAllow, WorldDeny;
UINT32 AceUid = 0;
UINT32 Uid, Gid, Mode;
NTSTATUS Result;
*PUid = 0;
*PGid = 0;
*PMode = 0;
if (!GetSecurityDescriptorOwner(SecurityDescriptor, &OwnerSid, &Defaulted))
goto lasterror;
if (!GetSecurityDescriptorGroup(SecurityDescriptor, &GroupSid, &Defaulted))
goto lasterror;
if (!GetSecurityDescriptorDacl(SecurityDescriptor, &DaclPresent, &Acl, &Defaulted))
goto lasterror;
if (0 == OwnerSid && OwnerOptional)
Uid = OrigUid;
else
{
Result = FspPosixMapSidToUid(OwnerSid, &Uid);
if (!NT_SUCCESS(Result))
goto exit;
}
if (0 == GroupSid && GroupOptional)
Gid = OrigGid;
else
{
Result = FspPosixMapSidToUid(GroupSid, &Gid);
if (!NT_SUCCESS(Result))
goto exit;
}
if (0 != Acl)
{
OwnerAllow = OwnerDeny = GroupAllow = GroupDeny = WorldAllow = WorldDeny = 0;
if (!GetAclInformation(Acl, &AclSizeInfo, sizeof AclSizeInfo, AclSizeInformation))
goto lasterror;
/* [PERMS]
* For each of the ACEs in the file's DACL
*/
for (ULONG Index = 0; AclSizeInfo.AceCount > Index; Index++)
{
if (!GetAce(Acl, Index, &Ace))
goto lasterror;
/* [PERMS]
* Ignore the ACE if it is not an access-denied or access-allowed type.
*/
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
{
AceSid = &((PACCESS_ALLOWED_ACE)Ace)->SidStart;
AceAccessMask = ((PACCESS_ALLOWED_ACE)Ace)->Mask;
}
else if (ACCESS_DENIED_ACE_TYPE == Ace->AceType)
{
AceSid = &((PACCESS_DENIED_ACE)Ace)->SidStart;
AceAccessMask = ((PACCESS_DENIED_ACE)Ace)->Mask;
}
else
continue;
AceAccessMask = FspPosixCanonicalizeAccessMask(AceAccessMask);
/* [PERMS]
* If the ACE contains the Authenticated Users SID or the World SID then
* add the allowed or denied access right bits into the "owner", "group"
* and "other" collections.
*/
if (EqualSid(FspWorldSid, AceSid) || EqualSid(FspAuthUsersSid, AceSid))
{
/* [PERMS]
* If this is an access-denied ACE, then add each access right to the set
* of denied rights in each collection but only if the access right is not
* already present in the set of granted rights in that collection. Similarly
* If this is an access-allowed ACE, then add each access right to the set
* of granted rights in each collection but only if the access right is not
* already present in the set of denied rights in that collection.
*/
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
{
WorldAllow |= AceAccessMask & ~WorldDeny;
GroupAllow |= AceAccessMask & ~GroupDeny;
OwnerAllow |= AceAccessMask & ~OwnerDeny;
}
else //if (ACCESS_DENIED_ACE_TYPE == Ace->AceType)
{
WorldDeny |= AceAccessMask & ~WorldAllow;
GroupDeny |= AceAccessMask & ~GroupAllow;
OwnerDeny |= AceAccessMask & ~OwnerAllow;
}
}
else
{
if (0 == OwnerSid || 0 == GroupSid)
FspPosixMapSidToUid(AceSid, &AceUid);
/* [PERMS]
* Note that if the file owner and file group SIDs are the same,
* then the access rights are saved in both the "owner" and "group"
* collections.
*/
/* [PERMS]
* If the ACE contains the file's group SID, then save the access rights
* in the "group" collection as appropriate in the corresponding set of
* granted or denied rights (as described above).
*/
if (0 != GroupSid ? EqualSid(GroupSid, AceSid) : (Gid == AceUid))
{
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
GroupAllow |= AceAccessMask & ~GroupDeny;
else //if (ACCESS_DENIED_ACE_TYPE == Ace->AceType)
GroupDeny |= AceAccessMask & ~GroupAllow;
}
/* [PERMS]
* If the ACE contains the file's owner SID, then save the access rights
* in the "owner" collection as appropriate in the corresponding set of
* granted or denied rights (as described above).
*/
if (0 != OwnerSid ? EqualSid(OwnerSid, AceSid) : (Uid == AceUid))
{
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
OwnerAllow |= AceAccessMask & ~OwnerDeny;
else //if (ACCESS_DENIED_ACE_TYPE == Ace->AceType)
OwnerDeny |= AceAccessMask & ~OwnerAllow;
}
}
}
Mode =
(FspPosixMapAccessMaskToPermission(OwnerAllow) << 6) |
(FspPosixMapAccessMaskToPermission(GroupAllow) << 3) |
(FspPosixMapAccessMaskToPermission(WorldAllow));
if (0 != (OwnerAllow & FILE_DELETE_CHILD) &&
(
(0 == (GroupAllow & FILE_DELETE_CHILD) && 0 != (Mode & 0000020)) ||
(0 == (WorldAllow & FILE_DELETE_CHILD) && 0 != (Mode & 0000002))
))
Mode |= 0001000; /* sticky bit */
}
else
Mode = 0777;
*PUid = Uid;
*PGid = Gid;
*PMode = Mode;
Result = STATUS_SUCCESS;
exit:
return Result;
lasterror:
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
/*
* Services for Macintosh and Cygwin compatible filename transformation:
* Transform characters invalid for Windows filenames to the Unicode
* private use area in the U+F0XX range.
*
* The invalid maps are produced by the following Python script:
* reserved = ['<', '>', ':', '"', '\\', '|', '?', '*']
* l = [str(int(0 < i < 32 or chr(i) in reserved)) for i in xrange(0, 128)]
* print "0x%08x" % int("".join(l[0:32]), 2)
* print "0x%08x" % int("".join(l[32:64]), 2)
* print "0x%08x" % int("".join(l[64:96]), 2)
* print "0x%08x" % int("".join(l[96:128]), 2)
*/
static UINT32 FspPosixInvalidPathChars[4] =
{
0x7fffffff,
0x2020002b,
0x00000008,
0x00000008,
};
FSP_API NTSTATUS FspPosixMapWindowsToPosixPathEx(PWSTR WindowsPath, char **PPosixPath,
BOOLEAN Translate)
{
FSP_KU_CODE;
NTSTATUS Result;
ULONG Size;
char *PosixPath = 0, *p, *q;
*PPosixPath = 0;
Size = WideCharToMultiByte(CP_UTF8, 0, WindowsPath, -1, 0, 0, 0, 0);
if (0 == Size)
goto lasterror;
PosixPath = MemAlloc(Size);
if (0 == PosixPath)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Size = WideCharToMultiByte(CP_UTF8, 0, WindowsPath, -1, PosixPath, Size, 0, 0);
if (0 == Size)
goto lasterror;
if (Translate)
{
for (p = PosixPath, q = p; *p; p++)
{
unsigned char c = *p;
if ('\\' == c)
*q++ = '/';
/* encode characters in the Unicode private use area: U+F0XX -> XX */
else if (0xef == c && 0x80 == (0xfc & p[1]) && 0x80 == (0xc0 & p[2]))
{
c = ((p[1] & 0x3) << 6) | (p[2] & 0x3f);
if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*q++ = c, p += 2;
else
*q++ = *p++, *q++ = *p++, *q++ = *p;
}
else
*q++ = c;
}
*q = '\0';
}
*PPosixPath = PosixPath;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
MemFree(PosixPath);
return Result;
lasterror:
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
FSP_API NTSTATUS FspPosixMapPosixToWindowsPathEx(const char *PosixPath, PWSTR *PWindowsPath,
BOOLEAN Translate)
{
FSP_KU_CODE;
NTSTATUS Result;
ULONG Size;
PWSTR WindowsPath = 0, p;
*PWindowsPath = 0;
Size = MultiByteToWideChar(CP_UTF8, 0, PosixPath, -1, 0, 0);
if (0 == Size)
goto lasterror;
WindowsPath = MemAlloc(Size * sizeof(WCHAR));
if (0 == PosixPath)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
Size = MultiByteToWideChar(CP_UTF8, 0, PosixPath, -1, WindowsPath, Size);
if (0 == Size)
goto lasterror;
if (Translate)
{
for (p = WindowsPath; *p; p++)
{
WCHAR c = *p;
if (L'/' == c)
*p = L'\\';
else if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*p |= 0xf000;
}
}
*PWindowsPath = WindowsPath;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
MemFree(WindowsPath);
return Result;
lasterror:
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
FSP_API VOID FspPosixDeletePath(void *Path)
{
FSP_KU_CODE;
MemFree(Path);
}
FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size)
{
FSP_KU_CODE;
for (PWSTR p = WindowsPath, endp = p + Size; endp > p; p++)
{
WCHAR c = *p;
if (L'\\' == c)
*p = L'/';
/* encode characters in the Unicode private use area: U+F0XX -> XX */
else if (0xf000 <= c && c <= 0xf0ff)
*p &= ~0xf000;
}
}
FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size)
{
FSP_KU_CODE;
for (PWSTR p = WindowsPath, endp = p + Size; endp > p; p++)
{
WCHAR c = *p;
if (L'/' == c)
*p = L'\\';
else if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*p |= 0xf000;
}
}