xref: /onnv-gate/usr/src/uts/common/exec/shbin/shbin.c (revision 11736:63a134e1f09c)
18462SApril.Chin@Sun.COM /*
28462SApril.Chin@Sun.COM  * CDDL HEADER START
38462SApril.Chin@Sun.COM  *
48462SApril.Chin@Sun.COM  * The contents of this file are subject to the terms of the
58462SApril.Chin@Sun.COM  * Common Development and Distribution License (the "License").
68462SApril.Chin@Sun.COM  * You may not use this file except in compliance with the License.
78462SApril.Chin@Sun.COM  *
88462SApril.Chin@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98462SApril.Chin@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108462SApril.Chin@Sun.COM  * See the License for the specific language governing permissions
118462SApril.Chin@Sun.COM  * and limitations under the License.
128462SApril.Chin@Sun.COM  *
138462SApril.Chin@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148462SApril.Chin@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158462SApril.Chin@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168462SApril.Chin@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178462SApril.Chin@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188462SApril.Chin@Sun.COM  *
198462SApril.Chin@Sun.COM  * CDDL HEADER END
208462SApril.Chin@Sun.COM  */
218462SApril.Chin@Sun.COM 
228462SApril.Chin@Sun.COM /*
23*11736SDonghai.Qiao@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
248462SApril.Chin@Sun.COM  * Use is subject to license terms.
258462SApril.Chin@Sun.COM  */
268462SApril.Chin@Sun.COM 
278462SApril.Chin@Sun.COM #include <sys/types.h>
288462SApril.Chin@Sun.COM #include <sys/param.h>
298462SApril.Chin@Sun.COM #include <sys/sysmacros.h>
308462SApril.Chin@Sun.COM #include <sys/signal.h>
318462SApril.Chin@Sun.COM #include <sys/cred.h>
328462SApril.Chin@Sun.COM #include <sys/user.h>
338462SApril.Chin@Sun.COM #include <sys/errno.h>
348462SApril.Chin@Sun.COM #include <sys/vnode.h>
358462SApril.Chin@Sun.COM #include <sys/proc.h>
368462SApril.Chin@Sun.COM #include <sys/cmn_err.h>
378462SApril.Chin@Sun.COM #include <sys/debug.h>
388462SApril.Chin@Sun.COM #include <sys/pathname.h>
398462SApril.Chin@Sun.COM #include <sys/disp.h>
408462SApril.Chin@Sun.COM #include <sys/exec.h>
418462SApril.Chin@Sun.COM #include <sys/kmem.h>
428462SApril.Chin@Sun.COM #include <sys/note.h>
438462SApril.Chin@Sun.COM 
448462SApril.Chin@Sun.COM /*
458462SApril.Chin@Sun.COM  * This is the loadable module wrapper.
468462SApril.Chin@Sun.COM  */
478462SApril.Chin@Sun.COM #include <sys/modctl.h>
488462SApril.Chin@Sun.COM 
4910205Sroland.mainz@nrubsig.org /* Local prototypes */
5010205Sroland.mainz@nrubsig.org static int
518462SApril.Chin@Sun.COM shbinexec(
528462SApril.Chin@Sun.COM 	struct vnode *vp,
538462SApril.Chin@Sun.COM 	struct execa *uap,
548462SApril.Chin@Sun.COM 	struct uarg *args,
558462SApril.Chin@Sun.COM 	struct intpdata *idatap,
568462SApril.Chin@Sun.COM 	int level,
578462SApril.Chin@Sun.COM 	long *execsz,
588462SApril.Chin@Sun.COM 	int setid,
598462SApril.Chin@Sun.COM 	caddr_t exec_file,
608462SApril.Chin@Sun.COM 	struct cred *cred,
618462SApril.Chin@Sun.COM 	int brand_action);
628462SApril.Chin@Sun.COM 
638462SApril.Chin@Sun.COM #define	SHBIN_CNTL(x)	((x)&037)
648462SApril.Chin@Sun.COM #define	SHBINMAGIC_LEN	4
658462SApril.Chin@Sun.COM extern char shbinmagicstr[];
668462SApril.Chin@Sun.COM 
678462SApril.Chin@Sun.COM /*
688462SApril.Chin@Sun.COM  * Our list where we may find a copy of ksh93. The ordering is:
698462SApril.Chin@Sun.COM  * 1. 64bit (may not be installed or not supported in hardware)
708462SApril.Chin@Sun.COM  * 2. 32bit
718462SApril.Chin@Sun.COM  * 3. Use /sbin/ksh93 when /usr is not available
728462SApril.Chin@Sun.COM  *
738462SApril.Chin@Sun.COM  * ([1] and [2] explicitly bypass /usr/bin/ksh93 to avoid the
748462SApril.Chin@Sun.COM  * isaexec overhead).
758462SApril.Chin@Sun.COM  */
768462SApril.Chin@Sun.COM static char *shell_list[] =
778462SApril.Chin@Sun.COM {
788462SApril.Chin@Sun.COM /* Bypass /usr/bin/ksh93 (which is "isaexec") for performance */
798462SApril.Chin@Sun.COM #if defined(__sparc)
808462SApril.Chin@Sun.COM 	"/usr/bin/sparcv9/ksh93",
818462SApril.Chin@Sun.COM 	"/usr/bin/sparcv7/ksh93",
828462SApril.Chin@Sun.COM #elif defined(__amd64)
838462SApril.Chin@Sun.COM 	"/usr/bin/amd64/ksh93",
848462SApril.Chin@Sun.COM 	"/usr/bin/i86/ksh93",
858462SApril.Chin@Sun.COM #elif defined(__i386)
868462SApril.Chin@Sun.COM 	"/usr/bin/i86/ksh93",
878462SApril.Chin@Sun.COM #else
888462SApril.Chin@Sun.COM #error "Unrecognized platform/CPU (use /usr/bin/ksh93 when in doubt)."
898462SApril.Chin@Sun.COM #endif
908462SApril.Chin@Sun.COM 	"/sbin/ksh93",
918462SApril.Chin@Sun.COM 	NULL
928462SApril.Chin@Sun.COM };
938462SApril.Chin@Sun.COM 
948462SApril.Chin@Sun.COM static struct execsw esw = {
958462SApril.Chin@Sun.COM 	shbinmagicstr,
968462SApril.Chin@Sun.COM 	0,
978462SApril.Chin@Sun.COM 	SHBINMAGIC_LEN,
988462SApril.Chin@Sun.COM 	shbinexec,
998462SApril.Chin@Sun.COM 	NULL
1008462SApril.Chin@Sun.COM };
1018462SApril.Chin@Sun.COM 
1028462SApril.Chin@Sun.COM /*
1038462SApril.Chin@Sun.COM  * Module linkage information for the kernel.
1048462SApril.Chin@Sun.COM  */
1058462SApril.Chin@Sun.COM extern struct mod_ops mod_execops;
1068462SApril.Chin@Sun.COM 
1078462SApril.Chin@Sun.COM static struct modlexec modlexec = {
1088462SApril.Chin@Sun.COM 	&mod_execops, "exec mod for shell binaries (ksh93)", &esw
1098462SApril.Chin@Sun.COM };
1108462SApril.Chin@Sun.COM 
1118462SApril.Chin@Sun.COM static struct modlinkage modlinkage = {
1128462SApril.Chin@Sun.COM 	MODREV_1, (void *)&modlexec, NULL
1138462SApril.Chin@Sun.COM };
1148462SApril.Chin@Sun.COM 
1158462SApril.Chin@Sun.COM int
_init(void)1168462SApril.Chin@Sun.COM _init(void)
1178462SApril.Chin@Sun.COM {
1188462SApril.Chin@Sun.COM 	return (mod_install(&modlinkage));
1198462SApril.Chin@Sun.COM }
1208462SApril.Chin@Sun.COM 
1218462SApril.Chin@Sun.COM int
_fini(void)1228462SApril.Chin@Sun.COM _fini(void)
1238462SApril.Chin@Sun.COM {
1248462SApril.Chin@Sun.COM 	return (mod_remove(&modlinkage));
1258462SApril.Chin@Sun.COM }
1268462SApril.Chin@Sun.COM 
1278462SApril.Chin@Sun.COM int
_info(struct modinfo * modinfop)1288462SApril.Chin@Sun.COM _info(struct modinfo *modinfop)
1298462SApril.Chin@Sun.COM {
1308462SApril.Chin@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1318462SApril.Chin@Sun.COM }
1328462SApril.Chin@Sun.COM 
1338462SApril.Chin@Sun.COM static int
checkshbinmagic(struct vnode * vp)1348462SApril.Chin@Sun.COM checkshbinmagic(struct vnode *vp)
1358462SApril.Chin@Sun.COM {
1368462SApril.Chin@Sun.COM 	int error;
1378462SApril.Chin@Sun.COM 	char linep[SHBINMAGIC_LEN];
1388462SApril.Chin@Sun.COM 	ssize_t resid;
1398462SApril.Chin@Sun.COM 
1408462SApril.Chin@Sun.COM 	/*
1418462SApril.Chin@Sun.COM 	 * Read the entire line and confirm that it starts with the magic
1428462SApril.Chin@Sun.COM 	 * sequence for compiled ksh93 shell scripts.
1438462SApril.Chin@Sun.COM 	 */
1448462SApril.Chin@Sun.COM 	if (error = vn_rdwr(UIO_READ, vp, linep, sizeof (linep), (offset_t)0,
1458462SApril.Chin@Sun.COM 	    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid))
1468462SApril.Chin@Sun.COM 		return (error);
1478462SApril.Chin@Sun.COM 
1488462SApril.Chin@Sun.COM 	if (memcmp(linep, shbinmagicstr, SHBINMAGIC_LEN) != 0)
1498462SApril.Chin@Sun.COM 		return (ENOEXEC);
1508462SApril.Chin@Sun.COM 
1518462SApril.Chin@Sun.COM 	return (0);
1528462SApril.Chin@Sun.COM }
1538462SApril.Chin@Sun.COM 
15410205Sroland.mainz@nrubsig.org static int
shbinexec(struct vnode * vp,struct execa * uap,struct uarg * args,struct intpdata * idatap,int level,long * execsz,int setid,caddr_t exec_file,struct cred * cred,int brand_action)1558462SApril.Chin@Sun.COM shbinexec(
1568462SApril.Chin@Sun.COM 	struct vnode *vp,
1578462SApril.Chin@Sun.COM 	struct execa *uap,
1588462SApril.Chin@Sun.COM 	struct uarg *args,
1598462SApril.Chin@Sun.COM 	struct intpdata *idatap,
1608462SApril.Chin@Sun.COM 	int level,
1618462SApril.Chin@Sun.COM 	long *execsz,
1628462SApril.Chin@Sun.COM 	int setid,
1638462SApril.Chin@Sun.COM 	caddr_t exec_file,
1648462SApril.Chin@Sun.COM 	struct cred *cred,
1658462SApril.Chin@Sun.COM 	int brand_action)
1668462SApril.Chin@Sun.COM {
1678462SApril.Chin@Sun.COM 	_NOTE(ARGUNUSED(brand_action))
1688462SApril.Chin@Sun.COM 	vnode_t *nvp;
1698462SApril.Chin@Sun.COM 	int error = 0;
1708462SApril.Chin@Sun.COM 	struct intpdata idata;
1718462SApril.Chin@Sun.COM 	struct pathname intppn;
1728462SApril.Chin@Sun.COM 	struct pathname resolvepn;
1738462SApril.Chin@Sun.COM 	char *opath;
1748462SApril.Chin@Sun.COM 	char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */
1758462SApril.Chin@Sun.COM 	int fd = -1;
1768462SApril.Chin@Sun.COM 	int i;
1778462SApril.Chin@Sun.COM 
1788462SApril.Chin@Sun.COM 	if (level) {		/* Can't recurse */
1798462SApril.Chin@Sun.COM 		error = ENOEXEC;
1808462SApril.Chin@Sun.COM 		goto bad;
1818462SApril.Chin@Sun.COM 	}
1828462SApril.Chin@Sun.COM 
1838462SApril.Chin@Sun.COM 	ASSERT(idatap == (struct intpdata *)NULL);
1848462SApril.Chin@Sun.COM 
1858462SApril.Chin@Sun.COM 	/*
1868462SApril.Chin@Sun.COM 	 * Check whether the executable has the correct magic value.
1878462SApril.Chin@Sun.COM 	 */
1888462SApril.Chin@Sun.COM 	if (error = checkshbinmagic(vp))
1898462SApril.Chin@Sun.COM 		goto fail;
1908462SApril.Chin@Sun.COM 
1918462SApril.Chin@Sun.COM 	pn_alloc(&resolvepn);
1928462SApril.Chin@Sun.COM 
1938462SApril.Chin@Sun.COM 	/*
1948462SApril.Chin@Sun.COM 	 * Travel the list of shells and look for one which is available...
1958462SApril.Chin@Sun.COM 	 */
1968462SApril.Chin@Sun.COM 	for (i = 0; shell_list[i] != NULL; i++) {
1978462SApril.Chin@Sun.COM 		error = pn_get(shell_list[i], UIO_SYSSPACE, &intppn);
1988462SApril.Chin@Sun.COM 		if (error != 0) {
1998462SApril.Chin@Sun.COM 			break;
2008462SApril.Chin@Sun.COM 		}
2018462SApril.Chin@Sun.COM 
2028462SApril.Chin@Sun.COM 		error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp);
2038462SApril.Chin@Sun.COM 		if (!error) {
2048462SApril.Chin@Sun.COM 			/* Found match */
2058462SApril.Chin@Sun.COM 			break;
2068462SApril.Chin@Sun.COM 		}
2078462SApril.Chin@Sun.COM 
2088462SApril.Chin@Sun.COM 		/* No match found ? Then continue with the next item... */
2098462SApril.Chin@Sun.COM 		pn_free(&intppn);
2108462SApril.Chin@Sun.COM 	}
2118462SApril.Chin@Sun.COM 
2128462SApril.Chin@Sun.COM 	if (error) {
2138462SApril.Chin@Sun.COM 		pn_free(&resolvepn);
2148462SApril.Chin@Sun.COM 		goto fail;
2158462SApril.Chin@Sun.COM 	}
2168462SApril.Chin@Sun.COM 
21710205Sroland.mainz@nrubsig.org 	/*
21810205Sroland.mainz@nrubsig.org 	 * Setup interpreter data
21910205Sroland.mainz@nrubsig.org 	 * "--" is passed to mark the end-of-arguments before adding
22010205Sroland.mainz@nrubsig.org 	 * the scripts file name, preventing problems when a
22110205Sroland.mainz@nrubsig.org 	 * a script's name starts with a '-' character.
22210205Sroland.mainz@nrubsig.org 	 */
22310205Sroland.mainz@nrubsig.org 	idata.intp = NULL;
22410205Sroland.mainz@nrubsig.org 	idata.intp_name = shell_list[i];
22510205Sroland.mainz@nrubsig.org 	idata.intp_arg = "--";
22610205Sroland.mainz@nrubsig.org 
2278462SApril.Chin@Sun.COM 	opath = args->pathname;
2288462SApril.Chin@Sun.COM 	args->pathname = resolvepn.pn_path;
2298462SApril.Chin@Sun.COM 	/* don't free resolvepn until we are done with args */
2308462SApril.Chin@Sun.COM 	pn_free(&intppn);
2318462SApril.Chin@Sun.COM 
2328462SApril.Chin@Sun.COM 	/*
2338462SApril.Chin@Sun.COM 	 * When we're executing a set-uid script resulting in uids
2348462SApril.Chin@Sun.COM 	 * mismatching or when we execute with additional privileges,
2358462SApril.Chin@Sun.COM 	 * we close the "replace script between exec and open by shell"
2368462SApril.Chin@Sun.COM 	 * hole by passing the script as /dev/fd parameter.
2378462SApril.Chin@Sun.COM 	 */
2388462SApril.Chin@Sun.COM 	if ((setid & EXECSETID_PRIVS) != 0 ||
2398462SApril.Chin@Sun.COM 	    (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) ==
2408462SApril.Chin@Sun.COM 	    (EXECSETID_UGIDS|EXECSETID_SETID)) {
2418462SApril.Chin@Sun.COM 		(void) strcpy(devfd, "/dev/fd/");
2428462SApril.Chin@Sun.COM 		if (error = execopen(&vp, &fd))
2438462SApril.Chin@Sun.COM 			goto done;
2448462SApril.Chin@Sun.COM 		numtos(fd, &devfd[8]);
2458462SApril.Chin@Sun.COM 		args->fname = devfd;
2468462SApril.Chin@Sun.COM 	}
2478462SApril.Chin@Sun.COM 
2488462SApril.Chin@Sun.COM 	error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
2498462SApril.Chin@Sun.COM 	    EBA_NONE);
250*11736SDonghai.Qiao@Sun.COM 
251*11736SDonghai.Qiao@Sun.COM 	if (!error) {
252*11736SDonghai.Qiao@Sun.COM 		/*
253*11736SDonghai.Qiao@Sun.COM 		 * Close this script as the sh interpreter
254*11736SDonghai.Qiao@Sun.COM 		 * will open and close it later on.
255*11736SDonghai.Qiao@Sun.COM 		 */
256*11736SDonghai.Qiao@Sun.COM 		(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL);
257*11736SDonghai.Qiao@Sun.COM 	}
2588462SApril.Chin@Sun.COM done:
2598462SApril.Chin@Sun.COM 	VN_RELE(nvp);
2608462SApril.Chin@Sun.COM 	args->pathname = opath;
2618462SApril.Chin@Sun.COM 	pn_free(&resolvepn);
2628462SApril.Chin@Sun.COM fail:
2638462SApril.Chin@Sun.COM 	if (error && fd != -1)
2648462SApril.Chin@Sun.COM 		(void) execclose(fd);
2658462SApril.Chin@Sun.COM bad:
2668462SApril.Chin@Sun.COM 	return (error);
2678462SApril.Chin@Sun.COM }
268