+ /* 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;
+
+ 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);
+
+ if (((ruid != KAUTH_UID_NONE && /* allow no change of ruid */
+ ruid != my_cred->cr_ruid && /* allow ruid = ruid */
+ ruid != my_cred->cr_uid && /* allow ruid = euid */
+ ruid != my_cred->cr_svuid) || /* allow ruid = svuid */
+ (euid != KAUTH_UID_NONE && /* allow no change of euid */
+ euid != my_cred->cr_uid && /* allow euid = euid */
+ euid != my_cred->cr_ruid && /* allow euid = ruid */
+ euid != my_cred->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_cred->cr_uid;
+ new_ruid = my_cred->cr_ruid;