854 lines
20 KiB
C
854 lines
20 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1989 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
psxsup.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
PSX Support Routines
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Mark Lucovsky (markl) 27-Nov-1989
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "psxsrv.h"
|
||
|
#include <ntlsa.h>
|
||
|
#include <ntsam.h>
|
||
|
#include <ntseapi.h>
|
||
|
#include "sesport.h"
|
||
|
|
||
|
#include "seposix.h"
|
||
|
|
||
|
#define UNICODE
|
||
|
#include <windows.h>
|
||
|
#include <lm.h>
|
||
|
#include <lmaccess.h>
|
||
|
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
//
|
||
|
// This number will never be returned in the PosixOffset field of
|
||
|
// a trusted domain query.
|
||
|
//
|
||
|
#define INVALID_POSIX_OFFSET 1
|
||
|
|
||
|
ULONG
|
||
|
PsxStatusToErrno(
|
||
|
IN NTSTATUS Status
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This procedure converts an NT status code to an
|
||
|
equivalent errno value.
|
||
|
|
||
|
The conversion is a function of the status code class.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Class - Supplies the status code class to use.
|
||
|
|
||
|
Status - Supplies the status code to convert.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns an equivalent error code to the supplied status code.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ULONG Error;
|
||
|
|
||
|
switch (Status) {
|
||
|
|
||
|
case STATUS_INVALID_PARAMETER:
|
||
|
Error = EINVAL;
|
||
|
break;
|
||
|
|
||
|
case STATUS_DIRECTORY_NOT_EMPTY:
|
||
|
// Error = ENOTEMPTY;
|
||
|
Error = EEXIST;
|
||
|
break;
|
||
|
|
||
|
case STATUS_OBJECT_PATH_INVALID:
|
||
|
case STATUS_OBJECT_PATH_SYNTAX_BAD:
|
||
|
case STATUS_NOT_A_DIRECTORY:
|
||
|
Error = ENOTDIR;
|
||
|
break;
|
||
|
|
||
|
case STATUS_OBJECT_NAME_COLLISION:
|
||
|
Error = EEXIST;
|
||
|
break;
|
||
|
|
||
|
case STATUS_OBJECT_PATH_NOT_FOUND:
|
||
|
case STATUS_OBJECT_NAME_NOT_FOUND:
|
||
|
case STATUS_DELETE_PENDING:
|
||
|
case STATUS_NO_SUCH_FILE:
|
||
|
Error = ENOENT;
|
||
|
break;
|
||
|
|
||
|
case STATUS_NO_MEMORY:
|
||
|
case STATUS_INSUFFICIENT_RESOURCES:
|
||
|
Error = ENOMEM;
|
||
|
break;
|
||
|
|
||
|
case STATUS_CANNOT_DELETE:
|
||
|
Error = ETXTBUSY;
|
||
|
break;
|
||
|
|
||
|
case STATUS_DISK_FULL:
|
||
|
Error = ENOSPC;
|
||
|
break;
|
||
|
|
||
|
case STATUS_MEDIA_WRITE_PROTECTED:
|
||
|
Error = EROFS;
|
||
|
break;
|
||
|
|
||
|
case STATUS_OBJECT_NAME_INVALID:
|
||
|
Error = ENAMETOOLONG;
|
||
|
break;
|
||
|
|
||
|
case STATUS_FILE_IS_A_DIRECTORY:
|
||
|
Error = EISDIR;
|
||
|
break;
|
||
|
|
||
|
case STATUS_NOT_SAME_DEVICE:
|
||
|
Error = EXDEV;
|
||
|
break;
|
||
|
|
||
|
case STATUS_INVALID_OWNER:
|
||
|
Error = EPERM;
|
||
|
break;
|
||
|
|
||
|
case STATUS_INVALID_IMAGE_FORMAT:
|
||
|
case STATUS_INVALID_IMAGE_LE_FORMAT:
|
||
|
case STATUS_INVALID_IMAGE_NOT_MZ:
|
||
|
case STATUS_INVALID_IMAGE_PROTECT:
|
||
|
case STATUS_INVALID_IMAGE_WIN_16:
|
||
|
Error = ENOEXEC;
|
||
|
break;
|
||
|
|
||
|
case STATUS_NOT_IMPLEMENTED:
|
||
|
Error = ENOSYS;
|
||
|
break;
|
||
|
|
||
|
case STATUS_TOO_MANY_LINKS:
|
||
|
Error = EMLINK;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Error = EACCES;
|
||
|
}
|
||
|
|
||
|
return Error;
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
PsxStatusToErrnoPath(
|
||
|
IN PUNICODE_STRING Path
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This procedure is called when an NtOpenFile returns
|
||
|
STATUS_OBJECT_PATH_NOT_FOUND; this routine is to
|
||
|
distinguish the following too cases:
|
||
|
|
||
|
/file.c/foo, where file.c exists (ENOTDIR)
|
||
|
/noent/foo, where noent doesn't exist (ENOENT)
|
||
|
|
||
|
(NtOpenFile returns OBJECT_PATH_NOT_FOUND for both cases).
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Path - Supplies the path that was given to NtOpenFile. The
|
||
|
path string is destroyed by this function.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Returns an equivalent error code to the supplied status code.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
OBJECT_ATTRIBUTES Obj;
|
||
|
HANDLE FileHandle;
|
||
|
ULONG DesiredAccess;
|
||
|
IO_STATUS_BLOCK Iosb;
|
||
|
ULONG Options;
|
||
|
PWCHAR pwc, pwcSav;
|
||
|
ULONG MinLen;
|
||
|
|
||
|
PSX_GET_SIZEOF(DOSDEVICE_X_W,MinLen);
|
||
|
|
||
|
DesiredAccess = SYNCHRONIZE;
|
||
|
Options = FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE;
|
||
|
|
||
|
pwcSav = NULL;
|
||
|
|
||
|
for (;;) {
|
||
|
//
|
||
|
// Remove a trailing component.
|
||
|
//
|
||
|
|
||
|
pwc = wcsrchr(Path->Buffer, L'\\');
|
||
|
|
||
|
if (pwcSav)
|
||
|
*pwcSav = L'\\';
|
||
|
|
||
|
if (NULL == pwc) {
|
||
|
break;
|
||
|
}
|
||
|
*pwc = UNICODE_NULL;
|
||
|
pwcSav = pwc;
|
||
|
|
||
|
Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
|
||
|
|
||
|
if (Path->Length <= MinLen) {
|
||
|
*pwcSav = L'\\';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
InitializeObjectAttributes(&Obj, Path, 0, NULL, NULL);
|
||
|
|
||
|
Status = NtOpenFile(&FileHandle, DesiredAccess, &Obj,
|
||
|
&Iosb, SHARE_ALL, Options);
|
||
|
if (NT_SUCCESS(Status)) {
|
||
|
NtClose(FileHandle);
|
||
|
}
|
||
|
|
||
|
if (STATUS_NOT_A_DIRECTORY == Status) {
|
||
|
*pwcSav = L'\\';
|
||
|
Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
|
||
|
return ENOTDIR;
|
||
|
}
|
||
|
}
|
||
|
Path->Length = wcslen(Path->Buffer) * sizeof(WCHAR);
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
PsxDetermineFileClass(
|
||
|
IN HANDLE FileHandle
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function examines a file handle and returns its FileClass
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
FileHandle - Supplies a handle to an open file whose class is to be
|
||
|
determined.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
The file class of the specified file. Defined in <sys/stat.h>.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
NTSTATUS st;
|
||
|
IO_STATUS_BLOCK Iosb;
|
||
|
FILE_BASIC_INFORMATION BasicInfo;
|
||
|
FILE_FS_DEVICE_INFORMATION DeviceInfo;
|
||
|
|
||
|
//
|
||
|
// Call NtQueryFile to get device type and attributes
|
||
|
//
|
||
|
|
||
|
st = NtQueryInformationFile(
|
||
|
FileHandle,
|
||
|
&Iosb,
|
||
|
&BasicInfo,
|
||
|
sizeof(BasicInfo),
|
||
|
FileBasicInformation
|
||
|
);
|
||
|
if (!NT_SUCCESS(st)) {
|
||
|
// XXX.mjb: Sometimes fails on HPFS
|
||
|
KdPrint(("PSXS: PsxDetermineFileClass: NtQueryInfoFile: 0x%x\n", st));
|
||
|
return S_IFREG;
|
||
|
}
|
||
|
|
||
|
st = NtQueryVolumeInformationFile(
|
||
|
FileHandle,
|
||
|
&Iosb,
|
||
|
&DeviceInfo,
|
||
|
sizeof(DeviceInfo),
|
||
|
FileFsDeviceInformation
|
||
|
);
|
||
|
|
||
|
ASSERT(NT_SUCCESS(st));
|
||
|
|
||
|
switch (DeviceInfo.DeviceType) {
|
||
|
|
||
|
case FILE_DEVICE_DATALINK:
|
||
|
case FILE_DEVICE_KEYBOARD:
|
||
|
case FILE_DEVICE_MOUSE:
|
||
|
case FILE_DEVICE_NETWORK:
|
||
|
case FILE_DEVICE_NULL:
|
||
|
case FILE_DEVICE_PHYSICAL_NETCARD:
|
||
|
case FILE_DEVICE_PARALLEL_PORT:
|
||
|
case FILE_DEVICE_PRINTER:
|
||
|
case FILE_DEVICE_SOUND:
|
||
|
case FILE_DEVICE_SCREEN:
|
||
|
case FILE_DEVICE_SERIAL_PORT:
|
||
|
case FILE_DEVICE_TRANSPORT:
|
||
|
return S_IFCHR;
|
||
|
|
||
|
case FILE_DEVICE_DFS:
|
||
|
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
||
|
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
|
||
|
return S_IFBLK;
|
||
|
|
||
|
case FILE_DEVICE_DISK:
|
||
|
case FILE_DEVICE_VIRTUAL_DISK:
|
||
|
case FILE_DEVICE_TAPE:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
// return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The only thing left is RegularFile class. Now
|
||
|
// determine if this is a directory, named pipe,
|
||
|
// or regular file.
|
||
|
//
|
||
|
|
||
|
if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
return S_IFDIR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For now, anything marked as a system file is a named pipe.
|
||
|
// In the future, this will involve checking to see if file
|
||
|
// has the Object Bit and has the appropriate EA (named pipe
|
||
|
// class id).
|
||
|
//
|
||
|
|
||
|
if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_SYSTEM) {
|
||
|
return S_IFIFO;
|
||
|
}
|
||
|
|
||
|
return S_IFREG;
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
EndImpersonation(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
HANDLE Handle;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
Handle = NULL;
|
||
|
Status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken,
|
||
|
(PVOID)&Handle, sizeof(HANDLE));
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// MakePosixId -- convert the given SID into a Posix Id. This basically
|
||
|
// means find the Posix Offset and add it to the last sub-authority
|
||
|
// in the SID. The Posix Id is returned.
|
||
|
//
|
||
|
|
||
|
uid_t
|
||
|
MakePosixId(PSID Sid)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
LSA_HANDLE
|
||
|
PolicyHandle,
|
||
|
TrustedDomainHandle;
|
||
|
PTRUSTED_POSIX_OFFSET_INFO
|
||
|
pPosixOff;
|
||
|
OBJECT_ATTRIBUTES
|
||
|
Obj;
|
||
|
SECURITY_QUALITY_OF_SERVICE
|
||
|
SecurityQoS;
|
||
|
CHAR buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
||
|
PSECURITY_DESCRIPTOR
|
||
|
SecurityDescriptor = (PVOID)buf;
|
||
|
PSID DomainSid;
|
||
|
ULONG RelativeId, offset;
|
||
|
UNICODE_STRING
|
||
|
DCName,
|
||
|
Domain_U;
|
||
|
PPOLICY_ACCOUNT_DOMAIN_INFO
|
||
|
AccountDomainInfo;
|
||
|
PPOLICY_PRIMARY_DOMAIN_INFO
|
||
|
PrimaryDomainInfo;
|
||
|
UCHAR SubAuthCount;
|
||
|
LPBYTE netbuf;
|
||
|
ULONG i;
|
||
|
|
||
|
SubAuthCount = *RtlSubAuthorityCountSid(Sid);
|
||
|
RelativeId = *RtlSubAuthoritySid(Sid, SubAuthCount - 1);
|
||
|
|
||
|
//
|
||
|
// Map S-1-5-5-X-Y to Id 0xFFF
|
||
|
//
|
||
|
|
||
|
if (3 == SubAuthCount &&
|
||
|
5 == RtlIdentifierAuthoritySid(Sid)->Value[5] &&
|
||
|
5 == *RtlSubAuthoritySid(Sid, 0)) {
|
||
|
return 0xFFF;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First copy the given Sid to a Sid for that domain.
|
||
|
//
|
||
|
|
||
|
DomainSid = RtlAllocateHeap(PsxHeap, 0, RtlLengthSid(Sid));
|
||
|
if (NULL == DomainSid) {
|
||
|
KdPrint(("PSXSS: MakePosixId: no memory\n"));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Status = RtlCopySid(RtlLengthSid(Sid), DomainSid, Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
|
||
|
--*RtlSubAuthorityCountSid(DomainSid);
|
||
|
|
||
|
//
|
||
|
// See if the offset for the domain is already known.
|
||
|
//
|
||
|
|
||
|
if (INVALID_POSIX_OFFSET != (offset = GetOffsetBySid(DomainSid))) {
|
||
|
// XXX.mjb: close handles, free memory.
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
return (offset | RelativeId);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If the Domain part of the passed-in Sid is our account domain,
|
||
|
// then the offset is known.
|
||
|
//
|
||
|
|
||
|
SecurityQoS.ImpersonationLevel = SecurityIdentification;
|
||
|
SecurityQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
||
|
SecurityQoS.EffectiveOnly = TRUE;
|
||
|
|
||
|
InitializeObjectAttributes(&Obj, NULL, 0, NULL, NULL);
|
||
|
Obj.SecurityQualityOfService = &SecurityQoS;
|
||
|
|
||
|
Status = LsaOpenPolicy(NULL, &Obj, GENERIC_EXECUTE, &PolicyHandle);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
KdPrint(("PSXSS: Can't open policy: 0x%x\n", Status));
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Status = LsaQueryInformationPolicy(PolicyHandle,
|
||
|
PolicyAccountDomainInformation, (PVOID *)&AccountDomainInfo);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
KdPrint(("PSXSS: Can't query info policy: 0x%x\n", Status));
|
||
|
return 0;
|
||
|
}
|
||
|
ASSERT(NULL != AccountDomainInfo->DomainSid);
|
||
|
|
||
|
if (RtlEqualSid(AccountDomainInfo->DomainSid, DomainSid)) {
|
||
|
MapSidToOffset(DomainSid, SE_ACCOUNT_DOMAIN_POSIX_OFFSET);
|
||
|
LsaFreeMemory(AccountDomainInfo);
|
||
|
LsaClose(PolicyHandle);
|
||
|
return RelativeId | SE_ACCOUNT_DOMAIN_POSIX_OFFSET;
|
||
|
}
|
||
|
LsaFreeMemory(AccountDomainInfo);
|
||
|
|
||
|
Status = LsaQueryInformationPolicy(PolicyHandle,
|
||
|
PolicyPrimaryDomainInformation, (PVOID *)&PrimaryDomainInfo);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
|
||
|
if (NULL == PrimaryDomainInfo->Sid) {
|
||
|
//
|
||
|
// This machine does not have a primary domain, and the
|
||
|
// sid we're mapping does not belong to the account domain
|
||
|
// and is not a well-known sid.
|
||
|
//
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
LsaFreeMemory(PrimaryDomainInfo);
|
||
|
LsaClose(PolicyHandle);
|
||
|
return RelativeId;
|
||
|
}
|
||
|
|
||
|
if (NULL != PrimaryDomainInfo->Sid &&
|
||
|
RtlEqualSid(PrimaryDomainInfo->Sid, DomainSid)) {
|
||
|
MapSidToOffset(DomainSid, SE_PRIMARY_DOMAIN_POSIX_OFFSET);
|
||
|
LsaFreeMemory(PrimaryDomainInfo);
|
||
|
LsaClose(PolicyHandle);
|
||
|
return RelativeId | SE_PRIMARY_DOMAIN_POSIX_OFFSET;
|
||
|
}
|
||
|
|
||
|
Status = NetGetAnyDCName(NULL,
|
||
|
PrimaryDomainInfo->Name.Buffer,
|
||
|
&netbuf);
|
||
|
if (Status != ERROR_SUCCESS) {
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
LsaFreeMemory(PrimaryDomainInfo);
|
||
|
LsaClose(PolicyHandle);
|
||
|
return RelativeId;
|
||
|
}
|
||
|
DCName.Buffer = (PVOID)netbuf;
|
||
|
NetApiBufferSize(netbuf, (LPDWORD)&DCName.MaximumLength);
|
||
|
DCName.Length = wcslen((PWCHAR)netbuf) * sizeof(WCHAR);
|
||
|
|
||
|
LsaClose(PolicyHandle);
|
||
|
|
||
|
Status = LsaOpenPolicy(&DCName, &Obj, GENERIC_EXECUTE, &PolicyHandle);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
KdPrint(("PSXSS: Can't open policy on DC %wZ: 0x%x\n", &DCName,
|
||
|
Status));
|
||
|
LsaFreeMemory(PrimaryDomainInfo);
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
return RelativeId;
|
||
|
}
|
||
|
|
||
|
NetApiBufferFree(netbuf);
|
||
|
LsaFreeMemory(PrimaryDomainInfo);
|
||
|
|
||
|
Status = LsaOpenTrustedDomain(PolicyHandle, DomainSid, GENERIC_EXECUTE,
|
||
|
&TrustedDomainHandle);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
KdPrint(("PSXSS: Can't open trusted domain\n"));
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
return RelativeId;
|
||
|
}
|
||
|
|
||
|
Status = LsaQueryInfoTrustedDomain(TrustedDomainHandle,
|
||
|
TrustedPosixOffsetInformation,
|
||
|
(PVOID)&pPosixOff);
|
||
|
if (!NT_SUCCESS(Status)) {
|
||
|
KdPrint(("PSXSS: Can't query Posix offset info: 0x%x\n",
|
||
|
Status));
|
||
|
LsaClose(PolicyHandle);
|
||
|
LsaClose(TrustedDomainHandle);
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
return RelativeId;
|
||
|
}
|
||
|
|
||
|
offset = pPosixOff->Offset;
|
||
|
|
||
|
LsaFreeMemory(pPosixOff);
|
||
|
|
||
|
if (offset & 0xFFFF) {
|
||
|
KdPrint(("PSXSS: bad PsxOffset 0x%x\n", offset));
|
||
|
RtlFreeHeap(PsxHeap, 0, (PVOID)DomainSid);
|
||
|
LsaClose(TrustedDomainHandle);
|
||
|
LsaClose(PolicyHandle);
|
||
|
offset = 0;
|
||
|
}
|
||
|
|
||
|
ASSERT(INVALID_POSIX_OFFSET != offset);
|
||
|
|
||
|
MapSidToOffset(DomainSid, offset);
|
||
|
|
||
|
//
|
||
|
// Do not free DomainSid -- there is still a reference to it in
|
||
|
// the Sid-Offset cache (put there by MapSidToOffset).
|
||
|
//
|
||
|
|
||
|
LsaClose(PolicyHandle);
|
||
|
LsaClose(TrustedDomainHandle);
|
||
|
|
||
|
return offset | RelativeId;
|
||
|
}
|
||
|
|
||
|
typedef struct _SID_AND_OFFSET {
|
||
|
LIST_ENTRY Links;
|
||
|
PSID Sid;
|
||
|
ULONG Offset;
|
||
|
} SID_AND_OFFSET, *PSID_AND_OFFSET;
|
||
|
|
||
|
LIST_ENTRY SidList;
|
||
|
RTL_CRITICAL_SECTION SidListMutex;
|
||
|
|
||
|
//
|
||
|
// GetOffsetBySid -- search the SidList for the given Sid. If we've
|
||
|
// encountered this domain before, we'll know the Posix offset,
|
||
|
// which is returned. If not, INVALID_POSIX_OFFSET is returned.
|
||
|
//
|
||
|
ULONG
|
||
|
GetOffsetBySid(PSID Sid)
|
||
|
{
|
||
|
PSID_AND_OFFSET pSO;
|
||
|
ULONG Offset = INVALID_POSIX_OFFSET;
|
||
|
|
||
|
RtlEnterCriticalSection(&SidListMutex);
|
||
|
|
||
|
for (pSO = (PSID_AND_OFFSET)SidList.Flink;
|
||
|
pSO != (PSID_AND_OFFSET)&SidList;
|
||
|
pSO = (PSID_AND_OFFSET)pSO->Links.Flink) {
|
||
|
if (RtlEqualSid(Sid, pSO->Sid)) {
|
||
|
Offset = pSO->Offset;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RtlLeaveCriticalSection(&SidListMutex);
|
||
|
|
||
|
return Offset;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// GetSidByOffset -- search the SidList for the given offset. Called in
|
||
|
// the process of converting a uid or gid to a Sid, as in getpwuid().
|
||
|
//
|
||
|
PSID
|
||
|
GetSidByOffset(ULONG Offset)
|
||
|
{
|
||
|
PSID_AND_OFFSET pSO;
|
||
|
PSID Sid = NULL;
|
||
|
|
||
|
RtlEnterCriticalSection(&SidListMutex);
|
||
|
|
||
|
for (pSO = (PSID_AND_OFFSET)SidList.Flink;
|
||
|
pSO != (PSID_AND_OFFSET)&SidList;
|
||
|
pSO = (PSID_AND_OFFSET)pSO->Links.Flink) {
|
||
|
if (Offset == pSO->Offset) {
|
||
|
Sid = pSO->Sid;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RtlLeaveCriticalSection(&SidListMutex);
|
||
|
|
||
|
return Sid;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// MapSidToOffset -- add the given sid and offset to the cache. If there's
|
||
|
// an error, like no memory, it's simply dropped on the floor.
|
||
|
//
|
||
|
VOID
|
||
|
MapSidToOffset(PSID Sid, ULONG Offset)
|
||
|
{
|
||
|
PSID_AND_OFFSET pSO;
|
||
|
|
||
|
pSO = RtlAllocateHeap(PsxHeap, 0, sizeof(*pSO));
|
||
|
if (NULL == pSO) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pSO->Sid = Sid;
|
||
|
pSO->Offset = Offset;
|
||
|
|
||
|
RtlEnterCriticalSection(&SidListMutex);
|
||
|
|
||
|
InsertHeadList(&SidList, &pSO->Links);
|
||
|
|
||
|
RtlLeaveCriticalSection(&SidListMutex);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// InitSidList -- do initialization, including mapping special Sids to
|
||
|
// their appropriate offsets. No locking is done, assumed to be
|
||
|
// called in a single-threaded way.
|
||
|
//
|
||
|
VOID
|
||
|
InitSidList(VOID)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
PSID Sid;
|
||
|
SID_IDENTIFIER_AUTHORITY
|
||
|
AuthSec = SECURITY_NT_AUTHORITY,
|
||
|
Auth0 = SECURITY_NULL_SID_AUTHORITY,
|
||
|
Auth1 = SECURITY_WORLD_SID_AUTHORITY,
|
||
|
Auth2 = SECURITY_LOCAL_SID_AUTHORITY,
|
||
|
Auth3 = SECURITY_CREATOR_SID_AUTHORITY,
|
||
|
Auth4 = SECURITY_NON_UNIQUE_AUTHORITY,
|
||
|
Auth5 = SECURITY_NT_AUTHORITY;
|
||
|
|
||
|
RtlInitializeCriticalSection(&SidListMutex);
|
||
|
InitializeListHead(&SidList);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_NULL_POSIX_ID);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth1, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_WORLD_POSIX_ID);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth2, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_LOCAL_POSIX_ID);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth3, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_CREATOR_OWNER_POSIX_ID);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth4, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_NON_UNIQUE_POSIX_ID);
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&Auth5, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_AUTHORITY_POSIX_ID);
|
||
|
|
||
|
//
|
||
|
// "Builtin" domain has known offset.
|
||
|
//
|
||
|
|
||
|
Status = RtlAllocateAndInitializeSid(&AuthSec, 1,
|
||
|
SECURITY_BUILTIN_DOMAIN_RID, 0, 0, 0, 0, 0, 0, 0, &Sid);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
MapSidToOffset(Sid, SE_BUILT_IN_DOMAIN_POSIX_OFFSET);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// AccessMaskToMode -- convert a set of NT ACCESS_MASKS to the POSIX
|
||
|
// mode_t.
|
||
|
//
|
||
|
mode_t
|
||
|
AccessMaskToMode(
|
||
|
ACCESS_MASK UserAccess,
|
||
|
ACCESS_MASK GroupAccess,
|
||
|
ACCESS_MASK OtherAccess
|
||
|
)
|
||
|
{
|
||
|
mode_t Mode = 0;
|
||
|
int i;
|
||
|
PACCESS_MASK pAM;
|
||
|
|
||
|
//
|
||
|
// Make sure that if a GENERIC_ACCESS is set, we notice that
|
||
|
// the mask implies FILE_GENERIC_ACCESS.
|
||
|
//
|
||
|
|
||
|
for (i = 0; i < 3; ++i) {
|
||
|
switch (i) {
|
||
|
case 0:
|
||
|
pAM = &UserAccess;
|
||
|
break;
|
||
|
case 1:
|
||
|
pAM = &GroupAccess;
|
||
|
break;
|
||
|
case 2:
|
||
|
pAM = &OtherAccess;
|
||
|
break;
|
||
|
}
|
||
|
if (*pAM & GENERIC_READ) {
|
||
|
*pAM |= FILE_GENERIC_READ;
|
||
|
}
|
||
|
if (*pAM & GENERIC_WRITE) {
|
||
|
*pAM |= FILE_GENERIC_WRITE;
|
||
|
}
|
||
|
if (*pAM & GENERIC_EXECUTE) {
|
||
|
*pAM |= FILE_GENERIC_EXECUTE;
|
||
|
}
|
||
|
if (*pAM & GENERIC_ALL) {
|
||
|
*pAM |= FILE_ALL_ACCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (UserAccess & FILE_READ_DATA) {
|
||
|
Mode |= S_IRUSR;
|
||
|
}
|
||
|
if ((UserAccess & FILE_WRITE_DATA) &&
|
||
|
(UserAccess & FILE_APPEND_DATA)) {
|
||
|
Mode |= S_IWUSR;
|
||
|
}
|
||
|
if (UserAccess & FILE_EXECUTE) {
|
||
|
Mode |= S_IXUSR;
|
||
|
}
|
||
|
|
||
|
if (GroupAccess & FILE_READ_DATA) {
|
||
|
Mode |= S_IRGRP;
|
||
|
}
|
||
|
if ((GroupAccess & FILE_WRITE_DATA) &&
|
||
|
(GroupAccess & FILE_APPEND_DATA)) {
|
||
|
Mode |= S_IWGRP;
|
||
|
}
|
||
|
if (GroupAccess & FILE_EXECUTE) {
|
||
|
Mode |= S_IXGRP;
|
||
|
}
|
||
|
|
||
|
if (OtherAccess & FILE_READ_DATA) {
|
||
|
Mode |= S_IROTH;
|
||
|
}
|
||
|
if ((OtherAccess & FILE_WRITE_DATA) &&
|
||
|
(OtherAccess & FILE_APPEND_DATA)) {
|
||
|
Mode |= S_IWOTH;
|
||
|
}
|
||
|
if (OtherAccess & FILE_EXECUTE) {
|
||
|
Mode |= S_IXOTH;
|
||
|
}
|
||
|
return Mode;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ModeToAccessMask(
|
||
|
mode_t Mode,
|
||
|
PACCESS_MASK pUserAccess,
|
||
|
PACCESS_MASK pGroupAccess,
|
||
|
PACCESS_MASK pOtherAccess
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// All ACL's have these standard permissions:
|
||
|
// READ_ATTR and READ_EA so anybody can access() any file.
|
||
|
//
|
||
|
|
||
|
*pUserAccess = SYNCHRONIZE |
|
||
|
READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA;
|
||
|
*pGroupAccess = *pOtherAccess = *pUserAccess;
|
||
|
|
||
|
//
|
||
|
// The owner always gets WRITE_DAC (for chmod), FILE_WRITE_ATTR
|
||
|
// (for utimes), and WRITE_OWNER (for chgrp).
|
||
|
//
|
||
|
*pUserAccess |= (WRITE_DAC | WRITE_OWNER | FILE_WRITE_ATTRIBUTES);
|
||
|
|
||
|
if (Mode & S_IRUSR) {
|
||
|
*pUserAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
|
||
|
}
|
||
|
if (Mode & S_IWUSR) {
|
||
|
*pUserAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
|
||
|
}
|
||
|
if (Mode & S_IXUSR) {
|
||
|
*pUserAccess |= FILE_GENERIC_EXECUTE;
|
||
|
}
|
||
|
|
||
|
if (Mode & S_IRGRP) {
|
||
|
*pGroupAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
|
||
|
}
|
||
|
if (Mode & S_IWGRP) {
|
||
|
*pGroupAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
|
||
|
}
|
||
|
if (Mode & S_IXGRP) {
|
||
|
*pGroupAccess |= FILE_GENERIC_EXECUTE;
|
||
|
}
|
||
|
|
||
|
if (Mode & S_IROTH) {
|
||
|
*pOtherAccess |= FILE_GENERIC_READ | FILE_LIST_DIRECTORY;
|
||
|
}
|
||
|
if (Mode & S_IWOTH) {
|
||
|
*pOtherAccess |= FILE_GENERIC_WRITE | FILE_DELETE_CHILD;
|
||
|
}
|
||
|
if (Mode & S_IXOTH) {
|
||
|
*pOtherAccess |= FILE_GENERIC_EXECUTE;
|
||
|
}
|
||
|
}
|