diff options
Diffstat (limited to 'usr/src/uts/common/exec')
| -rw-r--r-- | usr/src/uts/common/exec/aout/aout.c | 5 | ||||
| -rw-r--r-- | usr/src/uts/common/exec/elf/elf.c | 257 | ||||
| -rw-r--r-- | usr/src/uts/common/exec/intp/intp.c | 81 | ||||
| -rw-r--r-- | usr/src/uts/common/exec/java/java.c | 9 | ||||
| -rw-r--r-- | usr/src/uts/common/exec/shbin/shbin.c | 9 |
5 files changed, 295 insertions, 66 deletions
diff --git a/usr/src/uts/common/exec/aout/aout.c b/usr/src/uts/common/exec/aout/aout.c index fc45bd9544..5dbb2ed28c 100644 --- a/usr/src/uts/common/exec/aout/aout.c +++ b/usr/src/uts/common/exec/aout/aout.c @@ -22,6 +22,7 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2011 Bayard G. Bell. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <sys/types.h> @@ -54,7 +55,7 @@ static int aoutexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, int level, long *execsz, int setid, - caddr_t exec_file, cred_t *cred, int brand_action); + caddr_t exec_file, cred_t *cred, int *brand_action); static int get_aout_head(struct vnode **vpp, struct exdata *edp, long *execsz, int *isdyn); static int aoutcore(vnode_t *vp, proc_t *pp, cred_t *credp, @@ -130,7 +131,7 @@ _info(struct modinfo *modinfop) static int aoutexec(vnode_t *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, - caddr_t exec_file, cred_t *cred, int brand_action) + caddr_t exec_file, cred_t *cred, int *brand_action) { auxv32_t auxflags_auxv32; int error; diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c index 0eeb4e798c..edde693ac0 100644 --- a/usr/src/uts/common/exec/elf/elf.c +++ b/usr/src/uts/common/exec/elf/elf.c @@ -26,7 +26,7 @@ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -163,12 +163,16 @@ dtrace_safe_phdr(Phdr *phdrp, struct uarg *args, uintptr_t base) } /* - * Map in the executable pointed to by vp. Returns 0 on success. + * Map in the executable pointed to by vp. Returns 0 on success. Note that + * this function currently has the maximum number of arguments allowed by + * modstubs on x86 (MAXNARG)! Do _not_ add to this function signature without + * adding to MAXNARG. (Better yet, do not add to this monster of a function + * signature!) */ int mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, - intptr_t *voffset, caddr_t exec_file, int *interp, caddr_t *bssbase, - caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap) + intptr_t *voffset, caddr_t exec_file, char **interpp, caddr_t *bssbase, + caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap, uintptr_t *minaddrp) { size_t len; struct vattr vat; @@ -180,6 +184,7 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, Phdr *junk = NULL; Phdr *dynphdr = NULL; Phdr *dtrphdr = NULL; + char *interp = NULL; uintptr_t lddata; long execsz; intptr_t minaddr; @@ -187,6 +192,9 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, if (lddatap != NULL) *lddatap = NULL; + if (minaddrp != NULL) + *minaddrp = NULL; + if (error = execpermissions(vp, &vat, args)) { uprintf("%s: Cannot execute %s\n", exec_file, args->pathname); return (error); @@ -212,25 +220,65 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, &junk, &dtrphdr, NULL, bssbase, brkbase, voffset, &minaddr, len, &execsz, brksize)) { uprintf("%s: Cannot map %s\n", exec_file, args->pathname); + if (uphdr != NULL && uphdr->p_flags == 0) + kmem_free(uphdr, sizeof (Phdr)); kmem_free(phdrbase, phdrsize); return (error); } + if (minaddrp != NULL) + *minaddrp = minaddr; + /* - * Inform our caller if the executable needs an interpreter. + * If the executable requires an interpreter, determine its name. */ - *interp = (dynphdr == NULL) ? 0 : 1; + if (dynphdr != NULL) { + ssize_t resid; + + if (dynphdr->p_filesz > MAXPATHLEN || dynphdr->p_filesz == 0) { + uprintf("%s: Invalid interpreter\n", exec_file); + kmem_free(phdrbase, phdrsize); + return (ENOEXEC); + } + + interp = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + if ((error = vn_rdwr(UIO_READ, vp, interp, dynphdr->p_filesz, + (offset_t)dynphdr->p_offset, UIO_SYSSPACE, 0, + (rlim64_t)0, CRED(), &resid)) != 0 || resid != 0 || + interp[dynphdr->p_filesz - 1] != '\0') { + uprintf("%s: Cannot obtain interpreter pathname\n", + exec_file); + kmem_free(interp, MAXPATHLEN); + kmem_free(phdrbase, phdrsize); + return (error != 0 ? error : ENOEXEC); + } + } /* * If this is a statically linked executable, voffset should indicate * the address of the executable itself (it normally holds the address * of the interpreter). */ - if (ehdr->e_type == ET_EXEC && *interp == 0) + if (ehdr->e_type == ET_EXEC && interp == NULL) *voffset = minaddr; + /* + * If the caller has asked for the interpreter name, return it (it's + * up to the caller to free it); if the caller hasn't asked for it, + * free it ourselves. + */ + if (interpp != NULL) { + *interpp = interp; + } else if (interp != NULL) { + kmem_free(interp, MAXPATHLEN); + } + if (uphdr != NULL) { *uphdr_vaddr = uphdr->p_vaddr; + + if (uphdr->p_flags == 0) + kmem_free(uphdr, sizeof (Phdr)); } else { *uphdr_vaddr = (Addr)-1; } @@ -243,13 +291,13 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr, int elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred, - int brand_action) + int *brand_action) { caddr_t phdrbase = NULL; caddr_t bssbase = 0; caddr_t brkbase = 0; size_t brksize = 0; - ssize_t dlnsize; + ssize_t dlnsize, nsize = 0; aux_entry_t *aux; int error; ssize_t resid; @@ -273,6 +321,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, int hasauxv = 0; int hasdy = 0; int branded = 0; + int dynuphdr = 0; struct proc *p = ttoproc(curthread); struct user *up = PTOU(p); @@ -339,14 +388,46 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, #endif /* _LP64 */ /* - * We delay invoking the brand callback until we've figured out - * what kind of elf binary we're trying to run, 32-bit or 64-bit. - * We do this because now the brand library can just check - * args->to_model to see if the target is 32-bit or 64-bit without - * having do duplicate all the code above. + * We delay invoking the brand callback until we've figured out what + * kind of elf binary we're trying to run, 32-bit or 64-bit. We do this + * because now the brand library can just check args->to_model to see if + * the target is 32-bit or 64-bit without having do duplicate all the + * code above. + * + * We also give the brand a chance to indicate that based on the ELF + * OSABI of the target binary it should become unbranded and optionally + * indicate that it should be treated as existing in a specific prefix. + * + * Note that if a brand opts to go down this route it does not actually + * end up being debranded. In other words, future programs that exec + * will still be considered for branding unless this escape hatch is + * used. Consider the case of lx brand for example. If a user runs + * /native/usr/sbin/dtrace -c /bin/ls, the isaexec and normal executable + * of DTrace that's in /native will take this escape hatch and be run + * and interpreted using the normal system call table; however, the + * execution of a non-illumos binary in the form of /bin/ls will still + * be branded and be subject to all of the normal actions of the brand. + * + * The level checks associated with brand handling below are used to + * prevent a loop since the brand elfexec function typically comes back + * through this function. We must check <= here since the nested + * handling in the #! interpreter code will increment the level before + * calling gexec to run the final elfexec interpreter. */ - if ((level < 2) && - (brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) { + if ((level <= INTP_MAXDEPTH) && (*brand_action != EBA_NATIVE) && + (PROC_IS_BRANDED(p)) && (BROP(p)->b_native_exec != NULL)) { + if (BROP(p)->b_native_exec(ehdrp->e_ident[EI_OSABI], + &args->brand_nroot) == B_TRUE) { + ASSERT(ehdrp->e_ident[EI_OSABI]); + *brand_action = EBA_NATIVE; + /* Add one for the trailing '/' in the path */ + if (args->brand_nroot != NULL) + nsize = strlen(args->brand_nroot) + 1; + } + } + + if ((level <= INTP_MAXDEPTH) && + (*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) { error = BROP(p)->b_elfexec(vp, uap, args, idatap, level + 1, execsz, setid, exec_file, cred, brand_action); @@ -417,14 +498,15 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * AT_BASE * AT_FLAGS * AT_PAGESZ + * AT_RANDOM (added in stk_copyout) * AT_SUN_AUXFLAGS * AT_SUN_HWCAP * AT_SUN_HWCAP2 - * AT_SUN_PLATFORM (added in stk_copyout) - * AT_SUN_EXECNAME (added in stk_copyout) + * AT_SUN_PLATFORM (added in stk_copyout) + * AT_SUN_EXECNAME (added in stk_copyout) * AT_NULL * - * total == 9 + * total == 10 */ if (hasdy && hasu) { /* @@ -439,7 +521,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * * total = 5 */ - args->auxsize = (9 + 5) * sizeof (aux_entry_t); + args->auxsize = (10 + 5) * sizeof (aux_entry_t); } else if (hasdy) { /* * Has PT_INTERP but no PT_PHDR @@ -449,9 +531,9 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * * total = 2 */ - args->auxsize = (9 + 2) * sizeof (aux_entry_t); + args->auxsize = (10 + 2) * sizeof (aux_entry_t); } else { - args->auxsize = 9 * sizeof (aux_entry_t); + args->auxsize = 10 * sizeof (aux_entry_t); } } else { args->auxsize = 0; @@ -464,13 +546,21 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, if (args->emulator != NULL) args->auxsize += sizeof (aux_entry_t); - if ((brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) { + /* + * If this is a native binary that's been given a modified interpreter + * root, inform it that the native system exists at that root. + */ + if (args->brand_nroot != NULL) { + args->auxsize += sizeof (aux_entry_t); + } + + if ((*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) { branded = 1; /* - * We will be adding 4 entries to the aux vectors. One for - * the the brandname and 3 for the brand specific aux vectors. + * We will be adding 5 entries to the aux vectors. One for + * the the brandname and 4 for the brand specific aux vectors. */ - args->auxsize += 4 * sizeof (aux_entry_t); + args->auxsize += 5 * sizeof (aux_entry_t); } /* Hardware/Software capabilities */ @@ -501,7 +591,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, aux = bigwad->elfargs; /* * Move args to the user's stack. - * This can fill in the AT_SUN_PLATFORM and AT_SUN_EXECNAME aux entries. + * This can fill in the AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM + * aux entries. */ if ((error = exec_args(uap, args, idatap, (void **)&aux)) != 0) { if (error == -1) { @@ -528,6 +619,14 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, len, execsz, &brksize)) != 0) goto bad; + if (uphdr != NULL) { + /* + * Our uphdr has been dynamically allocated if (and only if) + * its program header flags are clear. + */ + dynuphdr = (uphdr->p_flags == 0); + } + if (uphdr != NULL && dyphdr == NULL) goto bad; @@ -542,17 +641,22 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, char *p; struct vnode *nvp; - dlnsize = dyphdr->p_filesz; + dlnsize = dyphdr->p_filesz + nsize; if (dlnsize > MAXPATHLEN || dlnsize <= 0) goto bad; + if (nsize != 0) { + bcopy(args->brand_nroot, dlnp, nsize - 1); + dlnp[nsize - 1] = '/'; + } + /* * Read in "interpreter" pathname. */ - if ((error = vn_rdwr(UIO_READ, vp, dlnp, dyphdr->p_filesz, - (offset_t)dyphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0, - CRED(), &resid)) != 0) { + if ((error = vn_rdwr(UIO_READ, vp, dlnp + nsize, + dyphdr->p_filesz, (offset_t)dyphdr->p_offset, UIO_SYSSPACE, + 0, (rlim64_t)0, CRED(), &resid)) != 0) { uprintf("%s: Cannot obtain interpreter pathname\n", exec_file); goto bad; @@ -688,9 +792,10 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, dtrphdr = NULL; - error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, &junk, &junk, + error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, NULL, &junk, &junk, &dtrphdr, NULL, NULL, NULL, &voffset, NULL, len, execsz, NULL); + if (error || junk != NULL) { VN_RELE(nvp); uprintf("%s: Cannot map %s\n", exec_file, dlnp); @@ -718,8 +823,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, if (hasauxv) { int auxf = AF_SUN_HWCAPVERIFY; /* - * Note: AT_SUN_PLATFORM and AT_SUN_EXECNAME were filled in via - * exec_args() + * Note: AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM were + * filled in via exec_args() */ ADDAUX(aux, AT_BASE, voffset) ADDAUX(aux, AT_FLAGS, at_flags) @@ -747,7 +852,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, * malicious user within the zone from crafting a wrapper to * run native suid commands with unsecure libraries interposed. */ - if ((brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) && + if ((*brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) && (setid &= ~EXECSETID_SETID) != 0)) auxf &= ~AF_SUN_SETUGID; @@ -789,6 +894,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, ADDAUX(aux, AT_SUN_BRAND_AUX1, 0) ADDAUX(aux, AT_SUN_BRAND_AUX2, 0) ADDAUX(aux, AT_SUN_BRAND_AUX3, 0) + ADDAUX(aux, AT_SUN_BRAND_AUX4, 0) } ADDAUX(aux, AT_NULL, 0) @@ -896,6 +1002,8 @@ bad: if (error == 0) error = ENOEXEC; out: + if (dynuphdr) + kmem_free(uphdr, sizeof (Phdr)); if (phdrbase != NULL) kmem_free(phdrbase, phdrsize); if (cap != NULL) @@ -1182,7 +1290,7 @@ mapelfexec( size_t *brksize) { Phdr *phdr; - int i, prot, error; + int i, prot, error, lastprot = 0; caddr_t addr = NULL; size_t zfodsz; int ptload = 0; @@ -1190,6 +1298,7 @@ mapelfexec( off_t offset; int hsize = ehdr->e_phentsize; caddr_t mintmp = (caddr_t)-1; + uintptr_t lastaddr = NULL; extern int use_brk_lpg; if (ehdr->e_type == ET_DYN) { @@ -1220,13 +1329,11 @@ mapelfexec( } else { *voffset = 0; } + phdr = (Phdr *)phdrbase; for (i = nphdrs; i > 0; i--) { switch (phdr->p_type) { case PT_LOAD: - if ((*dyphdr != NULL) && (*uphdr == NULL)) - return (0); - ptload = 1; prot = PROT_USER; if (phdr->p_flags & PF_R) @@ -1238,6 +1345,34 @@ mapelfexec( addr = (caddr_t)((uintptr_t)phdr->p_vaddr + *voffset); + if ((*dyphdr != NULL) && uphdr != NULL && + (*uphdr == NULL)) { + /* + * The PT_PHDR program header is, strictly + * speaking, optional. If we find that this + * is missing, we will determine the location + * of the program headers based on the address + * of the lowest PT_LOAD segment (namely, this + * one): we subtract the p_offset to get to + * the ELF header and then add back the program + * header offset to get to the program headers. + * We then cons up a Phdr that corresponds to + * the (missing) PT_PHDR, setting the flags + * to 0 to denote that this is artificial and + * should (must) be freed by the caller. + */ + Phdr *cons; + + cons = kmem_zalloc(sizeof (Phdr), KM_SLEEP); + + cons->p_flags = 0; + cons->p_type = PT_PHDR; + cons->p_vaddr = ((uintptr_t)addr - + phdr->p_offset) + ehdr->e_phoff; + + *uphdr = cons; + } + /* * Keep track of the segment with the lowest starting * address. @@ -1245,6 +1380,41 @@ mapelfexec( if (addr < mintmp) mintmp = addr; + /* + * Segments need not correspond to page boundaries: + * they are permitted to share a page. If two PT_LOAD + * segments share the same page, and the permissions + * of the segments differ, the behavior is historically + * that the permissions of the latter segment are used + * for the page that the two segments share. This is + * also historically a non-issue: binaries generated + * by most anything will make sure that two PT_LOAD + * segments with differing permissions don't actually + * share any pages. However, there exist some crazy + * things out there (including at least an obscure + * Portuguese teaching language called G-Portugol) that + * actually do the wrong thing and expect it to work: + * they have a segment with execute permission share + * a page with a subsequent segment that does not + * have execute permissions and expect the resulting + * shared page to in fact be executable. To accommodate + * such broken link editors, we take advantage of a + * latitude explicitly granted to the loader: it is + * permitted to make _any_ PT_LOAD segment executable + * (provided that it is readable or writable). If we + * see that we're sharing a page and that the previous + * page was executable, we will add execute permissions + * to our segment. + */ + if (btop(lastaddr) == btop((uintptr_t)addr) && + (phdr->p_flags & (PF_R | PF_W)) && + (lastprot & PROT_EXEC)) { + prot |= PROT_EXEC; + } + + lastaddr = (uintptr_t)addr + phdr->p_filesz; + lastprot = prot; + zfodsz = (size_t)phdr->p_memsz - phdr->p_filesz; offset = phdr->p_offset; @@ -1319,9 +1489,12 @@ mapelfexec( break; case PT_PHDR: - if (ptload) + if (ptload || phdr->p_flags == 0) goto bad; - *uphdr = phdr; + + if (uphdr != NULL) + *uphdr = phdr; + break; case PT_NULL: @@ -2170,7 +2343,7 @@ static struct modlexec modlexec = { extern int elf32exec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap, int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred, - int brand_action); + int *brand_action); extern int elf32core(vnode_t *vp, proc_t *p, cred_t *credp, rlim64_t rlimit, int sig, core_content_t content); diff --git a/usr/src/uts/common/exec/intp/intp.c b/usr/src/uts/common/exec/intp/intp.c index 998c0f1f8e..b4a404cc98 100644 --- a/usr/src/uts/common/exec/intp/intp.c +++ b/usr/src/uts/common/exec/intp/intp.c @@ -22,6 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. + * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1988 AT&T */ @@ -46,6 +47,7 @@ #include <sys/exec.h> #include <sys/kmem.h> #include <sys/note.h> +#include <sys/sdt.h> /* * This is the loadable module wrapper. @@ -53,7 +55,7 @@ #include <sys/modctl.h> extern int intpexec(struct vnode *, struct execa *, struct uarg *, - struct intpdata *, int, long *, int, caddr_t, struct cred *, int); + struct intpdata *, int, long *, int, caddr_t, struct cred *, int *); static struct execsw esw = { intpmagicstr, @@ -137,19 +139,19 @@ getintphead(struct vnode *vp, struct intpdata *idatap) ; if (*cp == '\0') return (ENOEXEC); - idatap->intp_name = cp; + idatap->intp_name[0] = cp; while (*cp && *cp != ' ') cp++; - if (*cp == '\0') - idatap->intp_arg = NULL; - else { + if (*cp == '\0') { + idatap->intp_arg[0] = NULL; + } else { *cp++ = '\0'; while (*cp == ' ') cp++; if (*cp == '\0') - idatap->intp_arg = NULL; + idatap->intp_arg[0] = NULL; else { - idatap->intp_arg = cp; + idatap->intp_arg[0] = cp; while (*cp && *cp != ' ') cp++; *cp = '\0'; @@ -158,6 +160,24 @@ getintphead(struct vnode *vp, struct intpdata *idatap) return (0); } +/* + * We support nested interpreters up to a depth of INTP_MAXDEPTH (this value + * matches the depth on Linux). When a nested interpreter is in use, the + * previous name and argument must be passed along. We use the intpdata_t + * name and argument arrays for this. In the normal, non-nested case, only the + * first element in those arrays will be populated. + * + * For setid scripts the "script hole" is a security race condition between + * when we exec the interpreter and when the interpreter reads the script. We + * handle this below for the initial script, but we don't allow setid scripts + * when using nested interpreters. Because gexec only modifies the credentials + * for a setid script at level 0, then if we come back through for a nested + * interpreter we know that args->fname will be set (the first script is setid) + * and we can return an error. If an intermediate nested interpreter is setid + * then it will not be run with different credentials because of the gexec + * handling, so it is effectively no longer setid and we don't have to worry + * about the "script hole". + */ int intpexec( struct vnode *vp, @@ -169,9 +189,8 @@ intpexec( int setid, caddr_t exec_file, struct cred *cred, - int brand_action) + int *brand_action) { - _NOTE(ARGUNUSED(brand_action)) vnode_t *nvp; int error = 0; struct intpdata idata; @@ -181,12 +200,15 @@ intpexec( char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */ int fd = -1; - if (level) { /* Can't recurse */ - error = ENOEXEC; + if (level >= INTP_MAXDEPTH) { /* Can't recurse past maxdepth */ + error = ELOOP; goto bad; } - ASSERT(idatap == (struct intpdata *)NULL); + if (level == 0) + ASSERT(idatap == (struct intpdata *)NULL); + + bzero(&idata, sizeof (intpdata_t)); /* * Allocate a buffer to read in the interpreter pathname. @@ -198,7 +220,7 @@ intpexec( /* * Look the new vnode up. */ - if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &intppn)) + if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &intppn)) goto fail; pn_alloc(&resolvepn); if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { @@ -206,12 +228,43 @@ intpexec( pn_free(&intppn); goto fail; } + + if (level > 0) { + /* + * We have a nested interpreter. The previous name(s) and + * argument(s) need to be passed along. We also keep track + * of how often this zone uses nested interpreters. + */ + int i; + + atomic_inc_32(&curproc->p_zone->zone_nested_intp); + + ASSERT(idatap != NULL); + /* since we're shifting up, loop stops one short */ + for (i = 0; i < (INTP_MAXDEPTH - 1); i++) { + idata.intp_name[i + 1] = idatap->intp_name[i]; + idata.intp_arg[i + 1] = idatap->intp_arg[i]; + } + + DTRACE_PROBE3(nested__intp, int, level, void *, &idata, + void *, nvp); + } + opath = args->pathname; args->pathname = resolvepn.pn_path; /* don't free resolvepn until we are done with args */ pn_free(&intppn); /* + * Disallow setuid or additional privilege execution for nested + * interpreters. + */ + if (level > 0 && args->fname != NULL) { + error = ENOEXEC; + goto done; + } + + /* * When we're executing a set-uid script resulting in uids * mismatching or when we execute with additional privileges, * we close the "replace script between exec and open by shell" @@ -228,7 +281,7 @@ intpexec( } error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred, - EBA_NONE); + brand_action); if (!error) { /* diff --git a/usr/src/uts/common/exec/java/java.c b/usr/src/uts/common/exec/java/java.c index 7cf9126e8d..5170fda5cb 100644 --- a/usr/src/uts/common/exec/java/java.c +++ b/usr/src/uts/common/exec/java/java.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ /* @@ -85,7 +86,7 @@ char *jexec_arg = "-jar"; static int javaexec(vnode_t *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, - caddr_t execfile, cred_t *cred, int brand_action) + caddr_t execfile, cred_t *cred, int *brand_action) { struct intpdata idata; int error; @@ -145,9 +146,9 @@ javaexec(vnode_t *vp, struct execa *uap, struct uarg *args, * Find and invoke the Java runtime environment on the file */ idata.intp = NULL; - idata.intp_name = jexec; - idata.intp_arg = jexec_arg; - if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &lookpn)) + idata.intp_name[0] = jexec; + idata.intp_arg[0] = jexec_arg; + if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &lookpn)) return (error); pn_alloc(&resolvepn); if (error = lookuppn(&lookpn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { diff --git a/usr/src/uts/common/exec/shbin/shbin.c b/usr/src/uts/common/exec/shbin/shbin.c index a6cf129d9d..016d87b9ef 100644 --- a/usr/src/uts/common/exec/shbin/shbin.c +++ b/usr/src/uts/common/exec/shbin/shbin.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ #include <sys/types.h> @@ -58,7 +59,7 @@ shbinexec( int setid, caddr_t exec_file, struct cred *cred, - int brand_action); + int *brand_action); #define SHBIN_CNTL(x) ((x)&037) #define SHBINMAGIC_LEN 4 @@ -162,7 +163,7 @@ shbinexec( int setid, caddr_t exec_file, struct cred *cred, - int brand_action) + int *brand_action) { _NOTE(ARGUNUSED(brand_action)) vnode_t *nvp; @@ -221,8 +222,8 @@ shbinexec( * a script's name starts with a '-' character. */ idata.intp = NULL; - idata.intp_name = shell_list[i]; - idata.intp_arg = "--"; + idata.intp_name[0] = shell_list[i]; + idata.intp_arg[0] = "--"; opath = args->pathname; args->pathname = resolvepn.pn_path; |
