+ if (uap->pid != 0 && uap->pid != curp->p_pid) {
+ if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) {
+ if (targp != PROC_NULL)
+ refheld = 1;
+ error = ESRCH;
+ goto out;
+ }
+ refheld = 1;
+ targp_sessp = proc_session(targp);
+ if (targp_sessp != curp_sessp) {
+ error = EPERM;
+ goto out;
+ }
+ if (targp->p_flag & P_EXEC) {
+ error = EACCES;
+ goto out;
+ }
+ } else {
+ targp = curp;
+ targp_sessp = proc_session(targp);
+ }
+
+ if (SESS_LEADER(targp, targp_sessp)) {
+ error = EPERM;
+ goto out;
+ }
+ if (targp_sessp != SESSION_NULL) {
+ session_rele(targp_sessp);
+ targp_sessp = SESSION_NULL;
+ }
+
+ if (uap->pgid < 0) {
+ error = EINVAL;
+ goto out;
+ }
+ if (uap->pgid == 0)
+ uap->pgid = targp->p_pid;
+ else if (uap->pgid != targp->p_pid) {
+ if ((pg = pgfind(uap->pgid)) == 0){
+ error = EPERM;
+ goto out;
+ }
+ samesess = (pg->pg_session != curp_sessp);
+ pg_rele(pg);
+ if (samesess != 0) {
+ error = EPERM;
+ goto out;
+ }
+ }
+ error = enterpgrp(targp, uap->pgid, 0);
+out:
+ if (targp_sessp != SESSION_NULL)
+ session_rele(targp_sessp);
+ if (curp_sessp != SESSION_NULL)
+ session_rele(curp_sessp);
+ if (refheld != 0)
+ proc_rele(targp);
+ return(error);
+}
+
+
+/*
+ * issetugid
+ *
+ * Description: Is current process tainted by uid or gid changes system call
+ *
+ * Parameters: (void)
+ *
+ * Returns: 0 Not tainted
+ * 1 Tainted
+ *
+ * Notes: A process is considered tainted if it was created as a retult
+ * of an execve call from an imnage that had either the SUID or
+ * SGID bit set on the executable, or if it has changed any of its
+ * real, effective, or saved user or group IDs since beginning
+ * execution.
+ */
+int
+issetugid(proc_t p, __unused struct issetugid_args *uap, int32_t *retval)
+{
+ /*
+ * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time,
+ * we use P_SUGID because we consider changing the owners as
+ * "tainting" as well.
+ * This is significant for procs that start as root and "become"
+ * a user without an exec - programs cannot know *everything*
+ * that libc *might* have put in their data segment.
+ */
+
+ *retval = (p->p_flag & P_SUGID) ? 1 : 0;
+ return (0);
+}
+
+
+/*
+ * setuid
+ *
+ * Description: Set user ID system call
+ *
+ * Parameters: uap->uid uid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permission denied
+ *
+ * Notes: If called by a privileged process, this function will set the
+ * real, effective, and saved uid to the requested value.
+ *
+ * If called from an unprivileged process, but uid is equal to the
+ * real or saved uid, then the effective uid will be set to the
+ * requested value, but the real and saved uid will not change.
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ */
+int
+setuid(proc_t p, struct setuid_args *uap, __unused int32_t *retval)
+{
+ uid_t uid;
+ uid_t svuid = KAUTH_UID_NONE;
+ uid_t ruid = KAUTH_UID_NONE;
+ uid_t gmuid = KAUTH_UID_NONE;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+
+ uid = uap->uid;
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ DEBUG_CRED_ENTER("setuid (%d/%d): %p %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), my_cred, uap->uid);
+ AUDIT_ARG(uid, uid);
+
+ if (uid != my_pcred->cr_ruid && /* allow setuid(getuid()) */
+ uid != my_pcred->cr_svuid && /* allow setuid(saved uid) */
+ (error = suser(my_cred, &p->p_acflag))) {
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+ /*
+ * Everything's okay, do it.
+ */
+
+ /*
+ * If we are priviledged, then set the saved and real UID too;
+ * otherwise, just set the effective UID
+ */
+ if (suser(my_cred, &p->p_acflag) == 0) {
+ svuid = uid;
+ ruid = uid;
+ /*
+ * Transfer proc count to new user.
+ * chgproccnt uses list lock for protection
+ */
+ (void)chgproccnt(uid, 1);
+ (void)chgproccnt(my_pcred->cr_ruid, -1);
+ }
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+ /*
+ * Only set the gmuid if the current cred has not opt'ed out;
+ * this normally only happens when calling setgroups() instead
+ * of initgroups() to set an explicit group list, or one of the
+ * other group manipulation functions is invoked and results in
+ * a dislocation (i.e. the credential group membership changes
+ * to something other than the default list for the user, as
+ * in entering a group or leaving an exclusion group).
+ */
+ if (!(my_pcred->cr_flags & CRF_NOMEMBERD))
+ gmuid = uid;
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_setresuid(my_cred, ruid, uid, svuid, gmuid);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("setuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we should
+ * restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ /* try again */
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+
+/*
+ * seteuid
+ *
+ * Description: Set effective user ID system call
+ *
+ * Parameters: uap->euid effective uid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permission denied
+ *
+ * Notes: If called by a privileged process, or called from an
+ * unprivileged process but euid is equal to the real or saved
+ * uid, then the effective uid will be set to the requested
+ * value, but the real and saved uid will not change.
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ */
+int
+seteuid(proc_t p, struct seteuid_args *uap, __unused int32_t *retval)
+{
+ uid_t euid;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+ DEBUG_CRED_ENTER("seteuid: %d\n", uap->euid);
+
+ euid = uap->euid;
+ AUDIT_ARG(euid, euid);
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ if (euid != my_pcred->cr_ruid && euid != my_pcred->cr_svuid &&
+ (error = suser(my_cred, &p->p_acflag))) {
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+
+ /*
+ * Everything's okay, do it. Copy credentials so other references do
+ * not see our changes. get current credential and take a reference
+ * while we muck with it
+ */
+ for (;;) {
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_setresuid(my_cred, KAUTH_UID_NONE, euid, KAUTH_UID_NONE, my_pcred->cr_gmuid);
+
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("seteuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we
+ * should restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ /* try again */
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+
+/*
+ * setreuid
+ *
+ * Description: Set real and effective user ID system call
+ *
+ * Parameters: uap->ruid real uid to set
+ * uap->euid effective uid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permission denied
+ *
+ * Notes: A value of -1 is a special case indicating that the uid for
+ * which that value is specified not be changed. If both values
+ * are specified as -1, no action is taken.
+ *
+ * If called by a privileged process, the real and effective uid
+ * will be set to the new value(s) specified.
+ *
+ * If called from an unprivileged process, the real uid may be
+ * set to the current value of the real uid, or to the current
+ * value of the saved uid. The effective uid may be set to the
+ * current value of any of the effective, real, or saved uid.
+ *
+ * If the newly requested real uid or effective uid does not
+ * match the saved uid, then set the saved uid to the new
+ * effective uid (potentially unrecoverably dropping saved
+ * privilege).
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ */
+int
+setreuid(proc_t p, struct setreuid_args *uap, __unused int32_t *retval)
+{
+ uid_t ruid, euid;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+ DEBUG_CRED_ENTER("setreuid %d %d\n", uap->ruid, uap->euid);
+
+ ruid = uap->ruid;
+ euid = uap->euid;
+ if (ruid == (uid_t)-1)
+ ruid = KAUTH_UID_NONE;
+ if (euid == (uid_t)-1)
+ euid = KAUTH_UID_NONE;
+ AUDIT_ARG(euid, euid);
+ AUDIT_ARG(ruid, ruid);
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ if (((ruid != KAUTH_UID_NONE && /* allow no change of ruid */
+ ruid != my_pcred->cr_ruid && /* allow ruid = ruid */
+ ruid != my_pcred->cr_uid && /* allow ruid = euid */
+ ruid != my_pcred->cr_svuid) || /* allow ruid = svuid */
+ (euid != KAUTH_UID_NONE && /* allow no change of euid */
+ euid != my_pcred->cr_uid && /* allow euid = euid */
+ euid != my_pcred->cr_ruid && /* allow euid = ruid */
+ euid != my_pcred->cr_svuid)) && /* allow euid = svui */
+ (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+
+ /*
+ * Everything's okay, do it. Copy credentials so other references do
+ * not see our changes. get current credential and take a reference
+ * while we muck with it
+ */
+ for (;;) {
+ uid_t new_euid;
+ uid_t new_ruid;
+ uid_t svuid = KAUTH_UID_NONE;
+
+ new_euid = my_pcred->cr_uid;
+ new_ruid = my_pcred->cr_ruid;
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ if (euid == KAUTH_UID_NONE && my_pcred->cr_uid != euid) {
+ /* changing the effective UID */
+ new_euid = euid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+ if (ruid != KAUTH_UID_NONE && my_pcred->cr_ruid != ruid) {
+ /* changing the real UID; must do user accounting */
+ /* chgproccnt uses list lock for protection */
+ (void)chgproccnt(ruid, 1);
+ (void)chgproccnt(my_pcred->cr_ruid, -1);
+ new_ruid = ruid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+ /*
+ * If the newly requested real uid or effective uid does
+ * not match the saved uid, then set the saved uid to the
+ * new effective uid. We are protected from escalation
+ * by the prechecking.
+ */
+ if (my_pcred->cr_svuid != uap->ruid &&
+ my_pcred->cr_svuid != uap->euid) {
+ svuid = new_euid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+
+ my_new_cred = kauth_cred_setresuid(my_cred, ruid, euid, svuid, my_pcred->cr_gmuid);
+
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("setreuid CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we should
+ * restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ /* try again */
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+ OSBitOrAtomic(P_SUGID, &p->p_flag); /* XXX redundant? */
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+
+/*
+ * setgid
+ *
+ * Description: Set group ID system call
+ *
+ * Parameters: uap->gid gid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permission denied
+ *
+ * Notes: If called by a privileged process, this function will set the
+ * real, effective, and saved gid to the requested value.
+ *
+ * If called from an unprivileged process, but gid is equal to the
+ * real or saved gid, then the effective gid will be set to the
+ * requested value, but the real and saved gid will not change.
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ *
+ * As an implementation detail, the effective gid is stored as
+ * the first element of the supplementary group list, and
+ * therefore the effective group list may be reordered to keep
+ * the supplementary group list unchanged.
+ */
+int
+setgid(proc_t p, struct setgid_args *uap, __unused int32_t *retval)
+{
+ gid_t gid;
+ gid_t rgid = KAUTH_GID_NONE;
+ gid_t svgid = KAUTH_GID_NONE;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+ DEBUG_CRED_ENTER("setgid(%d/%d): %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), uap->gid);
+
+ gid = uap->gid;
+ AUDIT_ARG(gid, gid);
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ if (gid != my_pcred->cr_rgid && /* allow setgid(getgid()) */
+ gid != my_pcred->cr_svgid && /* allow setgid(saved gid) */
+ (error = suser(my_cred, &p->p_acflag))) {
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+
+ /*
+ * If we are priviledged, then set the saved and real GID too;
+ * otherwise, just set the effective GID
+ */
+ if (suser(my_cred, &p->p_acflag) == 0) {
+ svgid = gid;
+ rgid = gid;
+ }
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_setresgid(my_cred, rgid, gid, svgid);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("setgid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we
+ * should restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ /* try again */
+ my_cred = kauth_cred_proc_ref(p);
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+
+/*
+ * setegid
+ *
+ * Description: Set effective group ID system call
+ *
+ * Parameters: uap->egid effective gid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM
+ *
+ * Notes: If called by a privileged process, or called from an
+ * unprivileged process but egid is equal to the real or saved
+ * gid, then the effective gid will be set to the requested
+ * value, but the real and saved gid will not change.
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ *
+ * As an implementation detail, the effective gid is stored as
+ * the first element of the supplementary group list, and
+ * therefore the effective group list may be reordered to keep
+ * the supplementary group list unchanged.
+ */
+int
+setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval)
+{
+ gid_t egid;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+ DEBUG_CRED_ENTER("setegid %d\n", uap->egid);
+
+ egid = uap->egid;
+ AUDIT_ARG(egid, egid);
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ if (egid != my_pcred->cr_rgid &&
+ egid != my_pcred->cr_svgid &&
+ (error = suser(my_cred, &p->p_acflag))) {
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ my_new_cred = kauth_cred_setresgid(my_cred, KAUTH_GID_NONE, egid, KAUTH_GID_NONE);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("setegid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+ proc_lock(p);
+ /*
+ * We need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we
+ * should restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ /* try again */
+ my_cred = kauth_cred_proc_ref(p);
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ proc_unlock(p);
+ }
+ break;
+ }
+
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+/*
+ * setregid
+ *
+ * Description: Set real and effective group ID system call
+ *
+ * Parameters: uap->rgid real gid to set
+ * uap->egid effective gid to set
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permission denied
+ *
+ * Notes: A value of -1 is a special case indicating that the gid for
+ * which that value is specified not be changed. If both values
+ * are specified as -1, no action is taken.
+ *
+ * If called by a privileged process, the real and effective gid
+ * will be set to the new value(s) specified.
+ *
+ * If called from an unprivileged process, the real gid may be
+ * set to the current value of the real gid, or to the current
+ * value of the saved gid. The effective gid may be set to the
+ * current value of any of the effective, real, or saved gid.
+ *
+ * If the new real and effective gid will not be equal, or the
+ * new real or effective gid is not the same as the saved gid,
+ * then the saved gid will be updated to reflect the new
+ * effective gid (potentially unrecoverably dropping saved
+ * privilege).
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ *
+ * As an implementation detail, the effective gid is stored as
+ * the first element of the supplementary group list, and
+ * therefore the effective group list may be reordered to keep
+ * the supplementary group list unchanged.
+ */
+int
+setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval)
+{
+ gid_t rgid, egid;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ posix_cred_t my_pcred;
+
+ DEBUG_CRED_ENTER("setregid %d %d\n", uap->rgid, uap->egid);
+
+ rgid = uap->rgid;
+ egid = uap->egid;
+
+ if (rgid == (uid_t)-1)
+ rgid = KAUTH_GID_NONE;
+ if (egid == (uid_t)-1)
+ egid = KAUTH_GID_NONE;
+ AUDIT_ARG(egid, egid);
+ AUDIT_ARG(rgid, rgid);
+
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+
+ if (((rgid != KAUTH_UID_NONE && /* allow no change of rgid */
+ rgid != my_pcred->cr_rgid && /* allow rgid = rgid */
+ rgid != my_pcred->cr_gid && /* allow rgid = egid */
+ rgid != my_pcred->cr_svgid) || /* allow rgid = svgid */
+ (egid != KAUTH_UID_NONE && /* allow no change of egid */
+ egid != my_pcred->cr_groups[0] && /* allow no change of egid */
+ egid != my_pcred->cr_gid && /* allow egid = egid */
+ egid != my_pcred->cr_rgid && /* allow egid = rgid */
+ egid != my_pcred->cr_svgid)) && /* allow egid = svgid */
+ (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */
+ kauth_cred_unref(&my_cred);
+ return (error);
+ }
+
+ /* get current credential and take a reference while we muck with it */
+ for (;;) {
+ uid_t new_egid = my_pcred->cr_gid;
+ uid_t new_rgid = my_pcred->cr_rgid;
+ uid_t svgid = KAUTH_UID_NONE;
+
+
+ /*
+ * Set the credential with new info. If there is no change,
+ * we get back the same credential we passed in; if there is
+ * a change, we drop the reference on the credential we
+ * passed in. The subsequent compare is safe, because it is
+ * a pointer compare rather than a contents compare.
+ */
+ if (egid == KAUTH_UID_NONE && my_pcred->cr_gid != egid) {
+ /* changing the effective GID */
+ new_egid = egid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+ if (rgid != KAUTH_UID_NONE && my_pcred->cr_rgid != rgid) {
+ /* changing the real GID */
+ new_rgid = rgid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+ /*
+ * If the newly requested real gid or effective gid does
+ * not match the saved gid, then set the saved gid to the
+ * new effective gid. We are protected from escalation
+ * by the prechecking.
+ */
+ if (my_pcred->cr_svgid != uap->rgid &&
+ my_pcred->cr_svgid != uap->egid) {
+ svgid = new_egid;
+ OSBitOrAtomic(P_SUGID, &p->p_flag);
+ }
+
+ my_new_cred = kauth_cred_setresgid(my_cred, rgid, egid, svgid);
+ if (my_cred != my_new_cred) {
+
+ DEBUG_CRED_CHANGE("setregid(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+ proc_lock(p);
+ /* need to protect for a race where another thread
+ * also changed the credential after we took our
+ * reference. If p_ucred has changed then we
+ * should restart this again with the new cred.
+ */
+ if (p->p_ucred != my_cred) {
+ proc_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ /* try again */
+ my_cred = kauth_cred_proc_ref(p);
+ continue;
+ }
+ p->p_ucred = my_new_cred;
+ /* update cred on proc */
+ PROC_UPDATE_CREDS_ONPROC(p);
+ OSBitOrAtomic(P_SUGID, &p->p_flag); /* XXX redundant? */
+ proc_unlock(p);
+ }
+ break;
+ }
+ /* Drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return (0);
+}
+
+
+/*
+ * Set the per-thread override identity. The first parameter can be the
+ * current real UID, KAUTH_UID_NONE, or, if the caller is priviledged, it
+ * can be any UID. If it is KAUTH_UID_NONE, then as a special case, this
+ * means "revert to the per process credential"; otherwise, if permitted,
+ * it changes the effective, real, and saved UIDs and GIDs for the current
+ * thread to the requested UID and single GID, and clears all other GIDs.
+ */
+int
+settid(proc_t p, struct settid_args *uap, __unused int32_t *retval)
+{
+ kauth_cred_t uc;
+ struct uthread *uthread = get_bsdthread_info(current_thread());
+ uid_t uid;
+ gid_t gid;
+
+ uid = uap->uid;
+ gid = uap->gid;
+ AUDIT_ARG(uid, uid);
+ AUDIT_ARG(gid, gid);
+
+ if (proc_suser(p) != 0)
+ return (EPERM);
+
+ if (uid == KAUTH_UID_NONE) {
+
+ /* must already be assuming another identity in order to revert back */
+ if ((uthread->uu_flag & UT_SETUID) == 0)
+ return (EPERM);
+
+ /* revert to delayed binding of process credential */
+ uc = kauth_cred_proc_ref(p);
+ kauth_cred_unref(&uthread->uu_ucred);
+ uthread->uu_ucred = uc;
+ uthread->uu_flag &= ~UT_SETUID;
+ } else {
+ kauth_cred_t my_cred, my_new_cred;
+
+ /* cannot already be assuming another identity */
+ if ((uthread->uu_flag & UT_SETUID) != 0) {
+ return (EPERM);
+ }
+
+ /*
+ * Get a new credential instance from the old if this one
+ * changes; otherwise kauth_cred_setuidgid() returns the
+ * same credential. We take an extra reference on the
+ * current credential while we muck with it, so we can do
+ * the post-compare for changes by pointer.
+ */
+ kauth_cred_ref(uthread->uu_ucred);
+ my_cred = uthread->uu_ucred;
+ my_new_cred = kauth_cred_setuidgid(my_cred, uid, gid);
+ if (my_cred != my_new_cred)
+ uthread->uu_ucred = my_new_cred;
+ uthread->uu_flag |= UT_SETUID;
+
+ /* Drop old uthread reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+ }
+ /*
+ * XXX should potentially set per thread security token (there is
+ * XXX none).
+ * XXX it is unclear whether P_SUGID should be st at this point;
+ * XXX in theory, it is being deprecated.
+ */
+ return (0);
+}
+
+
+/*
+ * Set the per-thread override identity. Use this system call for a thread to
+ * assume the identity of another process or to revert back to normal identity
+ * of the current process.
+ *
+ * When the "assume" argument is non zero the current thread will assume the
+ * identity of the process represented by the pid argument.
+ *
+ * When the assume argument is zero we revert back to our normal identity.
+ */
+int
+settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *retval)
+{
+ proc_t target_proc;
+ struct uthread *uthread = get_bsdthread_info(current_thread());
+ kauth_cred_t my_cred, my_target_cred, my_new_cred;
+ posix_cred_t my_target_pcred;
+
+ AUDIT_ARG(pid, uap->pid);
+ AUDIT_ARG(value32, uap->assume);
+
+ if (proc_suser(p) != 0) {
+ return (EPERM);
+ }
+
+ /*
+ * XXX should potentially set per thread security token (there is
+ * XXX none).
+ * XXX it is unclear whether P_SUGID should be st at this point;
+ * XXX in theory, it is being deprecated.
+ */
+
+ /*
+ * assume argument tells us to assume the identity of the process with the
+ * id passed in the pid argument.
+ */
+ if (uap->assume != 0) {
+ /* can't do this if we have already assumed an identity */
+ if ((uthread->uu_flag & UT_SETUID) != 0)
+ return (EPERM);
+
+ target_proc = proc_find(uap->pid);
+ /* can't assume the identity of the kernel process */
+ if (target_proc == NULL || target_proc == kernproc) {
+ if (target_proc!= NULL)
+ proc_rele(target_proc);
+ return (ESRCH);
+ }
+
+ /*
+ * Take a reference on the credential used in our target
+ * process then use it as the identity for our current
+ * thread. We take an extra reference on the current
+ * credential while we muck with it, so we can do the
+ * post-compare for changes by pointer.
+ *
+ * The post-compare is needed for the case that our process
+ * credential has been changed to be identical to our thread
+ * credential following our assumption of a per-thread one,
+ * since the credential cache will maintain a unique instance.
+ */
+ kauth_cred_ref(uthread->uu_ucred);
+ my_cred = uthread->uu_ucred;
+ my_target_cred = kauth_cred_proc_ref(target_proc);
+ my_target_pcred = posix_cred_get(my_target_cred);
+ my_new_cred = kauth_cred_setuidgid(my_cred, my_target_pcred->cr_uid, my_target_pcred->cr_gid);
+ if (my_cred != my_new_cred)
+ uthread->uu_ucred = my_new_cred;
+
+ uthread->uu_flag |= UT_SETUID;
+
+ /* Drop old uthread reference or our extra reference */
+ proc_rele(target_proc);
+ kauth_cred_unref(&my_cred);
+ kauth_cred_unref(&my_target_cred);
+
+ return (0);
+ }
+
+ /*
+ * Otherwise, we are reverting back to normal mode of operation where
+ * delayed binding of the process credential sets the credential in
+ * the thread (uu_ucred)
+ */
+ if ((uthread->uu_flag & UT_SETUID) == 0)
+ return (EPERM);
+
+ /* revert to delayed binding of process credential */
+ my_new_cred = kauth_cred_proc_ref(p);
+ kauth_cred_unref(&uthread->uu_ucred);
+ uthread->uu_ucred = my_new_cred;
+ uthread->uu_flag &= ~UT_SETUID;
+
+ return (0);
+}
+
+
+/*
+ * setgroups1
+ *
+ * Description: Internal implementation for both the setgroups and initgroups
+ * system calls
+ *
+ * Parameters: gidsetsize Number of groups in set
+ * gidset Pointer to group list
+ * gmuid Base gid (initgroups only!)
+ *
+ * Returns: 0 Success
+ * suser:EPERM Permision denied
+ * EINVAL Invalid gidsetsize value
+ * copyin:EFAULT Bad gidset or gidsetsize is
+ * too large
+ *
+ * Notes: When called from a thread running under an assumed per-thread
+ * identity, this function will operate against the per-thread
+ * credential, rather than against the process credential. In
+ * this specific case, the process credential is verified to
+ * still be privileged at the time of the call, rather than the
+ * per-thread credential for this operation to be permitted.
+ *
+ * This effectively means that setgroups/initigroups calls in
+ * a thread running a per-thread credential should occur *after*
+ * the settid call that created it, not before (unlike setuid,
+ * which must be called after, since it will result in privilege
+ * being dropped).
+ *
+ * When called normally (i.e. no per-thread assumed identity),
+ * the per process credential is updated per POSIX.
+ *
+ * If the credential is changed as a result of this call, then we
+ * flag the process as having set privilege since the last exec.
+ */
+static int
+setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused int32_t *retval)