+ 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 register_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;
+
+ AUDIT_ARG(pid, uap->pid);
+ AUDIT_ARG(value, 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_new_cred = kauth_cred_setuidgid(my_cred, my_target_cred->cr_uid, my_target_cred->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 register_t *retval)
+{
+ u_int ngrp;
+ gid_t newgroups[NGROUPS] = { 0 };
+ int error;
+ kauth_cred_t my_cred, my_new_cred;
+ struct uthread *uthread = get_bsdthread_info(current_thread());
+
+ DEBUG_CRED_ENTER("setgroups1 (%d/%d): %d 0x%016x %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), gidsetsize, gidset, gmuid);
+
+ ngrp = gidsetsize;
+ if (ngrp > NGROUPS)
+ return (EINVAL);
+
+ if ( ngrp < 1 ) {
+ ngrp = 1;
+ } else {
+ error = copyin(gidset,
+ (caddr_t)newgroups, ngrp * sizeof(gid_t));
+ if (error) {
+ return (error);
+ }