/***********************************************************/
/* ntux: native translation und extension */
/* Copyright (C) 2016--2021 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.NTUX. */
/***********************************************************/
#include <psxabi/sys_sysapi.h>
#include <psxabi/sys_stat.h>
#include <psxabi/sys_path.h>
#include <psxabi/sys_errno.h>
#include <psxxfi/xfi_base.h>
#include <psxxfi/xfi_acl.h>
#include <psxxfi/xfi_fs.h>
#include <psxxfi/xfi_ofd.h>
#include <psxxfi/xfi_path.h>
#include <psxxfi/xfi_unicode.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_acl.h>
#include <ntapi/nt_file.h>
#include <ntux/ntux.h>
#include "ntux_driver_impl.h"
#include "ntux_nolibc_impl.h"
#include "ntux_errinfo_impl.h"
#include "nolibc/stdbool.h"
#define __SID_SYSTEM {1,1,{{0,0,0,0,0,5}},{18}}
#define __SID_ADMINISTRATORS {1,2,{{0,0,0,0,0,5}},{32,544}}
#define NTUX_OPT_MODE_NEUTRAL (0)
#define NTUX_OPT_MODE_EQUAL (1)
#define NTUX_OPT_MODE_PLUS (2)
#define NTUX_OPT_MODE_MINUS (3)
#define NTUX_BUF_SIZE_64K (64*1024)
static const nt_sid sid_system = __SID_SYSTEM;
static const nt_sid_os sid_admins = __SID_ADMINISTRATORS;
struct ntux_sd_buffer {
nt_sd_common_buffer sd;
uint32_t exbuf[512];
};
struct ntux_ace_any {
nt_ace_header header;
uint32_t mask;
uint32_t sid_start;
};
static int ntux_cmd_chmod_ret(int fd, struct __ofd * ofd, void * hasync, int ret)
{
if (hasync)
__xfi_close_handle(hasync);
if (ofd)
__xfi_ofd_ref_dec(ofd);
if (fd >= 0)
__sys_close(fd);
return ret;
}
static nt_sid * ntux_cmd_chmod_sid_from_name(const char * name)
{
if (!strcmp(name,"Administrators"))
return (nt_sid *)&sid_admins;
else if (!strcmp(name,"SYSTEM"))
return (nt_sid *)&sid_system;
else
return 0;
}
int ntux_cmd_chmod(const struct ntux_driver_ctx * dctx, const char * dunit)
{
intptr_t ret;
int32_t status;
int fdout;
int fdcwd;
int finherit;
int optmode;
const char * strmode;
const char * chmode;
const unsigned char * refobj;
const unsigned char * unit;
char * ch;
struct ntux_sd_buffer sdbuf;
struct ntux_sd_buffer srcsd;
struct ntux_sd_buffer cntsd;
nt_sd_common_buffer * dstsd;
nt_sd_common_meta meta;
nt_sid * owner;
nt_sid * group;
nt_acl * dacl;
struct ntux_ace_any * srcace;
struct ntux_ace_any * dstace;
uint32_t access_owner;
uint32_t access_group;
uint32_t access_other;
uint32_t access_admin;
uint32_t ace_flags;
uint32_t sec_mask;
size_t size;
size_t addr;
size_t aceaddr;
size_t cntbufsize;
char * cntpath_utf8;
wchar16_t * cntpath_utf16;
int exacefilter;
size_t exacesize;
int exacecount;
int idx;
int fd = -1;
int reffd = -1;
int cntfd = -1;
struct __ofd * ofd = 0;
struct __ofd * refofd = 0;
struct __ofd * cntofd = 0;
void * cntbuf = 0;
void * hasync = 0;
char dummy = 0;
/* strmode */
strmode = dctx->cctx->strmode ? dctx->cctx->strmode : &dummy;
optmode = NTUX_OPT_MODE_NEUTRAL;
finherit = false;
ace_flags = 0;
for (chmode=strmode; *chmode; chmode++) {
switch (*chmode) {
case '=':
optmode = NTUX_OPT_MODE_EQUAL;
finherit = 0;
ace_flags = 0;
break;
case '+':
optmode = NTUX_OPT_MODE_PLUS;
break;
case '-':
optmode = NTUX_OPT_MODE_MINUS;
break;
case 'p':
switch (optmode) {
case NTUX_OPT_MODE_EQUAL:
case NTUX_OPT_MODE_PLUS:
ace_flags = NT_ACE_CONTAINER_INHERIT | NT_ACE_OBJECT_INHERIT;
break;
case NTUX_OPT_MODE_MINUS:
ace_flags = 0;
break;
default:
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLEE_ERROR));
}
break;
case 'P':
switch (optmode) {
case NTUX_OPT_MODE_EQUAL:
case NTUX_OPT_MODE_PLUS:
finherit = true;
break;
case NTUX_OPT_MODE_MINUS:
finherit = false;
break;
default:
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLEE_ERROR));
}
break;
default:
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLEE_ERROR));
}
}
/* conflicting arguments? */
if (finherit && dctx->cctx->refobj)
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_CONFLICTING_ARGUMENTS));
/* initial --owner and --group support: Administrators, SYSTEM */
owner = 0;
group = 0;
if (dctx->cctx->owner)
if (!(owner = ntux_cmd_chmod_sid_from_name(dctx->cctx->owner)))
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_NOT_IMPLEMENTED));
if (dctx->cctx->group)
if (!(group = ntux_cmd_chmod_sid_from_name(dctx->cctx->group)))
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_NOT_IMPLEMENTED));
/* init */
ntux_driver_set_ectx(
dctx,0,dunit);
unit = (const unsigned char *)dunit;
/* fdctx */
fdout = ntux_driver_fdout(dctx);
fdcwd = ntux_driver_fdcwd(dctx);
/* fd */
if ((ret = __sys_openat(fdcwd,unit,0,0)) < 0)
if (ntux_errno_set(dctx,ret))
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_SYSTEM_ERROR(dctx));
fd = ret;
/* ofd */
if (!(ofd = __xfi_ofd_ref_inc(fd)))
return ntux_cmd_chmod_ret(
fd,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLOW_ERROR));
/* hasync */
sec_mask = NT_SEC_READ_CONTROL;
sec_mask |= NT_SEC_WRITE_DAC;
sec_mask |= owner ? NT_SEC_WRITE_OWNER : 0;
if ((status = __xfi_fs_open_async(
&hasync,
ofd->info.hfile,0,
sec_mask,
NT_FILE_SHARE_READ
| NT_FILE_SHARE_WRITE
| NT_FILE_SHARE_DELETE)))
if (ntux_errno_set(dctx,EACCES))
return ntux_cmd_chmod_ret(
fd,ofd,0,
NTUX_SYSTEM_ERROR(dctx));
/* refofd */
if ((refobj = (const unsigned char *)dctx->cctx->refobj)) {
ntux_driver_set_ectx(
dctx,0,dctx->cctx->refobj);
if ((ret = __sys_openat(fdcwd,refobj,0,0)) < 0)
if (ntux_errno_set(dctx,ret))
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_SYSTEM_ERROR(dctx));
reffd = ret;
if (!(refofd = __xfi_ofd_ref_inc(reffd))) {
__sys_close(reffd);
return ntux_cmd_chmod_ret(
fd,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLOW_ERROR));
}
ntux_driver_set_ectx(
dctx,0,dunit);
}
/* srcsd */
status = __xfi_query_security_object(
refobj ? refofd->info.hfile : hasync,
NT_OWNER_SECURITY_INFORMATION
| NT_GROUP_SECURITY_INFORMATION
| NT_DACL_SECURITY_INFORMATION,
&srcsd.sd.sd,sizeof(srcsd),&size);
if (refofd) {
__xfi_ofd_ref_dec(refofd);
__sys_close(reffd);
}
if (status)
if (ntux_errno_set(dctx,ENXIO))
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
status = __xfi_acl_init_common_descriptor_meta(
&meta,&srcsd.sd.sd,
NT_ACL_INIT_COMMON_DESCRIPTION_META_STRICT_MODE);
/* sd copy based on a non-posix object? */
if (status && refobj) {
ntux_driver_set_ectx(
dctx,0,dctx->cctx->refobj);
if (dctx->cctx->strmode
|| dctx->cctx->owner
|| dctx->cctx->group)
return ntux_cmd_chmod_ret(
0,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_NON_POSIX_DESCRIPTOR));
ntux_driver_set_ectx(
dctx,0,dunit);
sec_mask = NT_DACL_SECURITY_INFORMATION;
if ((status = __xfi_set_security_object(
hasync,sec_mask,&srcsd.sd.sd)))
if (ntux_errno_set(dctx,EPERM))
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
return ntux_cmd_chmod_ret(fd,ofd,hasync,0);
}
if (status)
if (ntux_errno_set(dctx,EBADF))
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
/* source permissions */
access_owner = meta.owner_ace ? meta.owner_ace->mask : 0;
access_group = meta.group_ace ? meta.group_ace->mask : 0;
access_other = meta.other_ace ? meta.other_ace->mask : 0;
access_admin = meta.admin_ace ? meta.admin_ace->mask : 0;
/* initial --strmode support, retaining previous options as needed */
if (!dctx->cctx->strmode) {
ace_flags |= meta.owner_ace ? meta.owner_ace->header.ace_flags : 0;
ace_flags |= meta.group_ace ? meta.group_ace->header.ace_flags : 0;
ace_flags |= meta.other_ace ? meta.other_ace->header.ace_flags : 0;
ace_flags |= meta.admin_ace ? meta.admin_ace->header.ace_flags : 0;
}
/* updated dacl */
dstsd = &sdbuf.sd;
__xfi_acl_init_common_descriptor(
dstsd,
owner ? owner : meta.owner,
group ? group : meta.group,
0,0,
access_owner,access_group,access_other,
access_admin,meta.system_acc,
ace_flags);
if ((dstsd->sd.offset_dacl < dstsd->sd.offset_owner)
|| (dstsd->sd.offset_dacl < dstsd->sd.offset_group)
|| (dstsd->sd.offset_dacl < dstsd->sd.offset_sacl))
return ntux_cmd_chmod_ret(
fd,0,0,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLOW_ERROR));
/* inherited ace's */
if (finherit) {
dstsd->sd.control = NT_SE_SELF_RELATIVE
| NT_SE_DACL_AUTO_INHERIT_REQ
| NT_SE_DACL_AUTO_INHERITED
| NT_SE_DACL_PRESENT;
/* cntofd */
cntbuf = 0; cntbufsize = NTUX_BUF_SIZE_64K;
if (__xfi_allocate_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
&cntbuf,
0,
&cntbufsize,
NT_MEM_COMMIT,
NT_PAGE_READWRITE))
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
if (__xfi_ofd_is_dir(ofd)) {
cntpath_utf16 = (wchar16_t *)cntbuf;
__xfi_path_conv_ofdat_utf16(
ofd,
(const wchar16_t[]){'.','.','/',0},
PSX_PATH_SYNTAX_ANY,
4*sizeof(wchar16_t),
cntpath_utf16,
PSX_PATH_SYNTAX_POSIX_STRICT,
cntbufsize,
0,0,PSX_PATH_CONV_RESOLVE_ELEMENT,
&cntofd);
} else {
cntpath_utf8 = cntbuf;
status = __xfi_path_conv_utf8(
fdcwd,
unit,
PSX_PATH_SYNTAX_POSIX_STRICT,
ntux_strlen((const char *)unit),
cntpath_utf8,
PSX_PATH_SYNTAX_POSIX_STRICT,
cntbufsize,
0,0,PSX_PATH_CONV_RESOLVE_ELEMENT,
0);
if (status) {
__xfi_free_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
&cntbuf,
&cntbufsize,
NT_MEM_RELEASE);
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLOW_ERROR));
}
if ((cntpath_utf8[0] == '/') && cntpath_utf8[1]) {
for (ch=cntpath_utf8; *ch; ch++)
(void)0;
/* guard against an internal error */
if (ch[-1] != '/') {
for (--ch; *ch != '/'; ch--)
(void)0;
*ch = 0;
cntfd = __sys_openat(
fdcwd,
(const unsigned char *)cntpath_utf8,
0,0);
if (cntfd >= 0)
if (!(cntofd = __xfi_ofd_ref_inc(cntfd)))
__sys_close(cntfd);
}
}
}
__xfi_free_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
&cntbuf,
&cntbufsize,
NT_MEM_RELEASE);
if (!cntofd)
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_CUSTOM_ERROR(
dctx,
NTUX_ERR_FLOW_ERROR));
status = __xfi_query_security_object(
cntofd->info.hfile,
NT_OWNER_SECURITY_INFORMATION
| NT_GROUP_SECURITY_INFORMATION
| NT_DACL_SECURITY_INFORMATION,
&cntsd.sd.sd,sizeof(cntsd),&size);
if (status) {
__xfi_ofd_ref_dec(cntofd);
__sys_close(cntfd);
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
}
/* exacefilter */
exacefilter = __xfi_ofd_is_dir(ofd)
? NT_ACE_CONTAINER_INHERIT
: NT_ACE_OBJECT_INHERIT;
/* exacecount, exacesize */
if (cntsd.sd.sd.offset_dacl) {
addr = (size_t)&cntsd.sd.sd;
addr += cntsd.sd.sd.offset_dacl;
dacl = (nt_acl *)addr;
addr += sizeof(*dacl);
exacecount = 0;
exacesize = 0;
for (idx=0; idx<dacl->ace_count; idx++) {
srcace = (struct ntux_ace_any *)addr;
if (srcace->header.ace_flags & exacefilter) {
exacecount++;
exacesize += srcace->header.ace_size;
}
addr += srcace->header.ace_size;
}
/* dstsd::dacl */
addr = (size_t)dstsd;
addr += dstsd->sd.offset_dacl;
dacl = (nt_acl *)addr;
if (dstsd->sd.offset_dacl + dacl->acl_size + exacesize > sizeof(sdbuf)) {
__xfi_ofd_ref_dec(cntofd);
__sys_close(cntfd);
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_BUFFER_ERROR(dctx));
}
dacl->acl_size += exacesize;
dacl->ace_count += exacecount;
/* pointer to dstsd's next ace */
aceaddr = addr;
aceaddr += sizeof(*dacl);
for (idx=0; idx<dacl->ace_count; idx++) {
dstace = (struct ntux_ace_any *)aceaddr;
aceaddr += dstace->header.ace_size;
}
dstace = (struct ntux_ace_any *)aceaddr;
/* copy container's ace's according to filter */
addr = (size_t)&cntsd.sd.sd;
addr += cntsd.sd.sd.offset_dacl;
dacl = (nt_acl *)addr;
addr += sizeof(*dacl);
for (idx=0; idx<dacl->ace_count; idx++) {
srcace = (struct ntux_ace_any *)addr;
if (srcace->header.ace_flags & exacefilter) {
ntux_memcpy(dstace,srcace,srcace->header.ace_size);
dstace->header.ace_flags = NT_ACE_INHERITED | ace_flags;
aceaddr += srcace->header.ace_size;
dstace = (struct ntux_ace_any *)aceaddr;
}
addr += srcace->header.ace_size;
}
}
}
/* finalize */
sec_mask = NT_DACL_SECURITY_INFORMATION;
sec_mask |= owner ? NT_OWNER_SECURITY_INFORMATION : 0;
if ((status = __xfi_set_security_object(
hasync,sec_mask,&dstsd->sd)))
if (ntux_errno_set(dctx,EPERM))
return ntux_cmd_chmod_ret(
fd,ofd,hasync,
NTUX_SYSTEM_ERROR(dctx));
/* changes */
(void)fdout;
/* cntofd */
if (cntofd) {
__xfi_ofd_ref_dec(cntofd);
__sys_close(cntfd);
}
/* all done */
return ntux_cmd_chmod_ret(fd,ofd,hasync,0);
}