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