Index: include/unistd.h =================================================================== --- include/unistd.h +++ include/unistd.h @@ -431,6 +431,7 @@ int faccessat(int, const char *, int, int); int fchownat(int, const char *, uid_t, gid_t, int); int fexecve(int, char *const [], char *const []); +int ffexecve(int, int, char *const [], char *const []); int linkat(int, const char *, int, const char *, int); ssize_t readlinkat(int, const char * __restrict, char * __restrict, size_t); int symlinkat(const char *, int, const char *); Index: lib/libc/sys/Makefile.inc =================================================================== --- lib/libc/sys/Makefile.inc +++ lib/libc/sys/Makefile.inc @@ -366,7 +366,8 @@ cpuset.2 cpuset_setid.2 MLINKS+=cpuset_getaffinity.2 cpuset_setaffinity.2 MLINKS+=dup.2 dup2.2 -MLINKS+=execve.2 fexecve.2 +MLINKS+=execve.2 fexecve.2 \ + execve.2 ffexecve.2 MLINKS+=extattr_get_file.2 extattr.2 \ extattr_get_file.2 extattr_delete_fd.2 \ extattr_get_file.2 extattr_delete_file.2 \ Index: lib/libc/sys/execve.2 =================================================================== --- lib/libc/sys/execve.2 +++ lib/libc/sys/execve.2 @@ -33,7 +33,8 @@ .Os .Sh NAME .Nm execve , -.Nm fexecve +.Nm fexecve, +.Nm ffexecve .Nd execute a file .Sh LIBRARY .Lb libc @@ -43,6 +44,8 @@ .Fn execve "const char *path" "char *const argv[]" "char *const envp[]" .Ft int .Fn fexecve "int fd" "char *const argv[]" "char *const envp[]" +.Ft int +.Fn ffexecve "int interp" "int fd" "char *const argv[]" "char *const envp[]" .Sh DESCRIPTION The .Fn execve @@ -62,6 +65,15 @@ .Fa fd instead of a .Fa path . +The +.Fn ffexecve +system call is equivalent to +.Fn fexecve +except that the run-time linker for the file is specified explicitly +by the file descriptor +.Fa interp +instead of implicitly via the kernel's default logic +(e.g., using the run-time linker specified in an ELF program header). This file is either an executable object file, or a file of data for an interpreter. An executable object file consists of an identifying header, Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1084,3 +1084,5 @@ id_t id, \ const struct vm_domain_policy *policy); } 550 AUE_FSYNC NOPROTO { int fdatasync(int fd); } +551 AUE_NULL STD { int ffexecve(int rtld, int bin, char **argv, \ + char **envv); } Index: sys/kern/capabilities.conf =================================================================== --- sys/kern/capabilities.conf +++ sys/kern/capabilities.conf @@ -176,6 +176,7 @@ ## such as disallowing privilege escalation. ## fexecve +ffexecve ## ## Allow flock(2), subject to capability rights. Index: sys/kern/imgact_aout.c =================================================================== --- sys/kern/imgact_aout.c +++ sys/kern/imgact_aout.c @@ -166,6 +166,14 @@ int error; /* + * The a.out image activator doesn't support an explicit interpreter + * (or any interpreter, for that matter). + */ + if (imgp->args->interpreter != -1) { + return (ENOEXEC); + } + + /* * Linux and *BSD binaries look very much alike, * only the machine id is different: * 0x64 for Linux, 0x86 for *BSD, 0x00 for BSDI. Index: sys/kern/imgact_binmisc.c =================================================================== --- sys/kern/imgact_binmisc.c +++ sys/kern/imgact_binmisc.c @@ -583,6 +583,13 @@ struct sbuf *sname; char *s, *d; + /* + * This image activator doesn't support an explicit interpreter. + */ + if (imgp->args->interpreter != -1) { + return (EINVAL); + } + /* Do we have an interpreter for the given image header? */ sx_slock(&interp_list_sx); if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) { Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -89,8 +89,10 @@ static int __elfN(check_header)(const Elf_Ehdr *hdr); static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel); -static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, +static int __elfN(load_file)(struct proc *p, struct vnode *vp, u_long *addr, u_long *entry, size_t pagesize); +static int __elfN(load_path)(struct proc *p, const char *path, u_long *addr, + u_long *entry, size_t pagesize); static int __elfN(load_section)(struct image_params *imgp, vm_ooffset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize); @@ -642,17 +644,15 @@ * the entry point for the loaded file. */ static int -__elfN(load_file)(struct proc *p, const char *file, u_long *addr, +__elfN(load_file)(struct proc *p, struct vnode *vp, u_long *addr, u_long *entry, size_t pagesize) { struct { - struct nameidata nd; struct vattr attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; - struct nameidata *nd; struct vattr *attr; struct image_params *imgp; vm_prot_t prot; @@ -660,17 +660,7 @@ u_long base_addr = 0; int error, i, numsegs; -#ifdef CAPABILITY_MODE - /* - * XXXJA: This check can go away once we are sufficiently confident - * that the checks in namei() are correct. - */ - if (IN_CAPABILITY_MODE(curthread)) - return (ECAPMODE); -#endif - tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK); - nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; @@ -683,14 +673,7 @@ imgp->image_header = NULL; imgp->object = NULL; imgp->execlabel = NULL; - - NDINIT(nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, file, curthread); - if ((error = namei(nd)) != 0) { - nd->ni_vp = NULL; - goto fail; - } - NDFREE(nd, NDF_ONLY_PNBUF); - imgp->vp = nd->ni_vp; + imgp->vp = vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. @@ -707,9 +690,9 @@ * Also make certain that the interpreter stays the same, so set * its VV_TEXT flag, too. */ - VOP_SET_TEXT(nd->ni_vp); + VOP_SET_TEXT(vp); - imgp->object = nd->ni_vp->v_object; + imgp->object = vp->v_object; hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) @@ -762,17 +745,47 @@ if (imgp->firstpage) exec_unmap_first_page(imgp); - if (nd->ni_vp) - vput(nd->ni_vp); - + vput(vp); free(tempdata, M_TEMP); return (error); } +/* + * Load a file into memory as specified by a path. + */ +static int +__elfN(load_path)(struct proc *p, const char *path, u_long *addr, + u_long *entry, size_t pagesize) +{ + struct nameidata nd; + struct vnode *vp = NULL; + int error; + +#ifdef CAPABILITY_MODE + /* + * XXXJA: This check can go away once we are sufficiently confident + * that the checks in namei() are correct. + */ + if (IN_CAPABILITY_MODE(curthread)) + return (ECAPMODE); +#endif + + NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, path, curthread); + error = namei(&nd); + vp = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + if (error != 0) { + return (error); + } + + return __elfN(load_file)(p, vp, addr, entry, pagesize); +} + static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { + cap_rights_t rights; struct thread *td; const Elf_Ehdr *hdr; const Elf_Phdr *phdr; @@ -786,7 +799,7 @@ u_long text_size, data_size, total_size, text_addr, data_addr; u_long seg_size, seg_addr, addr, baddr, et_dyn_addr, entry, proghdr; int32_t osrel; - int error, i, n, interp_name_len, have_interp; + int error, i, n, interp_fd, interp_name_len, have_interp; hdr = (const Elf_Ehdr *)imgp->image_header; @@ -836,6 +849,10 @@ break; case PT_INTERP: /* Path to interpreter */ + if (interp != NULL) { + /* ffexecve() is overriding the interpreter */ + break; + } if (phdr[i].p_filesz > MAXPATHLEN) { uprintf("Invalid PT_INTERP\n"); error = ENOEXEC; @@ -1031,12 +1048,32 @@ if (interp != NULL) { have_interp = FALSE; VOP_UNLOCK(imgp->vp, 0); - if (brand_info->emul_path != NULL && + if ((interp_fd = imgp->args->interpreter) != -1) { + /* ffexecve() is overriding the interpreter */ + error = fgetvp_exec(td, interp_fd, + cap_rights_init(&rights, CAP_FEXECVE), &imgp->vp); + if (error) { + uprintf("failed getting interpreter from FD %d", + interp_fd); + goto ret; + } + + error = vn_lock(imgp->vp, LK_SHARED | LK_RETRY); + if (error) { + goto ret; + } + + error = __elfN(load_file)(imgp->proc, imgp->vp, &addr, + &imgp->entry_addr, sv->sv_pagesize); + if (error == 0) + have_interp = TRUE; + } + if (!have_interp && brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); - error = __elfN(load_file)(imgp->proc, path, &addr, + error = __elfN(load_path)(imgp->proc, path, &addr, &imgp->entry_addr, sv->sv_pagesize); free(path, M_TEMP); if (error == 0) @@ -1045,13 +1082,13 @@ if (!have_interp && newinterp != NULL && (brand_info->interp_path == NULL || strcmp(interp, brand_info->interp_path) == 0)) { - error = __elfN(load_file)(imgp->proc, newinterp, &addr, + error = __elfN(load_path)(imgp->proc, newinterp, &addr, &imgp->entry_addr, sv->sv_pagesize); if (error == 0) have_interp = TRUE; } if (!have_interp) { - error = __elfN(load_file)(imgp->proc, interp, &addr, + error = __elfN(load_path)(imgp->proc, interp, &addr, &imgp->entry_addr, sv->sv_pagesize); } vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); Index: sys/kern/imgact_gzip.c =================================================================== --- sys/kern/imgact_gzip.c +++ sys/kern/imgact_gzip.c @@ -76,6 +76,13 @@ struct inflate infl; struct vmspace *vmspace; + /* + * This image activator doesn't support an explicit interpreter. + */ + if (imgp->args->interpreter != -1) { + return (EINVAL); + } + /* If these four are not OK, it isn't a gzip file */ if (p[0] != 0x1f) return -1; /* 0 Simply magic */ Index: sys/kern/imgact_shell.c =================================================================== --- sys/kern/imgact_shell.c +++ sys/kern/imgact_shell.c @@ -107,6 +107,14 @@ struct vattr vattr; struct sbuf *sname; + /* + * This image activator doesn't support an explicit interpreter. + * TODO(JA): consider adding support for an explicit shell interpreter + */ + if (imgp->args->interpreter != -1) { + return (EINVAL); + } + /* a shell script? */ if (((const short *)image_header)[0] != SHELLMAGIC) return (-1); Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -252,6 +252,35 @@ } #ifndef _SYS_SYSPROTO_H_ +struct ffexecve_args { + int interpreter; + int fd; + char **argv; + char **envv; +} +#endif +int +sys_ffexecve(struct thread *td, struct ffexecve_args *uap) +{ + struct image_args args; + struct vmspace *oldvmspace; + int error; + + error = pre_execve(td, &oldvmspace); + if (error != 0) + return (error); + error = exec_copyin_args(&args, NULL, UIO_SYSSPACE, + uap->argv, uap->envv); + if (error == 0) { + args.interpreter = uap->interpreter; + args.fd = uap->fd; + error = kern_execve(td, &args, NULL); + } + post_execve(td, error, oldvmspace); + return (error); +} + +#ifndef _SYS_SYSPROTO_H_ struct __mac_execve_args { char *fname; char **argv; @@ -498,7 +527,8 @@ * so that it can be used by process_exec handlers to determine * credential/setid changes. * - * Don't honor setuid/setgid if the filesystem prohibits it or if + * Don't honor setuid/setgid if the filesystem prohibits it, if + * employing a user-specified run-time interpreter or if * the process is being traced. * * We disable setuid/setgid/etc in capability mode on the basis @@ -523,6 +553,7 @@ #endif if (credential_changing && + (imgp->args->interpreter == -1) && #ifdef CAPABILITY_MODE ((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) && #endif @@ -1172,6 +1203,11 @@ return (EFAULT); /* + * Don't override the imgact-specified interpreter by default. + */ + args->interpreter = -1; + + /* * Allocate demand-paged memory for the file name, argument, and * environment strings. */ Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -997,6 +997,8 @@ id_t id, const struct \ vm_domain_policy_entry *policy); } 550 AUE_FSYNC STD { int fdatasync(int fd); } +551 AUE_NULL STD { int ffexecve(int interpreter, int fd, \ + char **argv, char **envv); } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: sys/sys/imgact.h =================================================================== --- sys/sys/imgact.h +++ sys/sys/imgact.h @@ -51,6 +51,7 @@ int stringspace; /* space left in arg & env buffer */ int argc; /* count of argument strings */ int envc; /* count of environment strings */ + int interpreter; /* descriptor of interpreter to override with */ int fd; /* file descriptor of the executable */ struct filedesc *fdp; /* new file descriptor table */ };