+ *retval = proc_issetugid(p);
+ 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;
+
+ /* get current credential and take a reference while we muck with it */
+ 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);
+
+ for (;;) {
+ 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;
+ }
+
+ /*
+ * If we are privileged, 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;
+ } else {
+ svuid = KAUTH_UID_NONE;
+ ruid = KAUTH_UID_NONE;
+ }
+ /*
+ * 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);
+
+ /*
+ * If we're changing the ruid from A to B, we might race with another thread that's setting ruid from B to A.
+ * The current locking mechanisms don't allow us to make the entire credential switch operation atomic,
+ * thus we may be able to change the process credentials from ruid A to B, but get preempted before incrementing the proc
+ * count of B. If a second thread sees the new process credentials and switches back to ruid A, that other thread
+ * may be able to decrement the proc count of B before we can increment it. This results in a panic.
+ * Incrementing the proc count of the target ruid, B, before setting the process credentials prevents this race.
+ */
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
+ (void)chgproccnt(ruid, 1);
+ }
+
+ proc_ucred_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.
+ *
+ * Note: the kauth_cred_setresuid has consumed a reference to my_cred, it p_ucred != my_cred, then my_cred must not be dereferenced!
+ */
+ if (p->p_ucred != my_cred) {
+ proc_ucred_unlock(p);
+ /*
+ * We didn't successfully switch to the new ruid, so decrement
+ * the procs/uid count that we incremented above.
+ */
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
+ (void)chgproccnt(ruid, -1);
+ }
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+ /* 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_ucred_unlock(p);
+ /*
+ * If we've updated the ruid, decrement the count of procs running
+ * under the previous ruid
+ */
+ if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
+ (void)chgproccnt(my_pcred->cr_ruid, -1);
+ }
+ }
+ 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);
+
+ for (;;) {
+ 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;
+ }
+
+ /*
+ * 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_ucred_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_ucred_unlock(p);
+ kauth_cred_unref(&my_new_cred);
+ my_cred = kauth_cred_proc_ref(p);
+ my_pcred = posix_cred_get(my_cred);
+ /* 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_ucred_unlock(p);
+ }
+ break;
+ }
+ /* drop old proc reference or our extra reference */
+ kauth_cred_unref(&my_cred);
+
+ set_security_token(p);
+ return 0;