/***********************************************************/ /* ntux: native translation und extension */ /* Copyright (C) 2016--2021 SysDeer Technologies, LLC */ /* Released under GPLv2 and GPLv3; see COPYING.NTUX. */ /***********************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; idxace_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; idxace_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; idxace_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); }