Blame src/cmds/ntux_cmd_chmod.c

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