mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 01:12:58 -05:00
src: ku: posix.c
- src/ku directory contains shared kernel/user mode code
This commit is contained in:
956
src/dll/posix.c
956
src/dll/posix.c
@ -1,956 +0,0 @@
|
||||
/**
|
||||
* @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:
|
||||
*
|
||||
* [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-2019 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 <dll/library.h>
|
||||
#include <aclapi.h>
|
||||
#define _NTDEF_
|
||||
#include <ntsecapi.h>
|
||||
|
||||
static PISID FspPosixCreateSid(BYTE Authority, ULONG Count, ...);
|
||||
|
||||
static INIT_ONCE FspPosixInitOnce = INIT_ONCE_STATIC_INIT;
|
||||
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,
|
||||
};
|
||||
static PISID FspAccountDomainSid, FspPrimaryDomainSid;
|
||||
|
||||
#define FspUnmappedSid (&FspUnmappedSidBuf.V)
|
||||
#define FspUnmappedUid (65534)
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid)
|
||||
{
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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(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((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)
|
||||
{
|
||||
InitOnceExecuteOnce(&FspPosixInitOnce, FspPosixInitialize, 0, 0);
|
||||
|
||||
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 (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.
|
||||
*/
|
||||
|
||||
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 (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, ...)
|
||||
{
|
||||
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 (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)
|
||||
{
|
||||
PSID OwnerSid = 0, GroupSid = 0, WorldSid = 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;
|
||||
|
||||
WorldSid = FspWksidGet(WinWorldSid);
|
||||
if (0 == WorldSid)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
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 (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(WorldSid) - 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),
|
||||
WorldSid))
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
PSID OwnerSid = 0, GroupSid = 0, WorldSid = 0, AuthUsersSid = 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 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;
|
||||
|
||||
Result = FspPosixMapSidToUid(OwnerSid, &Uid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Result = FspPosixMapSidToUid(GroupSid, &Gid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
if (0 != Acl)
|
||||
{
|
||||
WorldSid = FspWksidGet(WinWorldSid);
|
||||
if (0 == WorldSid)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
AuthUsersSid = FspWksidGet(WinAuthenticatedUserSid);
|
||||
if (0 == AuthUsersSid)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* [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(WorldSid, AceSid) || EqualSid(AuthUsersSid, 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
|
||||
{
|
||||
/* [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 (EqualSid(GroupSid, AceSid))
|
||||
{
|
||||
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 (EqualSid(OwnerSid, AceSid))
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
MemFree(Path);
|
||||
}
|
||||
|
||||
FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user