--- 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