usr/src/uts/common/os/exec.c
changeset 12273 63678502e95e
parent 11974 5dc5c5cda3f3
child 13057 76bc2e54d54b
--- a/usr/src/uts/common/os/exec.c	Wed Apr 28 09:25:44 2010 +0200
+++ b/usr/src/uts/common/os/exec.c	Wed Apr 28 10:01:37 2010 +0200
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -66,6 +65,7 @@
 #include <sys/pool.h>
 #include <sys/sdt.h>
 #include <sys/brand.h>
+#include <sys/klpd.h>
 
 #include <c2/audit.h>
 
@@ -80,8 +80,10 @@
 #define	PRIV_SETUGID		0x04	/* is setuid/setgid/forced privs */
 #define	PRIV_INCREASE		0x08	/* child runs with more privs */
 #define	MAC_FLAGS		0x10	/* need to adjust MAC flags */
+#define	PRIV_FORCED		0x20	/* has forced privileges */
 
-static int execsetid(struct vnode *, struct vattr *, uid_t *, uid_t *);
+static int execsetid(struct vnode *, struct vattr *, uid_t *, uid_t *,
+    priv_set_t *, cred_t *, const char *);
 static int hold_execsw(struct execsw *);
 
 uint_t auxv_hwcap = 0;	/* auxv AT_SUN_HWCAP value; determined on the fly */
@@ -252,6 +254,31 @@
 	pn_free(&pn);
 
 	/*
+	 * If we're running in a profile shell, then call pfexecd.
+	 */
+	if ((CR_FLAGS(p->p_cred) & PRIV_PFEXEC) != 0) {
+		error = pfexec_call(p->p_cred, &resolvepn, &args.pfcred,
+		    &args.scrubenv);
+
+		/* Returning errno in case we're not allowed to execute. */
+		if (error > 0) {
+			if (dir != NULL)
+				VN_RELE(dir);
+			pn_free(&resolvepn);
+			VN_RELE(vp);
+			goto out;
+		}
+
+		/* Don't change the credentials when using old ptrace. */
+		if (args.pfcred != NULL &&
+		    (p->p_proc_flag & P_PR_PTRACE) != 0) {
+			crfree(args.pfcred);
+			args.pfcred = NULL;
+			args.scrubenv = B_FALSE;
+		}
+	}
+
+	/*
 	 * Specific exec handlers, or policies determined via
 	 * /etc/system may override the historical default.
 	 */
@@ -527,6 +554,7 @@
 	cred_t *oldcred, *newcred = NULL;
 	int privflags = 0;
 	int setidfl;
+	priv_set_t fset;
 
 	/*
 	 * If the SNOCD or SUGID flag is set, turn it off and remember the
@@ -562,9 +590,17 @@
 		goto bad;
 
 	if (level == 0 &&
-	    (privflags = execsetid(vp, &vattr, &uid, &gid)) != 0) {
+	    (privflags = execsetid(vp, &vattr, &uid, &gid, &fset,
+	    args->pfcred == NULL ? cred : args->pfcred, args->pathname)) != 0) {
+
+		/* Pfcred is a credential with a ref count of 1 */
 
-		newcred = cred = crdup(cred);
+		if (args->pfcred != NULL) {
+			privflags |= PRIV_INCREASE|PRIV_RESET;
+			newcred = cred = args->pfcred;
+		} else {
+			newcred = cred = crdup(cred);
+		}
 
 		/* If we can, drop the PA bit */
 		if ((privflags & PRIV_RESET) != 0)
@@ -592,17 +628,31 @@
 		 *
 		 *	E' = P' = (I' + F) & A
 		 *
-		 * But if running under ptrace, we cap I with P.
+		 * But if running under ptrace, we cap I and F with P.
 		 */
-		if ((privflags & PRIV_RESET) != 0) {
+		if ((privflags & (PRIV_RESET|PRIV_FORCED)) != 0) {
 			if ((privflags & PRIV_INCREASE) != 0 &&
-			    (pp->p_proc_flag & P_PR_PTRACE) != 0)
+			    (pp->p_proc_flag & P_PR_PTRACE) != 0) {
 				priv_intersect(&CR_OPPRIV(cred),
 				    &CR_IPRIV(cred));
+				priv_intersect(&CR_OPPRIV(cred), &fset);
+			}
 			priv_intersect(&CR_LPRIV(cred), &CR_IPRIV(cred));
 			CR_EPRIV(cred) = CR_PPRIV(cred) = CR_IPRIV(cred);
+			if (privflags & PRIV_FORCED) {
+				priv_set_PA(cred);
+				priv_union(&fset, &CR_EPRIV(cred));
+				priv_union(&fset, &CR_PPRIV(cred));
+			}
 			priv_adjust_PA(cred);
 		}
+	} else if (level == 0 && args->pfcred != NULL) {
+		newcred = cred = args->pfcred;
+		privflags |= PRIV_INCREASE;
+		/* pfcred is not forced to adhere to these settings */
+		priv_intersect(&CR_LPRIV(cred), &CR_IPRIV(cred));
+		CR_EPRIV(cred) = CR_PPRIV(cred) = CR_IPRIV(cred);
+		priv_adjust_PA(cred);
 	}
 
 	/* SunOS 4.x buy-back */
@@ -659,7 +709,7 @@
 	 * credentials of the process.  In privflags, it told us
 	 * whether we gained any privileges or executed a set-uid executable.
 	 */
-	setid = (privflags & (PRIV_SETUGID|PRIV_INCREASE));
+	setid = (privflags & (PRIV_SETUGID|PRIV_INCREASE|PRIV_FORCED));
 
 	/*
 	 * Use /etc/system variable to determine if the stack
@@ -692,7 +742,7 @@
 	}
 	if (setid & PRIV_SETUGID)
 		setidfl |= EXECSETID_SETID;
-	if (setid & PRIV_INCREASE)
+	if (setid & PRIV_FORCED)
 		setidfl |= EXECSETID_PRIVS;
 
 	execvp = pp->p_exec;
@@ -718,6 +768,8 @@
 	}
 
 	if (level == 0) {
+		uid_t oruid;
+
 		if (execvp != NULL) {
 			/*
 			 * Close the previous executable only if we are
@@ -728,6 +780,9 @@
 		}
 
 		mutex_enter(&pp->p_crlock);
+
+		oruid = pp->p_cred->cr_ruid;
+
 		if (newcred != NULL) {
 			/*
 			 * Free the old credentials, and set the new ones.
@@ -778,6 +833,13 @@
 			suidflags = 0;
 
 		mutex_exit(&pp->p_crlock);
+		if (newcred != NULL && oruid != newcred->cr_ruid) {
+			/* Note that the process remains in the same zone. */
+			mutex_enter(&pidlock);
+			upcount_dec(oruid, crgetzoneid(newcred));
+			upcount_inc(newcred->cr_ruid, crgetzoneid(newcred));
+			mutex_exit(&pidlock);
+		}
 		if (suidflags) {
 			mutex_enter(&pp->p_lock);
 			pp->p_flag |= suidflags;
@@ -929,11 +991,11 @@
 }
 
 static int
-execsetid(struct vnode *vp, struct vattr *vattrp, uid_t *uidp, uid_t *gidp)
+execsetid(struct vnode *vp, struct vattr *vattrp, uid_t *uidp, uid_t *gidp,
+    priv_set_t *fset, cred_t *cr, const char *pathname)
 {
 	proc_t *pp = ttoproc(curthread);
 	uid_t uid, gid;
-	cred_t *cr = pp->p_cred;
 	int privflags = 0;
 
 	/*
@@ -948,13 +1010,38 @@
 
 	if ((vp->v_vfsp->vfs_flag & VFS_NOSETUID) == 0) {
 		/*
-		 * Set-uid root execution only allowed if the limit set
-		 * holds all unsafe privileges.
+		 * If it's a set-uid root program we perform the
+		 * forced privilege look-aside. This has three possible
+		 * outcomes:
+		 *	no look aside information -> treat as before
+		 *	look aside in Limit set -> apply forced privs
+		 *	look aside not in Limit set -> ignore set-uid root
+		 *
+		 * Ordinary set-uid root execution only allowed if the limit
+		 * set holds all unsafe privileges.
 		 */
-		if ((vattrp->va_mode & VSUID) && (vattrp->va_uid != 0 ||
-		    priv_issubset(&priv_unsafe, &CR_LPRIV(cr)))) {
-			uid = vattrp->va_uid;
-			privflags |= PRIV_SETUGID;
+		if (vattrp->va_mode & VSUID) {
+			if (vattrp->va_uid == 0) {
+				int res = get_forced_privs(cr, pathname, fset);
+
+				switch (res) {
+				case -1:
+					if (priv_issubset(&priv_unsafe,
+					    &CR_LPRIV(cr))) {
+						uid = vattrp->va_uid;
+						privflags |= PRIV_SETUGID;
+					}
+					break;
+				case 0:
+					privflags |= PRIV_FORCED|PRIV_INCREASE;
+					break;
+				default:
+					break;
+				}
+			} else {
+				uid = vattrp->va_uid;
+				privflags |= PRIV_SETUGID;
+			}
 		}
 		if (vattrp->va_mode & VSGID) {
 			gid = vattrp->va_gid;
@@ -980,20 +1067,14 @@
 	    !priv_isequalset(&CR_PPRIV(cr), &CR_IPRIV(cr)))
 		privflags |= PRIV_RESET;
 
+	/* Child has more privileges than parent */
+	if (!priv_issubset(&CR_IPRIV(cr), &CR_PPRIV(cr)))
+		privflags |= PRIV_INCREASE;
+
 	/* If MAC-aware flag(s) are on, need to update cred to remove. */
 	if ((CR_FLAGS(cr) & NET_MAC_AWARE) ||
 	    (CR_FLAGS(cr) & NET_MAC_AWARE_INHERIT))
 		privflags |= MAC_FLAGS;
-
-	/*
-	 * When we introduce the "forced" set then we will need
-	 * to set PRIV_INCREASE here if I not a subset of P.
-	 * If the "allowed" set is introduced we will need to do
-	 * a similar thing; however, it seems more reasonable to
-	 * have the allowed set reduce "L": script language interpreters
-	 * would typically have an allowed set of "all".
-	 */
-
 	/*
 	 * Set setuid/setgid protections if no ptrace() compatibility.
 	 * For privileged processes, honor setuid/setgid even in
@@ -1498,12 +1579,18 @@
 	 */
 	if (envp != NULL) {
 		for (;;) {
+			char *tmp = args->stk_strp;
 			if (stk_getptr(args, envp, &sp))
 				return (EFAULT);
 			if (sp == NULL)
 				break;
 			if ((error = stk_add(args, sp, UIO_USERSPACE)) != 0)
 				return (error);
+			if (args->scrubenv && strncmp(tmp, "LD_", 3) == 0) {
+				/* Undo the copied string */
+				args->stk_strp = tmp;
+				*(args->stk_offp++) = NULL;
+			}
 			envp += ptrsize;
 		}
 	}
@@ -1840,7 +1927,7 @@
 
 	if (AU_AUDITING())
 		audit_exec(args->stk_base, args->stk_base + args->arglen,
-		    args->na - args->ne, args->ne);
+		    args->na - args->ne, args->ne, args->pfcred);
 
 	/*
 	 * Ensure that we don't change resource associations while we