#include <cputypes.h>
#include <machine/reg.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/user.h>
#include <sys/socketvar.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/mount_internal.h>
#include <sys/vnode_internal.h>
#include <sys/file_internal.h>
#include <sys/stat.h>
#include <sys/uio_internal.h>
#include <sys/acct.h>
#include <sys/exec.h>
#include <sys/kdebug.h>
#include <sys/signal.h>
#include <sys/aio_kern.h>
#include <sys/sysproto.h>
#include <sys/shm_internal.h>
#include <sys/ubc_internal.h>
#include <bsm/audit_kernel.h>
#include <mach/mach_types.h>
#include <mach/task.h>
#include <mach/thread_act.h>
#include <mach/vm_map.h>
#include <mach/mach_vm.h>
#include <mach/vm_param.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <vm/vm_pager.h>
#include <vm/vm_kern.h>
#include <vm/task_working_set.h>
#include <vm/vm_shared_memory_server.h>
void ipc_task_reset(
task_t task);
extern struct savearea *get_user_regs(thread_t);
#include <kern/thread.h>
#include <kern/task.h>
#include <kern/ast.h>
#include <kern/mach_loader.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <machine/vmparam.h>
#if KTRACE
#include <sys/ktrace.h>
#endif
#include <sys/imgact.h>
#define SIZE_MAXPTR 8
#define SIZE_IMG_STRSPACE (NCARGS - 2 * SIZE_MAXPTR)
int app_profile = 0;
extern vm_map_t bsd_pageable_map;
extern struct fileops vnops;
#define ROUND_PTR(type, addr) \
(type *)( ( (unsigned)(addr) + 16 - 1) \
& ~(16 - 1) )
struct image_params;
static int exec_copyout_strings(struct image_params *imgp, user_addr_t *stackp);
static int load_return_to_errno(load_return_t lrtn);
static int execargs_alloc(struct image_params *imgp);
static int execargs_free(struct image_params *imgp);
static int exec_check_permissions(struct image_params *imgp);
static int exec_extract_strings(struct image_params *imgp);
static int exec_handle_sugid(struct image_params *imgp);
static int sugid_scripts = 0;
SYSCTL_INT (_kern, OID_AUTO, sugid_scripts, CTLFLAG_RW, &sugid_scripts, 0, "");
static kern_return_t create_unix_stack(vm_map_t map, user_addr_t user_stack,
int customstack, struct proc *p);
static int copyoutptr(user_addr_t ua, user_addr_t ptr, int ptr_size);
extern int grade_binary(cpu_type_t exectype, cpu_subtype_t execsubtype);
extern void vfork_return(thread_t th_act,
struct proc * p,
struct proc *p2,
register_t *retval);
extern char classichandler[32];
extern uint32_t classichandler_fsid;
extern long classichandler_fileid;
static int
exec_add_string(struct image_params *imgp, user_addr_t str, int seg)
{
int error = 0;
do {
size_t len = 0;
if (imgp->ip_strspace <= 0) {
error = E2BIG;
break;
}
if (IS_UIO_SYS_SPACE(seg)) {
char *kstr = CAST_DOWN(char *,str);
error = copystr(kstr, imgp->ip_strendp, imgp->ip_strspace, &len);
} else {
error = copyinstr(str, imgp->ip_strendp, imgp->ip_strspace,
&len);
}
imgp->ip_strendp += len;
imgp->ip_strspace -= len;
} while (error == ENAMETOOLONG);
return error;
}
static int
exec_save_path(struct image_params *imgp, user_addr_t path, int seg)
{
int error;
size_t len;
char *kpath = CAST_DOWN(char *,path);
imgp->ip_strendp = imgp->ip_strings;
imgp->ip_strspace = SIZE_IMG_STRSPACE;
len = MIN(MAXPATHLEN, imgp->ip_strspace);
switch( seg) {
case UIO_USERSPACE32:
case UIO_USERSPACE64:
error = copyinstr(path, imgp->ip_strings, len, &len);
break;
case UIO_SYSSPACE32:
error = copystr(kpath, imgp->ip_strings, len, &len);
break;
default:
error = EFAULT;
break;
}
if (!error) {
imgp->ip_strendp += len;
imgp->ip_strspace -= len;
imgp->ip_argv = imgp->ip_strendp;
}
return(error);
}
static int
exec_shell_imgact(struct image_params *imgp)
{
char *vdata = imgp->ip_vdata;
char *ihp;
char *line_endp;
char *interp;
if (vdata[0] != '#' ||
vdata[1] != '!' ||
(imgp->ip_flags & IMGPF_INTERPRET) != 0) {
return (-1);
}
imgp->ip_flags |= IMGPF_INTERPRET;
if (sugid_scripts == 0) {
imgp->ip_origvattr->va_mode &= ~(VSUID | VSGID);
}
for( ihp = &vdata[2]; *ihp != '\n' && *ihp != '#'; ihp++) {
if (ihp >= &vdata[IMG_SHSIZE])
return (ENOEXEC);
}
line_endp = ihp;
ihp = &vdata[2];
while ( ihp < line_endp && ((*ihp == ' ') || (*ihp == '\t')))
ihp++;
for (;line_endp > ihp && ((*line_endp == ' ') || (*line_endp == '\t')); line_endp--)
continue;
if (line_endp == ihp)
return (ENOEXEC);
interp = imgp->ip_interp_name;
while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t'))
*interp++ = *ihp++;
*interp = '\0';
exec_save_path(imgp, CAST_USER_ADDR_T(imgp->ip_interp_name),
UIO_SYSSPACE32);
ihp = &vdata[2];
while (ihp < line_endp) {
while ((*ihp == ' ') || (*ihp == '\t'))
ihp++;
if (ihp >= line_endp)
break;
while ((ihp < line_endp) && (*ihp != ' ') && (*ihp != '\t')) {
*imgp->ip_strendp++ = *ihp++;
imgp->ip_strspace--;
}
*imgp->ip_strendp++ = 0;
imgp->ip_strspace--;
imgp->ip_argc++;
}
return (-3);
}
static int
exec_fat_imgact(struct image_params *imgp)
{
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
kauth_cred_t cred = p->p_ucred;
struct fat_header *fat_header = (struct fat_header *)imgp->ip_vdata;
struct fat_arch fat_arch;
int resid, error;
load_return_t lret;
if ((fat_header->magic != FAT_MAGIC) &&
(fat_header->magic != FAT_CIGAM)) {
error = -1;
goto bad;
}
lret = fatfile_getarch_affinity(imgp->ip_vp,
(vm_offset_t)fat_header,
&fat_arch,
(p->p_flag & P_AFFINITY));
if (lret != LOAD_SUCCESS) {
error = load_return_to_errno(lret);
goto bad;
}
error = vn_rdwr(UIO_READ, imgp->ip_vp, imgp->ip_vdata,
PAGE_SIZE, fat_arch.offset,
UIO_SYSSPACE32, (IO_UNIT|IO_NODELOCKED),
cred, &resid, p);
if (error) {
goto bad;
}
if (resid) {
error = EBADEXEC;
goto bad;
}
error = -2;
imgp->ip_arch_offset = (user_size_t)fat_arch.offset;
imgp->ip_arch_size = (user_size_t)fat_arch.size;
bad:
return (error);
}
static int
exec_mach_imgact(struct image_params *imgp)
{
struct mach_header *mach_header = (struct mach_header *)imgp->ip_vdata;
kauth_cred_t cred = vfs_context_ucred(imgp->ip_vfs_context);
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
int error = 0;
int vfexec = 0;
task_t task;
task_t new_task;
thread_t thread;
struct uthread *uthread;
vm_map_t old_map = VM_MAP_NULL;
vm_map_t map;
boolean_t clean_regions = FALSE;
shared_region_mapping_t initial_region = NULL;
load_return_t lret;
load_result_t load_result;
if ((mach_header->magic != MH_MAGIC) &&
(mach_header->magic != MH_MAGIC_64)) {
error = -1;
goto bad;
}
task = current_task();
thread = current_thread();
uthread = get_bsdthread_info(thread);
if (uthread->uu_flag & UT_VFORK)
vfexec = 1;
if ((mach_header->cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64)
imgp->ip_flags |= IMGPF_IS_64BIT;
if (!grade_binary(mach_header->cputype, mach_header->cpusubtype)) {
error = EBADARCH;
goto bad;
}
if (imgp->ip_user_argv != 0LL) {
error = exec_extract_strings(imgp);
if (error)
goto bad;
}
imgp->ip_strendp[0] = 0;
imgp->ip_strendp[1] = 0;
imgp->ip_strendp[2] = 0;
imgp->ip_strendp += (((imgp->ip_strendp - imgp->ip_strings) + NBPW-1) & ~(NBPW-1));
if (vfexec) {
kern_return_t result;
result = task_create_internal(task, FALSE, &new_task);
if (result != KERN_SUCCESS)
printf("execve: task_create failed. Code: 0x%x\n", result);
p->task = new_task;
set_bsdtask_info(new_task, p);
if (p->p_nice != 0)
resetpriority(p);
map = get_task_map(new_task);
result = thread_create(new_task, &imgp->ip_vfork_thread);
if (result != KERN_SUCCESS)
printf("execve: thread_create failed. Code: 0x%x\n", result);
task = new_task;
thread = imgp->ip_vfork_thread;
uthread = get_bsdthread_info(thread);
} else {
map = VM_MAP_NULL;
}
if (imgp->ip_flags & IMGPF_IS_64BIT) {
task_set_64bit(task, TRUE);
p->p_flag |= P_LP64;
} else {
task_set_64bit(task, FALSE);
p->p_flag &= ~P_LP64;
}
if((imgp->ip_flags & IMGPF_IS_64BIT) == 0)
if(imgp->ip_tws_cache_name) {
tws_handle_startup_file(task, kauth_cred_getuid(cred),
imgp->ip_tws_cache_name, imgp->ip_vp, &clean_regions);
}
vm_get_shared_region(task, &initial_region);
task_set_64bit(task,
((imgp->ip_flags & IMGPF_IS_64BIT) == IMGPF_IS_64BIT));
lret = load_machfile(imgp, mach_header, thread, map, clean_regions, &load_result);
if (lret != LOAD_SUCCESS) {
error = load_return_to_errno(lret);
goto badtoolate;
}
(void)ubc_map(imgp->ip_vp, PROT_EXEC);
error = exec_handle_sugid(imgp);
KNOTE(&p->p_klist, NOTE_EXEC);
if (!vfexec && (p->p_flag & P_TRACED))
psignal(p, SIGTRAP);
if (error) {
goto badtoolate;
}
vnode_put(imgp->ip_vp);
imgp->ip_vp = NULL;
if (load_result.unixproc &&
create_unix_stack(get_task_map(task),
load_result.user_stack, load_result.customstack, p)) {
error = load_return_to_errno(LOAD_NOSPACE);
goto badtoolate;
}
if (vfexec) {
uthread->uu_ar0 = (void *)get_user_regs(thread);
old_map = vm_map_switch(get_task_map(task));
}
if (load_result.unixproc) {
user_addr_t ap;
ap = p->user_stack;
error = exec_copyout_strings(imgp, &ap);
if (error) {
if (vfexec)
vm_map_switch(old_map);
goto badtoolate;
}
thread_setuserstack(thread, ap);
}
if (load_result.dynlinker) {
uint64_t ap;
if (imgp->ip_flags & IMGPF_IS_64BIT) {
ap = thread_adjuserstack(thread, -8);
(void)copyoutptr(load_result.mach_header, ap, 8);
} else {
ap = thread_adjuserstack(thread, -4);
(void)suword(ap, load_result.mach_header);
}
}
if (vfexec) {
vm_map_switch(old_map);
}
thread_setentrypoint(thread, load_result.entry_point);
stopprofclock(p);
execsigs(p, thread);
fdexec(p);
_aio_exec( p );
if (!vfexec && p->vm_shm)
shmexec(p);
semexit(p);
p->p_acflag &= ~AFORK;
if (0 != imgp->ip_p_comm[0]) {
bcopy((caddr_t)imgp->ip_p_comm, (caddr_t)p->p_comm,
sizeof(p->p_comm));
} else {
if (imgp->ip_ndp->ni_cnd.cn_namelen > MAXCOMLEN)
imgp->ip_ndp->ni_cnd.cn_namelen = MAXCOMLEN;
bcopy((caddr_t)imgp->ip_ndp->ni_cnd.cn_nameptr, (caddr_t)p->p_comm,
(unsigned)imgp->ip_ndp->ni_cnd.cn_namelen);
p->p_comm[imgp->ip_ndp->ni_cnd.cn_namelen] = '\0';
}
{
long dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4;
kdbg_trace_string(p, &dbg_arg1, &dbg_arg2, &dbg_arg3, &dbg_arg4);
if (vfexec)
{
KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
p->p_pid ,0,0,0, (unsigned int)thread);
KERNEL_DEBUG_CONSTANT1((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, (unsigned int)thread);
}
else
{
KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_DATA, 2)) | DBG_FUNC_NONE,
p->p_pid ,0,0,0,0);
KERNEL_DEBUG_CONSTANT((TRACEDBG_CODE(DBG_TRACE_STRING, 2)) | DBG_FUNC_NONE,
dbg_arg1, dbg_arg2, dbg_arg3, dbg_arg4, 0);
}
}
p->p_flag &= ~P_CLASSIC;
p->p_flag |= P_EXEC;
if (p->p_pptr && (p->p_flag & P_PPWAIT)) {
p->p_flag &= ~P_PPWAIT;
wakeup((caddr_t)p->p_pptr);
}
if (vfexec && (p->p_flag & P_TRACED)) {
psignal_vfork(p, new_task, thread, SIGTRAP);
}
badtoolate:
if (vfexec) {
task_deallocate(new_task);
thread_deallocate(thread);
if (error)
error = 0;
}
bad:
return(error);
}
struct execsw {
int (*ex_imgact)(struct image_params *);
const char *ex_name;
} execsw[] = {
{ exec_mach_imgact, "Mach-o Binary" },
{ exec_fat_imgact, "Fat Binary" },
{ exec_shell_imgact, "Interpreter Script" },
{ NULL, NULL}
};
int
execve(struct proc *p, struct execve_args *uap, register_t *retval)
{
kauth_cred_t cred = p->p_ucred;
struct image_params image_params, *imgp;
struct vnode_attr va;
struct vnode_attr origva;
struct nameidata nd;
struct uthread *uthread;
int i;
int resid, error;
task_t task;
int numthreads;
int vfexec=0;
int once = 1;
char alt_p_comm[sizeof(p->p_comm)] = {0};
int is_64 = IS_64BIT_PROCESS(p);
int seg = (is_64 ? UIO_USERSPACE64 : UIO_USERSPACE32);
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = p->p_ucred;
imgp = &image_params;
bzero(imgp, sizeof(*imgp));
imgp->ip_user_fname = uap->fname;
imgp->ip_user_argv = uap->argp;
imgp->ip_user_envv = uap->envp;
imgp->ip_vattr = &va;
imgp->ip_origvattr = &origva;
imgp->ip_vfs_context = &context;
imgp->ip_flags = (is_64 ? IMGPF_WAS_64BIT : IMGPF_NONE);
imgp->ip_tws_cache_name = NULL;
imgp->ip_p_comm = alt_p_comm;
task = current_task();
uthread = get_bsdthread_info(current_thread());
if (uthread->uu_flag & UT_VFORK) {
vfexec = 1;
} else {
if (task != kernel_task) {
numthreads = get_task_numacts(task);
if (numthreads <= 0 )
return(EINVAL);
if (numthreads > 1) {
return(ENOTSUP);
}
}
}
error = execargs_alloc(imgp);
if (error)
return(error);
error = exec_save_path(imgp, uap->fname, seg);
if (error) {
execargs_free(imgp);
return(error);
}
if((p->p_fd->fd_rdir == NULLVP) && (app_profile != 0)) {
imgp->ip_tws_cache_name = imgp->ip_strendp;
while (imgp->ip_tws_cache_name[0] != '/') {
if(imgp->ip_tws_cache_name == imgp->ip_strings) {
imgp->ip_tws_cache_name--;
break;
}
imgp->ip_tws_cache_name--;
}
imgp->ip_tws_cache_name++;
}
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
seg, uap->fname, imgp->ip_vfs_context);
again:
error = namei(&nd);
if (error)
goto bad;
imgp->ip_ndp = &nd;
imgp->ip_vp = nd.ni_vp;
error = exec_check_permissions(imgp);
if (error)
goto bad;
if (once) {
once = 0;
origva = va;
}
error = vn_rdwr(UIO_READ, imgp->ip_vp, imgp->ip_vdata, PAGE_SIZE, 0,
UIO_SYSSPACE32, IO_NODELOCKED, cred, &resid, p);
if (error)
goto bad;
encapsulated_binary:
error = -1;
for(i = 0; error == -1 && execsw[i].ex_imgact != NULL; i++) {
error = (*execsw[i].ex_imgact)(imgp);
switch (error) {
case -2:
goto encapsulated_binary;
case -3:
vnode_put(imgp->ip_vp);
imgp->ip_vp = NULL;
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags = (nd.ni_cnd.cn_flags & HASBUF) |
(FOLLOW | LOCKLEAF);
nd.ni_segflg = UIO_SYSSPACE32;
nd.ni_dirp = CAST_USER_ADDR_T(imgp->ip_interp_name);
goto again;
default:
break;
}
}
if (error == 0 && kauth_authorize_fileop_has_listeners()) {
kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_EXEC,
(uintptr_t)nd.ni_vp, 0);
}
if (error == -1)
error = ENOEXEC;
bad:
if (imgp->ip_ndp)
nameidone(imgp->ip_ndp);
if (imgp->ip_vp)
vnode_put(imgp->ip_vp);
if (imgp->ip_strings)
execargs_free(imgp);
if (!error && vfexec) {
vfork_return(current_thread(), p->p_pptr, p, retval);
(void)thread_resume(imgp->ip_vfork_thread);
return(0);
}
return(error);
}
static int
copyinptr(user_addr_t froma, user_addr_t *toptr, int ptr_size)
{
int error;
if (ptr_size == 4) {
unsigned int i;
error = copyin(froma, &i, 4);
*toptr = CAST_USER_ADDR_T(i);
} else {
error = copyin(froma, toptr, 8);
}
return (error);
}
static int
copyoutptr(user_addr_t ua, user_addr_t ptr, int ptr_size)
{
int error;
if (ptr_size == 4) {
unsigned int i = CAST_DOWN(unsigned int,ua);
error = copyout(&i, ptr, 4);
} else {
error = copyout(&ua, ptr, 8);
}
return (error);
}
static int
exec_copyout_strings(struct image_params *imgp, user_addr_t *stackp)
{
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
int ptr_size = (imgp->ip_flags & IMGPF_IS_64BIT) ? 8 : 4;
char *argv = imgp->ip_argv;
user_addr_t string_area;
user_addr_t path_area;
user_addr_t ptr_area;
user_addr_t stack;
int stringc = imgp->ip_argc + imgp->ip_envc;
int len;
int error;
int strspace;
stack = *stackp;
string_area = stack - (((imgp->ip_strendp - imgp->ip_strings) + ptr_size-1) & ~(ptr_size-1)) - ptr_size;
path_area = string_area - (((imgp->ip_argv - imgp->ip_strings) + ptr_size-1) & ~(ptr_size-1));
ptr_area = path_area - ((imgp->ip_argc + imgp->ip_envc + 4) * ptr_size) - ptr_size ;
*stackp = ptr_area;
p->p_argc = imgp->ip_argc;
p->p_argslen = (int)(stack - path_area);
len = 0;
error = copyoutstr(imgp->ip_strings, path_area,
(unsigned)(imgp->ip_argv - imgp->ip_strings),
(size_t *)&len);
if (error)
goto bad;
(void)copyoutptr(0LL, path_area - ptr_size, ptr_size);
(void)copyoutptr(path_area, path_area - 2*ptr_size, ptr_size);
(void)suword(ptr_area, imgp->ip_argc);
ptr_area += sizeof(int);
if (imgp->ip_flags & IMGPF_IS_64BIT) {
(void)suword(ptr_area, 0);
ptr_area += sizeof(int);
}
strspace = SIZE_IMG_STRSPACE - (string_area - path_area);
for (;;) {
if (stringc == imgp->ip_envc) {
(void)copyoutptr(0LL, ptr_area, ptr_size);
ptr_area += ptr_size;
}
if (--stringc < 0)
break;
(void)copyoutptr(string_area, ptr_area, ptr_size);
do {
if (strspace <= 0) {
error = E2BIG;
break;
}
error = copyoutstr(argv, string_area,
(unsigned)strspace,
(size_t *)&len);
string_area += len;
argv += len;
strspace -= len;
} while (error == ENAMETOOLONG);
if (error == EFAULT || error == E2BIG)
break;
ptr_area += ptr_size;
}
(void)copyoutptr(0LL, ptr_area, ptr_size);
bad:
return(error);
}
static int
exec_extract_strings(struct image_params *imgp)
{
int error = 0;
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
int seg = (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32);
int ptr_size = (imgp->ip_flags & IMGPF_WAS_64BIT) ? 8 : 4;
user_addr_t argv = imgp->ip_user_argv;
user_addr_t envv = imgp->ip_user_envv;
if((imgp->ip_flags & IMGPF_INTERPRET) != 0 && argv != 0LL) {
user_addr_t arg;
error = copyinptr(argv, &arg, ptr_size);
if (error)
goto bad;
if (arg != 0LL && arg != (user_addr_t)-1) {
argv += ptr_size;
error = exec_add_string(imgp, imgp->ip_user_fname, seg);
if (error)
goto bad;
imgp->ip_argc++;
}
}
while (argv != 0LL) {
user_addr_t arg;
error = copyinptr(argv, &arg, ptr_size);
if (error)
goto bad;
argv += ptr_size;
if (arg == 0LL) {
break;
} else if (arg == (user_addr_t)-1) {
error = EFAULT;
goto bad;
}
error = exec_add_string(imgp, arg, seg);
if (error)
goto bad;
imgp->ip_argc++;
}
while (envv != 0LL) {
user_addr_t env;
error = copyinptr(envv, &env, ptr_size);
if (error)
goto bad;
envv += ptr_size;
if (env == 0LL) {
break;
} else if (env == (user_addr_t)-1) {
error = EFAULT;
goto bad;
}
error = exec_add_string(imgp, env, seg);
if (error)
goto bad;
imgp->ip_envc++;
}
bad:
return error;
}
#define unix_stack_size(p) (p->p_rlimit[RLIMIT_STACK].rlim_cur)
static int
exec_check_permissions(struct image_params *imgp)
{
struct vnode *vp = imgp->ip_vp;
struct vnode_attr *vap = imgp->ip_vattr;
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
int error;
kauth_action_t action;
if (!vnode_isreg(vp))
return (EACCES);
VATTR_INIT(vap);
VATTR_WANTED(vap, va_uid);
VATTR_WANTED(vap, va_gid);
VATTR_WANTED(vap, va_mode);
VATTR_WANTED(vap, va_fsid);
VATTR_WANTED(vap, va_fileid);
VATTR_WANTED(vap, va_data_size);
if ((error = vnode_getattr(vp, vap, imgp->ip_vfs_context)) != 0)
return (error);
if ((vap->va_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
return (EACCES);
if (vap->va_data_size == 0)
return (ENOEXEC);
imgp->ip_arch_offset = (user_size_t)0;
imgp->ip_arch_size = vap->va_data_size;
if ((vp->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
vap->va_mode &= ~(VSUID | VSGID);
action = KAUTH_VNODE_EXECUTE;
if (p->p_flag & P_TRACED)
action |= KAUTH_VNODE_READ_DATA;
if ((error = vnode_authorize(vp, NULL, action, imgp->ip_vfs_context)) != 0)
return (error);
if (vp->v_writecount)
return (ETXTBSY);
return (error);
}
static int
exec_handle_sugid(struct image_params *imgp)
{
kauth_cred_t cred = vfs_context_ucred(imgp->ip_vfs_context);
struct proc *p = vfs_context_proc(imgp->ip_vfs_context);
int i;
int error = 0;
static struct vnode *dev_null = NULLVP;
p->p_flag &= ~P_SUGID;
if (((imgp->ip_origvattr->va_mode & VSUID) != 0 &&
kauth_cred_getuid(cred) != imgp->ip_origvattr->va_uid) ||
((imgp->ip_origvattr->va_mode & VSGID) != 0 &&
cred->cr_gid != imgp->ip_origvattr->va_gid)) {
#if KTRACE
if (p->p_tracep && !(p->p_traceflag & KTRFAC_ROOT)) {
struct vnode *tvp = p->p_tracep;
p->p_tracep = NULL;
p->p_traceflag = 0;
vnode_rele(tvp);
}
#endif
if (imgp->ip_origvattr->va_mode & VSUID) {
p->p_ucred = kauth_cred_seteuid(p->p_ucred, imgp->ip_origvattr->va_uid);
}
if (imgp->ip_origvattr->va_mode & VSGID) {
p->p_ucred = kauth_cred_setegid(p->p_ucred, imgp->ip_origvattr->va_gid);
}
if (current_task() == p->task)
ipc_task_reset(p->task);
p->p_flag |= P_SUGID;
if (dev_null == NULLVP) {
struct nameidata nd1;
NDINIT(&nd1, LOOKUP, FOLLOW, UIO_SYSSPACE32,
CAST_USER_ADDR_T("/dev/null"),
imgp->ip_vfs_context);
if ((error = vn_open(&nd1, FREAD, 0)) == 0) {
dev_null = nd1.ni_vp;
vnode_put(nd1.ni_vp);
}
}
if (dev_null != NULLVP) {
for (i = 0; i < 3; i++) {
struct fileproc *fp;
int indx;
if (p->p_fd->fd_ofiles[i] != NULL)
continue;
if ((error = falloc(p, &fp, &indx)) != 0)
continue;
if ((error = vnode_ref_ext(dev_null, FREAD)) != 0) {
fp_free(p, indx, fp);
break;
}
fp->f_fglob->fg_flag = FREAD;
fp->f_fglob->fg_type = DTYPE_VNODE;
fp->f_fglob->fg_ops = &vnops;
fp->f_fglob->fg_data = (caddr_t)dev_null;
proc_fdlock(p);
*fdflags(p, indx) &= ~UF_RESERVED;
fp_drop(p, indx, fp, 1);
proc_fdunlock(p);
}
vnode_rele(dev_null);
dev_null = NULLVP;
}
}
p->p_ucred = kauth_cred_setsvuidgid(p->p_ucred, kauth_cred_getuid(p->p_ucred), p->p_ucred->cr_gid);
set_security_token(p);
return(error);
}
static kern_return_t
create_unix_stack(vm_map_t map, user_addr_t user_stack, int customstack,
struct proc *p)
{
mach_vm_size_t size;
mach_vm_offset_t addr;
p->user_stack = user_stack;
if (!customstack) {
size = mach_vm_round_page(unix_stack_size(p));
addr = mach_vm_trunc_page(user_stack - size);
return (mach_vm_allocate(map, &addr, size,
VM_MAKE_TAG(VM_MEMORY_STACK) |
VM_FLAGS_FIXED));
} else
return(KERN_SUCCESS);
}
#include <sys/reboot.h>
static char init_program_name[128] = "/sbin/launchd";
static const char * other_init = "/sbin/mach_init";
char init_args[128] = "";
struct execve_args init_exec_args;
int init_attempts = 0;
void
load_init_program(struct proc *p)
{
vm_offset_t init_addr;
char *argv[3];
int error;
register_t retval[2];
error = 0;
do {
if (boothowto & RB_INITNAME) {
printf("init program? ");
#if FIXME
gets(init_program_name, init_program_name);
#endif
}
if (error && ((boothowto & RB_INITNAME) == 0) &&
(init_attempts == 1)) {
printf("Load of %s, errno %d, trying %s\n",
init_program_name, error, other_init);
error = 0;
bcopy(other_init, init_program_name,
sizeof(other_init));
}
init_attempts++;
if (error) {
printf("Load of %s failed, errno %d\n",
init_program_name, error);
error = 0;
boothowto |= RB_INITNAME;
continue;
}
init_addr = VM_MIN_ADDRESS;
(void) vm_allocate(current_map(), &init_addr,
PAGE_SIZE, VM_FLAGS_ANYWHERE);
if (init_addr == 0)
init_addr++;
(void) copyout((caddr_t) init_program_name,
CAST_USER_ADDR_T(init_addr),
(unsigned) sizeof(init_program_name)+1);
argv[0] = (char *) init_addr;
init_addr += sizeof(init_program_name);
init_addr = (vm_offset_t)ROUND_PTR(char, init_addr);
(void) copyout((caddr_t) init_args,
CAST_USER_ADDR_T(init_addr),
(unsigned) sizeof(init_args));
argv[1] = (char *) init_addr;
init_addr += sizeof(init_args);
init_addr = (vm_offset_t)ROUND_PTR(char, init_addr);
argv[2] = (char *) 0;
(void) copyout((caddr_t) argv,
CAST_USER_ADDR_T(init_addr),
(unsigned) sizeof(argv));
init_exec_args.fname = CAST_USER_ADDR_T(argv[0]);
init_exec_args.argp = CAST_USER_ADDR_T((char **)init_addr);
init_exec_args.envp = CAST_USER_ADDR_T(0);
set_security_token(p);
error = execve(p,&init_exec_args,retval);
} while (error);
}
static int
load_return_to_errno(load_return_t lrtn)
{
switch (lrtn) {
case LOAD_SUCCESS:
return 0;
case LOAD_BADARCH:
return EBADARCH;
case LOAD_BADMACHO:
return EBADMACHO;
case LOAD_SHLIB:
return ESHLIBVERS;
case LOAD_NOSPACE:
case LOAD_RESOURCE:
return ENOMEM;
case LOAD_PROTECT:
return EACCES;
case LOAD_ENOENT:
return ENOENT;
case LOAD_IOERROR:
return EIO;
case LOAD_FAILURE:
default:
return EBADEXEC;
}
}
#include <mach/mach_types.h>
#include <mach/vm_prot.h>
#include <mach/semaphore.h>
#include <mach/sync_policy.h>
#include <kern/clock.h>
#include <mach/kern_return.h>
extern semaphore_t execve_semaphore;
static int
execargs_alloc(struct image_params *imgp)
{
kern_return_t kret;
kret = semaphore_wait(execve_semaphore);
if (kret != KERN_SUCCESS)
switch (kret) {
default:
return (EINVAL);
case KERN_INVALID_ADDRESS:
case KERN_PROTECTION_FAILURE:
return (EACCES);
case KERN_ABORTED:
case KERN_OPERATION_TIMED_OUT:
return (EINTR);
}
kret = kmem_alloc_pageable(bsd_pageable_map, (vm_offset_t *)&imgp->ip_strings, NCARGS + PAGE_SIZE);
imgp->ip_vdata = imgp->ip_strings + NCARGS;
if (kret != KERN_SUCCESS) {
semaphore_signal(execve_semaphore);
return (ENOMEM);
}
return (0);
}
static int
execargs_free(struct image_params *imgp)
{
kern_return_t kret;
kmem_free(bsd_pageable_map, (vm_offset_t)imgp->ip_strings, NCARGS + PAGE_SIZE);
imgp->ip_strings = NULL;
kret = semaphore_signal(execve_semaphore);
switch (kret) {
case KERN_INVALID_ADDRESS:
case KERN_PROTECTION_FAILURE:
return (EINVAL);
case KERN_ABORTED:
case KERN_OPERATION_TIMED_OUT:
return (EINTR);
case KERN_SUCCESS:
return(0);
default:
return (EINVAL);
}
}