+
+ /* 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_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;
+ OSBitOrAtomic(P_SUGID, (UInt32 *)&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 register_t *retval)
+{
+ gid_t rgid, egid;
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+
+ 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(gid, egid, rgid, 0, 0);
+
+ my_cred = kauth_cred_proc_ref(p);
+
+ if (((rgid != KAUTH_UID_NONE && /* allow no change of rgid */
+ rgid != my_cred->cr_rgid && /* allow rgid = rgid */
+ rgid != my_cred->cr_gid && /* allow rgid = egid */
+ rgid != my_cred->cr_svgid) || /* allow rgid = svgid */
+ (egid != KAUTH_UID_NONE && /* allow no change of egid */
+ egid != my_cred->cr_groups[0] && /* allow no change of egid */
+ egid != my_cred->cr_gid && /* allow egid = egid */
+ egid != my_cred->cr_rgid && /* allow egid = rgid */
+ egid != my_cred->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_cred->cr_gid;
+ uid_t new_rgid = my_cred->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_cred->cr_groups[0] != egid) {
+ /* changing the effective GID */
+ new_egid = egid;
+ OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
+ }
+ if (rgid != KAUTH_UID_NONE && my_cred->cr_rgid != rgid) {
+ /* changing the real GID */
+ new_rgid = rgid;
+ OSBitOrAtomic(P_SUGID, (UInt32 *)&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_cred->cr_svgid != uap->rgid &&
+ my_cred->cr_svgid != uap->egid) {
+ svgid = new_egid;
+ OSBitOrAtomic(P_SUGID, (UInt32 *)&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_cred->cr_flags, my_new_cred, 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;
+ OSBitOrAtomic(P_SUGID, (UInt32 *)&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 register_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, gid, gid, 0);
+
+ 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);