X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e5568f75972dfc723778653c11cb6b4dc825716a..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/bsd/kern/kern_prot.c

diff --git a/bsd/kern/kern_prot.c b/bsd/kern/kern_prot.c
index db25475d6..d689b70d5 100644
--- a/bsd/kern/kern_prot.c
+++ b/bsd/kern/kern_prot.c
@@ -1,26 +1,34 @@
 /*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
-/*
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ *
+ *
+ * Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved
+ *
+ *
  * Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
  *	The Regents of the University of California.  All rights reserved.
  * (c) UNIX System Laboratories, Inc.
@@ -58,6 +66,19 @@
  * SUCH DAMAGE.
  *
  *	@(#)kern_prot.c	8.9 (Berkeley) 2/14/95
+ *
+ *
+ * NOTICE: This file was modified by McAfee Research in 2004 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ *
+ *
+ * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ *
  */
 
 /*
@@ -68,279 +89,614 @@
 #include <sys/acct.h>
 #include <sys/systm.h>
 #include <sys/ucred.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
+#include <sys/user.h>
+#include <sys/kauth.h>
 #include <sys/timeb.h>
 #include <sys/times.h>
 #include <sys/malloc.h>
+#include <sys/persona.h>
 
-#include <bsm/audit_kernel.h>
+#include <security/audit/audit.h>
 
-#include <sys/mount.h>
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#endif
+
+#include <sys/mount_internal.h>
+#include <sys/sysproto.h>
 #include <mach/message.h>
 #include <mach/host_security.h>
 
 #include <kern/host.h>
+#include <kern/task.h>          /* for current_task() */
+#include <kern/assert.h>
+
+
+/*
+ * Credential debugging; we can track entry into a function that might
+ * change a credential, and we can track actual credential changes that
+ * result.
+ *
+ * Note:	Does *NOT* currently include per-thread credential changes
+ *
+ *		We don't use kauth_cred_print() in current debugging, but it
+ *		can be used if needed when debugging is active.
+ */
+#if DEBUG_CRED
+#define DEBUG_CRED_ENTER                printf
+#define DEBUG_CRED_CHANGE               printf
+extern void kauth_cred_print(kauth_cred_t cred);
+#else   /* !DEBUG_CRED */
+#define DEBUG_CRED_ENTER(fmt, ...)      do {} while (0)
+#define DEBUG_CRED_CHANGE(fmt, ...)     do {} while (0)
+#endif  /* !DEBUG_CRED */
+
+#if DEVELOPMENT || DEBUG
+extern void task_importance_update_owner_info(task_t);
+#endif
+
 
 /*
- * setprivexec:  (dis)allow this process to hold
- * task, thread, or execption ports of processes about to exec.
+ * setprivexec
+ *
+ * Description:	(dis)allow this process to hold task, thread, or execption
+ *		ports of processes about to exec.
+ *
+ * Parameters:	uap->flag			New value for flag
+ *
+ * Returns:	int				Previous value of flag
+ *
+ * XXX:		Belongs in kern_proc.c
  */
-struct setprivexec_args {
-	int flag;
-}; 
 int
-setprivexec(p, uap, retval)
-	struct proc *p;
-	register struct setprivexec_args *uap;
-	register_t *retval;
+setprivexec(proc_t p, struct setprivexec_args *uap, int32_t *retval)
 {
-	AUDIT_ARG(value, uap->flag);
+	AUDIT_ARG(value32, uap->flag);
 	*retval = p->p_debugger;
 	p->p_debugger = (uap->flag != 0);
-	return(0);
+	return 0;
 }
 
-/* ARGSUSED */
-getpid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
-{
 
+/*
+ * getpid
+ *
+ * Description:	get the process ID
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	pid_t				Current process ID
+ *
+ * XXX:		Belongs in kern_proc.c
+ */
+int
+getpid(proc_t p, __unused struct getpid_args *uap, int32_t *retval)
+{
 	*retval = p->p_pid;
-#if COMPAT_43
-	retval[1] = p->p_pptr->p_pid;
-#endif
-	return (0);
+	return 0;
 }
 
-/* ARGSUSED */
-getppid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
-{
 
-	*retval = p->p_pptr->p_pid;
-	return (0);
+/*
+ * getppid
+ *
+ * Description: get the parent process ID
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	pid_t				Parent process ID
+ *
+ * XXX:		Belongs in kern_proc.c
+ */
+int
+getppid(proc_t p, __unused struct getppid_args *uap, int32_t *retval)
+{
+	*retval = p->p_ppid;
+	return 0;
 }
 
-/* Get process group ID; note that POSIX getpgrp takes no parameter */
-getpgrp(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
-{
 
-	*retval = p->p_pgrp->pg_id;
-	return (0);
+/*
+ * getpgrp
+ *
+ * Description:	get the process group ID of the calling process
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	pid_t				Process group ID
+ *
+ * XXX:		Belongs in kern_proc.c
+ */
+int
+getpgrp(proc_t p, __unused struct getpgrp_args *uap, int32_t *retval)
+{
+	*retval = p->p_pgrpid;
+	return 0;
 }
 
-/* Get an arbitary pid's process group id */
-struct getpgid_args {
-	pid_t   pid;
-};
 
+/*
+ * getpgid
+ *
+ * Description: Get an arbitary pid's process group id
+ *
+ * Parameters:	uap->pid			The target pid
+ *
+ * Returns:	0				Success
+ *		ESRCH				No such process
+ *
+ * Notes:	We are permitted to return EPERM in the case that the target
+ *		process is not in the same session as the calling process,
+ *		which could be a security consideration
+ *
+ * XXX:		Belongs in kern_proc.c
+ */
 int
-getpgid(p, uap, retval)
-	struct proc *p;
-	struct getpgid_args *uap;
-	register_t *retval;
+getpgid(proc_t p, struct getpgid_args *uap, int32_t *retval)
 {
-	struct proc *pt;
+	proc_t pt;
+	int refheld = 0;
 
 	pt = p;
-	if (uap->pid == 0)
+	if (uap->pid == 0) {
 		goto found;
+	}
 
-	if ((pt = pfind(uap->pid)) == 0)
-		return (ESRCH);
+	if ((pt = proc_find(uap->pid)) == 0) {
+		return ESRCH;
+	}
+	refheld = 1;
 found:
-	*retval = pt->p_pgrp->pg_id;
-	return (0);
+	*retval = pt->p_pgrpid;
+	if (refheld != 0) {
+		proc_rele(pt);
+	}
+	return 0;
 }
 
+
 /*
- * Get an arbitary pid's session id.
+ * getsid
+ *
+ * Description:	Get an arbitary pid's session leaders process group ID
+ *
+ * Parameters:	uap->pid			The target pid
+ *
+ * Returns:	0				Success
+ *		ESRCH				No such process
+ *
+ * Notes:	We are permitted to return EPERM in the case that the target
+ *		process is not in the same session as the calling process,
+ *		which could be a security consideration
+ *
+ * XXX:		Belongs in kern_proc.c
  */
-struct getsid_args {
-	pid_t   pid;
-};
-
 int
-getsid(p, uap, retval)
-	struct proc *p;
-	struct getsid_args *uap;
-	register_t *retval;
+getsid(proc_t p, struct getsid_args *uap, int32_t *retval)
 {
-	struct proc *pt;
+	proc_t pt;
+	int refheld = 0;
+	struct session * sessp;
 
 	pt = p;
-	if (uap->pid == 0)
+	if (uap->pid == 0) {
 		goto found;
+	}
 
-	if ((pt = pfind(uap->pid)) == 0)
-		return (ESRCH);
+	if ((pt = proc_find(uap->pid)) == 0) {
+		return ESRCH;
+	}
+	refheld = 1;
 found:
-	*retval = pt->p_session->s_sid;
-	return (0);
+	sessp = proc_session(pt);
+	*retval = sessp->s_sid;
+	session_rele(sessp);
+
+	if (refheld != 0) {
+		proc_rele(pt);
+	}
+	return 0;
 }
 
-/* ARGSUSED */
-getuid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
-{
 
-	*retval = p->p_cred->p_ruid;
-#if COMPAT_43
-	retval[1] = p->p_ucred->cr_uid;
-#endif
-	return (0);
+/*
+ * getuid
+ *
+ * Description:	get real user ID for caller
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	uid_t				The real uid of the caller
+ */
+int
+getuid(__unused proc_t p, __unused struct getuid_args *uap, int32_t *retval)
+{
+	*retval = kauth_getruid();
+	return 0;
 }
 
-/* ARGSUSED */
-geteuid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
-{
 
-	*retval = p->p_ucred->cr_uid;
-	return (0);
+/*
+ * geteuid
+ *
+ * Description:	get effective user ID for caller
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	uid_t				The effective uid of the caller
+ */
+int
+geteuid(__unused proc_t p, __unused struct geteuid_args *uap, int32_t *retval)
+{
+	*retval = kauth_getuid();
+	return 0;
 }
 
-/* ARGSUSED */
-getgid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
+
+/*
+ * gettid
+ *
+ * Description:	Return the per-thread override identity.
+ *
+ * Parameters:	uap->uidp			Address of uid_t to get uid
+ *		uap->gidp			Address of gid_t to get gid
+ *
+ * Returns:	0				Success
+ *		ESRCH				No per thread identity active
+ */
+int
+gettid(__unused proc_t p, struct gettid_args *uap, int32_t *retval)
 {
+	struct uthread *uthread = get_bsdthread_info(current_thread());
+	int     error;
 
-	*retval = p->p_cred->p_rgid;
-#if COMPAT_43
-	retval[1] = p->p_ucred->cr_groups[0];
-#endif
-	return (0);
+	/*
+	 * If this thread is not running with an override identity, we can't
+	 * return one to the caller, so return an error instead.
+	 */
+	if (!(uthread->uu_flag & UT_SETUID)) {
+		return ESRCH;
+	}
+
+	if ((error = suword(uap->uidp, kauth_cred_getruid(uthread->uu_ucred)))) {
+		return error;
+	}
+	if ((error = suword(uap->gidp, kauth_cred_getrgid(uthread->uu_ucred)))) {
+		return error;
+	}
+
+	*retval = 0;
+	return 0;
 }
 
+
 /*
- * Get effective group ID.  The "egid" is groups[0], and could be obtained
- * via getgroups.  This syscall exists because it is somewhat painful to do
- * correctly in a library function.
+ * getgid
+ *
+ * Description:	get the real group ID for the calling process
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	gid_t				The real gid of the caller
  */
-/* ARGSUSED */
-getegid(p, uap, retval)
-	struct proc *p;
-	void *uap;
-	register_t *retval;
+int
+getgid(__unused proc_t p, __unused struct getgid_args *uap, int32_t *retval)
 {
+	*retval = kauth_getrgid();
+	return 0;
+}
+
 
-	*retval = p->p_ucred->cr_groups[0];
-	return (0);
+/*
+ * getegid
+ *
+ * Description:	get the effective group ID for the calling process
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	gid_t				The effective gid of the caller
+ *
+ * Notes:	As an implementation detail, the effective gid is stored as
+ *		the first element of the supplementary group list.
+ *
+ *		This could be implemented in Libc instead because of the above
+ *		detail.
+ */
+int
+getegid(__unused proc_t p, __unused struct getegid_args *uap, int32_t *retval)
+{
+	*retval = kauth_getgid();
+	return 0;
 }
 
-struct	getgroups_args {
-	u_int	gidsetsize;
-	gid_t 	*gidset;
-};
-getgroups(p, uap, retval)
-	struct proc *p;
-	register struct	getgroups_args *uap;
-	register_t *retval;
+
+/*
+ * getgroups
+ *
+ * Description:	get the list of supplementary groups for the calling process
+ *
+ * Parameters:	uap->gidsetsize			# of gid_t's in user buffer
+ *		uap->gidset			Pointer to user buffer
+ *
+ * Returns:	0				Success
+ *		EINVAL				User buffer too small
+ *	copyout:EFAULT				User buffer invalid
+ *
+ * Retval:	-1				Error
+ *		!0				# of groups
+ *
+ * Notes:	The caller may specify a 0 value for gidsetsize, and we will
+ *		then return how large a buffer is required (in gid_t's) to
+ *		contain the answer at the time of the call.  Otherwise, we
+ *		return the number of gid_t's catually copied to user space.
+ *
+ *		When called with a 0 gidsetsize from a multithreaded program,
+ *		there is no guarantee that another thread may not change the
+ *		number of supplementary groups, and therefore a subsequent
+ *		call could still fail, unless the maximum possible buffer
+ *		size is supplied by the user.
+ *
+ *		As an implementation detail, the effective gid is stored as
+ *		the first element of the supplementary group list, and will
+ *		be returned by this call.
+ */
+int
+getgroups(__unused proc_t p, struct getgroups_args *uap, int32_t *retval)
 {
-	register struct pcred *pc = p->p_cred;
-	register u_int ngrp;
+	int ngrp;
 	int error;
+	kauth_cred_t cred;
+	posix_cred_t pcred;
+
+	/* grab reference while we muck around with the credential */
+	cred = kauth_cred_get_with_ref();
+	pcred = posix_cred_get(cred);
 
 	if ((ngrp = uap->gidsetsize) == 0) {
-		*retval = pc->pc_ucred->cr_ngroups;
-		return (0);
-	}
-	if (ngrp < pc->pc_ucred->cr_ngroups)
-		return (EINVAL);
-	pcred_readlock(p);
-	ngrp = pc->pc_ucred->cr_ngroups;
-	if (error = copyout((caddr_t)pc->pc_ucred->cr_groups,
-	    (caddr_t)uap->gidset, ngrp * sizeof(gid_t))) {
-	    	pcred_unlock(p);
-		return (error);
-	}
-	pcred_unlock(p);
+		*retval = pcred->cr_ngroups;
+		kauth_cred_unref(&cred);
+		return 0;
+	}
+	if (ngrp < pcred->cr_ngroups) {
+		kauth_cred_unref(&cred);
+		return EINVAL;
+	}
+	ngrp = pcred->cr_ngroups;
+	if ((error = copyout((caddr_t)pcred->cr_groups,
+	    uap->gidset,
+	    ngrp * sizeof(gid_t)))) {
+		kauth_cred_unref(&cred);
+		return error;
+	}
+	kauth_cred_unref(&cred);
 	*retval = ngrp;
-	return (0);
+	return 0;
+}
+
+
+/*
+ * Return the per-thread/per-process supplementary groups list.
+ *
+ * XXX implement getsgroups
+ *
+ */
+
+int
+getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused int32_t *retval)
+{
+	return ENOTSUP;
+}
+
+/*
+ * Return the per-thread/per-process whiteout groups list.
+ *
+ * XXX implement getwgroups
+ *
+ */
+
+int
+getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused int32_t *retval)
+{
+	return ENOTSUP;
 }
 
-/* ARGSUSED */
-setsid(p, uap, retval)
-	register struct proc *p;
-	void *uap;
-	register_t *retval;
+/*
+ * setsid_internal
+ *
+ * Description:	Core implementation of setsid().
+ */
+int
+setsid_internal(proc_t p)
 {
+	struct pgrp * pg = PGRP_NULL;
 
-	if (p->p_pgid == p->p_pid || pgfind(p->p_pid) || p->p_flag & P_INVFORK) {
-		return (EPERM);
+	if (p->p_pgrpid == p->p_pid || (pg = pgfind(p->p_pid)) || p->p_lflag & P_LINVFORK) {
+		if (pg != PGRP_NULL) {
+			pg_rele(pg);
+		}
+		return EPERM;
 	} else {
+		/* enter pgrp works with its own pgrp refcount */
 		(void)enterpgrp(p, p->p_pid, 1);
+		return 0;
+	}
+}
+
+/*
+ * setsid
+ *
+ * Description:	Create a new session and set the process group ID to the
+ *		session ID
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	0				Success
+ *		EPERM				Permission denied
+ *
+ * Notes:	If the calling process is not the process group leader; there
+ *		is no existing process group with its ID, and we are not
+ *		currently in vfork, then this function will create a new
+ *		session, a new process group, and put the caller in the
+ *		process group (as the sole member) and make it the session
+ *		leader (as the sole process in the session).
+ *
+ *		The existing controlling tty (if any) will be dissociated
+ *		from the process, and the next non-O_NOCTTY open of a tty
+ *		will establish a new controlling tty.
+ *
+ * XXX:		Belongs in kern_proc.c
+ */
+int
+setsid(proc_t p, __unused struct setsid_args *uap, int32_t *retval)
+{
+	int rc = setsid_internal(p);
+	if (rc == 0) {
 		*retval = p->p_pid;
-		return (0);
 	}
+	return rc;
 }
 
+
 /*
- * set process group (setpgid/old setpgrp)
+ * setpgid
+ *
+ * Description: set process group ID for job control
+ *
+ * Parameters:	uap->pid			Process to change
+ *		uap->pgid			Process group to join or create
  *
- * caller does setpgid(targpid, targpgid)
+ * Returns:	0			Success
+ *		ESRCH			pid is not the caller or a child of
+ *					the caller
+ *	enterpgrp:ESRCH			No such process
+ *		EACCES			Permission denied due to exec
+ *		EINVAL			Invalid argument
+ *		EPERM			The target process is not in the same
+ *					session as the calling process
+ *		EPERM			The target process is a session leader
+ *		EPERM			pid and pgid are not the same, and
+ *					there is no process in the calling
+ *					process whose process group ID matches
+ *					pgid
  *
- * pid must be caller or child of caller (ESRCH)
- * if a child
- *	pid must be in same session (EPERM)
- *	pid can't have done an exec (EACCES)
- * if pgid != pid
- * 	there must exist some pid in same session having pgid (EPERM)
- * pid must not be session leader (EPERM)
+ * Notes:	This function will cause the target process to either join
+ *		an existing process process group, or create a new process
+ *		group in the session of the calling process.  It cannot be
+ *		used to change the process group ID of a process which is
+ *		already a session leader.
+ *
+ *		If the target pid is 0, the pid of the calling process is
+ *		substituted as the new target; if pgid is 0, the target pid
+ *		is used as the target process group ID.
+ *
+ * Legacy:	This system call entry point is also used to implement the
+ *		legacy library routine setpgrp(), which under POSIX
+ *
+ * XXX:		Belongs in kern_proc.c
  */
-struct setpgid_args {
-	int	pid;
-	int	pgid;
-};
-/* ARGSUSED */
-setpgid(curp, uap, retval)
-	struct proc *curp;
-	register struct setpgid_args *uap;
-	register_t *retval;
+int
+setpgid(proc_t curp, struct setpgid_args *uap, __unused int32_t *retval)
 {
-	register struct proc *targp;		/* target process */
-	register struct pgrp *pgrp;		/* target pgrp */
+	proc_t targp = PROC_NULL;       /* target process */
+	struct pgrp *pg = PGRP_NULL;    /* target pgrp */
+	int error = 0;
+	int refheld = 0;
+	int samesess = 0;
+	struct session * curp_sessp = SESSION_NULL;
+	struct session * targp_sessp = SESSION_NULL;
+
+	curp_sessp = proc_session(curp);
 
 	if (uap->pid != 0 && uap->pid != curp->p_pid) {
-		if ((targp = pfind(uap->pid)) == 0 || !inferior(targp))
-			return (ESRCH);
-		if (targp->p_session != curp->p_session)
-			return (EPERM);
-		if (targp->p_flag & P_EXEC)
-			return (EACCES);
-	} else
+		if ((targp = proc_find(uap->pid)) == 0 || !inferior(targp)) {
+			if (targp != PROC_NULL) {
+				refheld = 1;
+			}
+			error = ESRCH;
+			goto out;
+		}
+		refheld = 1;
+		targp_sessp = proc_session(targp);
+		if (targp_sessp != curp_sessp) {
+			error = EPERM;
+			goto out;
+		}
+		if (targp->p_flag & P_EXEC) {
+			error = EACCES;
+			goto out;
+		}
+	} else {
 		targp = curp;
-	if (SESS_LEADER(targp))
-		return (EPERM);
-	if (uap->pgid == 0)
+		targp_sessp = proc_session(targp);
+	}
+
+	if (SESS_LEADER(targp, targp_sessp)) {
+		error = EPERM;
+		goto out;
+	}
+	if (targp_sessp != SESSION_NULL) {
+		session_rele(targp_sessp);
+		targp_sessp = SESSION_NULL;
+	}
+
+	if (uap->pgid < 0) {
+		error = EINVAL;
+		goto out;
+	}
+	if (uap->pgid == 0) {
 		uap->pgid = targp->p_pid;
-	else if (uap->pgid != targp->p_pid)
-		if ((pgrp = pgfind(uap->pgid)) == 0 ||
-		    pgrp->pg_session != curp->p_session)
-			return (EPERM);
-	return (enterpgrp(targp, uap->pgid, 0));
+	} else if (uap->pgid != targp->p_pid) {
+		if ((pg = pgfind(uap->pgid)) == 0) {
+			error = EPERM;
+			goto out;
+		}
+		samesess = (pg->pg_session != curp_sessp);
+		pg_rele(pg);
+		if (samesess != 0) {
+			error = EPERM;
+			goto out;
+		}
+	}
+	error = enterpgrp(targp, uap->pgid, 0);
+out:
+	if (targp_sessp != SESSION_NULL) {
+		session_rele(targp_sessp);
+	}
+	if (curp_sessp != SESSION_NULL) {
+		session_rele(curp_sessp);
+	}
+	if (refheld != 0) {
+		proc_rele(targp);
+	}
+	return error;
+}
+
+
+/*
+ * issetugid
+ *
+ * Description:	Is current process tainted by uid or gid changes system call
+ *
+ * Parameters:	(void)
+ *
+ * Returns:	0				Not tainted
+ *		1				Tainted
+ *
+ * Notes:	A process is considered tainted if it was created as a retult
+ *		of an execve call from an imnage that had either the SUID or
+ *		SGID bit set on the executable, or if it has changed any of its
+ *		real, effective, or saved user or group IDs since beginning
+ *		execution.
+ */
+int
+proc_issetugid(proc_t p)
+{
+	return (p->p_flag & P_SUGID) ? 1 : 0;
 }
 
-struct issetugid_args {
-	int dummy;
-};
-issetugid(p, uap, retval)
-	struct proc *p;
-	struct issetugid_args *uap;
-	register_t *retval;
+int
+issetugid(proc_t p, __unused struct issetugid_args *uap, int32_t *retval)
 {
 	/*
 	 * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time,
@@ -351,472 +707,1336 @@ issetugid(p, uap, retval)
 	 * that libc *might* have put in their data segment.
 	 */
 
-	*retval = (p->p_flag & P_SUGID) ? 1 : 0;
-	return (0);
+	*retval = proc_issetugid(p);
+	return 0;
 }
 
-struct setuid_args {
-	uid_t	uid;
-};
-/* ARGSUSED */
-setuid(p, uap, retval)
-	struct proc *p;
-	struct setuid_args *uap;
-	register_t *retval;
+
+/*
+ * 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)
 {
-	register struct pcred *pc = p->p_cred;
-	register uid_t uid;
+	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;
-	AUDIT_ARG(uid, uid, 0, 0, 0);
-	if (uid != pc->p_ruid &&
-	    (error = suser(pc->pc_ucred, &p->p_acflag)))
-		return (error);
-	/*
-	 * Everything's okay, do it.
-	 * Transfer proc count to new user.
-	 * Copy credentials so other references do not see our changes.
-	 */
-
-	/* prepare app access profile files */
-	prepare_profile_database(uap->uid);
-	pcred_writelock(p);
-	(void)chgproccnt(pc->p_ruid, -1);
-	(void)chgproccnt(uid, 1);
-	pc->pc_ucred = crcopy(pc->pc_ucred);
-	pc->pc_ucred->cr_uid = uid;
-	pc->p_ruid = uid;
-	pc->p_svuid = uid;
-	pcred_unlock(p);
-	set_security_token(p);
-	p->p_flag |= P_SUGID;
-	return (0);
-}
 
-struct seteuid_args {
-	uid_t euid;
-};
-/* ARGSUSED */
-seteuid(p, uap, retval)
-	struct proc *p;
-	struct seteuid_args *uap;
-	register_t *retval;
-{
-	register struct pcred *pc = p->p_cred;
-	register uid_t euid;
-	int error;
+	/* 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);
 
-	euid = uap->euid;
-	AUDIT_ARG(uid, 0, euid, 0, 0);
-	if (euid != pc->p_ruid && euid != pc->p_svuid &&
-	    (error = suser(pc->pc_ucred, &p->p_acflag)))
-		return (error);
-	/*
-	 * Everything's okay, do it.  Copy credentials so other references do
-	 * not see our changes.
-	 */
-	pcred_writelock(p);
-	pc->pc_ucred = crcopy(pc->pc_ucred);
-	pc->pc_ucred->cr_uid = euid;
-	pcred_unlock(p);
-	set_security_token(p);
-	p->p_flag |= P_SUGID;
-	return (0);
-}
+	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);
 
-struct setgid_args {
-	gid_t	gid;
-};
-/* ARGSUSED */
-setgid(p, uap, retval)
-	struct proc *p;
-	struct setgid_args *uap;
-	register_t *retval;
-{
-	register struct pcred *pc = p->p_cred;
-	register gid_t gid;
-	int error;
+	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;
+		}
 
-	gid = uap->gid;
-	AUDIT_ARG(gid, gid, 0, 0, 0);
-	if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag)))
-		return (error);
-	pcred_writelock(p);
-	pc->pc_ucred = crcopy(pc->pc_ucred);
-	pc->pc_ucred->cr_groups[0] = gid;
-	pc->p_rgid = gid;
-	pc->p_svgid = gid;		/* ??? */
-	pcred_unlock(p);
-	set_security_token(p);
-	p->p_flag |= P_SUGID;
-	return (0);
-}
+		/*
+		 * 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;
+		}
 
-struct setegid_args {
-	gid_t	egid;
-};
-/* ARGSUSED */
-setegid(p, uap, retval)
-	struct proc *p;
-	struct setegid_args *uap;
-	register_t *retval;
-{
-	register struct pcred *pc = p->p_cred;
-	register gid_t egid;
-	int 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, 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);
 
-	egid = uap->egid;
-	AUDIT_ARG(gid, 0, egid, 0, 0);
-	if (egid != pc->p_rgid && egid != pc->p_svgid &&
-	    (error = suser(pc->pc_ucred, &p->p_acflag)))
-		return (error);
-	pcred_writelock(p);
-	pc->pc_ucred = crcopy(pc->pc_ucred);
-	pc->pc_ucred->cr_groups[0] = egid;
-	pcred_unlock(p);
 	set_security_token(p);
-	p->p_flag |= P_SUGID;
-	return (0);
+	return 0;
 }
 
-struct setgroups_args{
-	u_int	gidsetsize;
-	gid_t	*gidset;
-};
-
-/* ARGSUSED */
-setgroups(p, uap, retval)
-	struct proc *p;
-	struct setgroups_args *uap;
-	register_t *retval;
-{
-	register struct pcred *pc = p->p_cred;
-	struct ucred *new, *old;
-	register u_int ngrp;
-	int error;
 
-	if (error = suser(pc->pc_ucred, &p->p_acflag))
-		return (error);
-	ngrp = uap->gidsetsize;
-	if (ngrp > NGROUPS)
-		return (EINVAL);
-	new = crget();
-	
-	if ( ngrp < 1 ) {
-		ngrp = 1;
-	}
-	else {
-		error = copyin((caddr_t)uap->gidset,
-			(caddr_t)new->cr_groups, ngrp * sizeof(gid_t));
-		if (error) {
-			crfree(new);
-			return (error);
+/*
+ * 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;
 	}
-	new->cr_ngroups = ngrp;
-	AUDIT_ARG(groupset, new->cr_groups, ngrp);
-	pcred_writelock(p);
-	old = pc->pc_ucred;
-	new->cr_uid = old->cr_uid;
-	pc->pc_ucred = new;
-	pcred_unlock(p);
+	/* drop old proc reference or our extra reference */
+	kauth_cred_unref(&my_cred);
+
 	set_security_token(p);
-	p->p_flag |= P_SUGID;
-	if (old != NOCRED)
-		crfree(old);
-	return (0);
-}
-
-#if COMPAT_43
-struct osetreuid_args{
-	int	ruid;
-	int	euid;
-};
-/* ARGSUSED */
-osetreuid(p, uap, retval)
-	register struct proc *p;
-	struct osetreuid_args *uap;
-	register_t *retval;
-{
-	struct seteuid_args seuidargs;
-	struct setuid_args suidargs;
+	return 0;
+}
 
-	/*
-	 * There are five cases, and we attempt to emulate them in
-	 * the following fashion:
-	 * -1, -1: return 0. This is correct emulation.
-	 * -1,  N: call seteuid(N). This is correct emulation.
-	 *  N, -1: if we called setuid(N), our euid would be changed
-	 *         to N as well. the theory is that we don't want to
-	 * 	   revoke root access yet, so we call seteuid(N)
-	 * 	   instead. This is incorrect emulation, but often
-	 *	   suffices enough for binary compatibility.
-	 *  N,  N: call setuid(N). This is correct emulation.
-	 *  N,  M: call setuid(N). This is close to correct emulation.
-	 */
-	if (uap->ruid == (uid_t)-1) {
-		if (uap->euid == (uid_t)-1)
-			return (0);				/* -1, -1 */
-		seuidargs.euid = uap->euid;	/* -1,  N */
-		return (seteuid(p, &seuidargs, retval));
+
+/*
+ * 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;
+	posix_cred_t my_pcred;
+
+	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;
 	}
-	if (uap->euid == (uid_t)-1) {
-		seuidargs.euid = uap->ruid;	/* N, -1 */
-		return (seteuid(p, &seuidargs, retval));
+	AUDIT_ARG(euid, euid);
+	AUDIT_ARG(ruid, ruid);
+
+	my_cred = kauth_cred_proc_ref(p);
+	my_pcred = posix_cred_get(my_cred);
+
+	for (;;) {
+		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 = svuid */
+		    (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */
+			kauth_cred_unref(&my_cred);
+			return error;
+		}
+
+		uid_t new_euid;
+		uid_t svuid = KAUTH_UID_NONE;
+
+		new_euid = my_pcred->cr_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.
+		 */
+		if (euid != KAUTH_UID_NONE && my_pcred->cr_uid != euid) {
+			/* changing the effective UID */
+			new_euid = euid;
+			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);
+
+			/*
+			 * 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);
+				if (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
+					/*
+					 * We didn't successfully switch to the new ruid, so decrement
+					 * the procs/uid count that we incremented above.
+					 */
+					(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 (ruid != KAUTH_UID_NONE && !proc_has_persona(p)) {
+				/*
+				 * We switched to a new ruid, so decrement the count of procs running
+				 * under the previous ruid
+				 */
+				(void)chgproccnt(my_pcred->cr_ruid, -1);
+			}
+		}
+		break;
 	}
-	suidargs.uid = uap->ruid;	/* N, N and N, M */
-	return (setuid(p, &suidargs, retval));
+	/* drop old proc reference or our extra reference */
+	kauth_cred_unref(&my_cred);
+
+	set_security_token(p);
+	return 0;
 }
 
-struct osetregid_args {
-	int	rgid;
-	int egid;
-};
-/* ARGSUSED */
-osetregid(p, uap, retval)
-	register struct proc *p;
-	struct osetregid_args *uap;
-	register_t *retval;
+
+/*
+ * 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)
 {
-	struct setegid_args segidargs;
-	struct setgid_args sgidargs;
+	gid_t gid;
+	gid_t rgid = KAUTH_GID_NONE;
+	gid_t svgid = KAUTH_GID_NONE;
+	int error;
+	kauth_cred_t my_cred, my_new_cred;
+	posix_cred_t my_pcred;
 
-	/*
-	 * There are five cases, described above in osetreuid()
-	 */
-	if (uap->rgid == (gid_t)-1) {
-		if (uap->egid == (gid_t)-1)
-			return (0);				/* -1, -1 */
-		segidargs.egid = uap->egid;	/* -1,  N */
-		return (setegid(p, &segidargs, retval));
-	}
-	if (uap->egid == (gid_t)-1) {
-		segidargs.egid = uap->rgid;	/* N, -1 */
-		return (setegid(p, &segidargs, retval));
+	DEBUG_CRED_ENTER("setgid(%d/%d): %d\n", p->p_pid, (p->p_pptr ? p->p_pptr->p_pid : 0), uap->gid);
+
+	gid = uap->gid;
+	AUDIT_ARG(gid, gid);
+
+	/* 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);
+
+	for (;;) {
+		if (gid != my_pcred->cr_rgid &&         /* allow setgid(getgid()) */
+		    gid != my_pcred->cr_svgid &&        /* allow setgid(saved gid) */
+		    (error = suser(my_cred, &p->p_acflag))) {
+			kauth_cred_unref(&my_cred);
+			return error;
+		}
+
+		/*
+		 * If we are privileged, then set the saved and real GID too;
+		 * otherwise, just set the effective GID
+		 */
+		if (suser(my_cred, &p->p_acflag) == 0) {
+			svgid = gid;
+			rgid = gid;
+		} else {
+			svgid = KAUTH_GID_NONE;
+			rgid = KAUTH_GID_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.
+		 */
+		my_new_cred = kauth_cred_setresgid(my_cred, rgid, gid, svgid);
+		if (my_cred != my_new_cred) {
+			DEBUG_CRED_CHANGE("setgid(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_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);
+				/* try again */
+				my_cred = kauth_cred_proc_ref(p);
+				my_pcred = posix_cred_get(my_cred);
+				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;
 	}
-	sgidargs.gid = uap->rgid;	/* N, N and N, M */
-	return (setgid(p, &sgidargs, retval));
+	/* Drop old proc reference or our extra reference */
+	kauth_cred_unref(&my_cred);
+
+	set_security_token(p);
+	return 0;
 }
-#endif /* COMPAT_43 */
+
 
 /*
- * Check if gid is a member of the group set.
+ * setegid
+ *
+ * Description:	Set effective group ID system call
+ *
+ * Parameters:	uap->egid			effective gid to set
+ *
+ * Returns:	0				Success
+ *	suser:EPERM
+ *
+ * Notes:	If called by a privileged process, or called from an
+ *		unprivileged process but egid 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.
  */
-groupmember(gid, cred)
-	gid_t gid;
-	register struct ucred *cred;
+int
+setegid(proc_t p, struct setegid_args *uap, __unused int32_t *retval)
 {
-	register gid_t *gp;
-	gid_t *egp;
+	gid_t egid;
+	int error;
+	kauth_cred_t my_cred, my_new_cred;
+	posix_cred_t my_pcred;
+
+	DEBUG_CRED_ENTER("setegid %d\n", uap->egid);
+
+	egid = uap->egid;
+	AUDIT_ARG(egid, egid);
 
-	egp = &(cred->cr_groups[cred->cr_ngroups]);
-	for (gp = cred->cr_groups; gp < egp; gp++)
-		if (*gp == gid)
-			return (1);
-	return (0);
+	/* 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);
+
+
+	for (;;) {
+		if (egid != my_pcred->cr_rgid &&
+		    egid != my_pcred->cr_svgid &&
+		    (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_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_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);
+				/* try again */
+				my_cred = kauth_cred_proc_ref(p);
+				my_pcred = posix_cred_get(my_cred);
+				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;
 }
 
 /*
- * Test whether the specified credentials imply "super-user"
- * privilege; if so, and we have accounting info, set the flag
- * indicating use of super-powers.
- * Returns 0 or error.
+ * 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.
  */
-suser(cred, acflag)
-	struct ucred *cred;
-	u_short *acflag;
+int
+setregid(proc_t p, struct setregid_args *uap, __unused int32_t *retval)
 {
-#if DIAGNOSTIC
-	if (cred == NOCRED || cred == FSCRED)
-		panic("suser");
-#endif
-	if (cred->cr_uid == 0) {
-		if (acflag)
-			*acflag |= ASU;
-		return (0);
+	gid_t rgid, egid;
+	int error;
+	kauth_cred_t my_cred, my_new_cred;
+	posix_cred_t my_pcred;
+
+	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(egid, egid);
+	AUDIT_ARG(rgid, rgid);
+
+	/* 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);
+
+	for (;;) {
+		if (((rgid != KAUTH_UID_NONE &&         /* allow no change of rgid */
+		    rgid != my_pcred->cr_rgid &&        /* allow rgid = rgid */
+		    rgid != my_pcred->cr_gid &&         /* allow rgid = egid */
+		    rgid != my_pcred->cr_svgid) ||      /* allow rgid = svgid */
+		    (egid != KAUTH_UID_NONE &&          /* allow no change of egid */
+		    egid != my_pcred->cr_groups[0] &&   /* allow no change of egid */
+		    egid != my_pcred->cr_gid &&         /* allow egid = egid */
+		    egid != my_pcred->cr_rgid &&        /* allow egid = rgid */
+		    egid != my_pcred->cr_svgid)) &&     /* allow egid = svgid */
+		    (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */
+			kauth_cred_unref(&my_cred);
+			return error;
+		}
+
+		uid_t new_egid = my_pcred->cr_gid;
+		uid_t new_rgid = my_pcred->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_pcred->cr_gid != egid) {
+			/* changing the effective GID */
+			new_egid = egid;
+			OSBitOrAtomic(P_SUGID, &p->p_flag);
+		}
+		if (rgid != KAUTH_UID_NONE && my_pcred->cr_rgid != rgid) {
+			/* changing the real GID */
+			new_rgid = rgid;
+			OSBitOrAtomic(P_SUGID, &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_pcred->cr_svgid != uap->rgid &&
+		    my_pcred->cr_svgid != uap->egid) {
+			svgid = new_egid;
+			OSBitOrAtomic(P_SUGID, &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_pcred->cr_flags, my_new_cred, posix_cred_get(my_new_cred)->cr_flags);
+
+			proc_ucred_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_ucred_unlock(p);
+				kauth_cred_unref(&my_new_cred);
+				/* try again */
+				my_cred = kauth_cred_proc_ref(p);
+				my_pcred = posix_cred_get(my_cred);
+				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_ucred_unlock(p);
+		}
+		break;
 	}
-	return (EPERM);
+	/* 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 privileged, 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
-is_suser(void)
+settid(proc_t p, struct settid_args *uap, __unused int32_t *retval)
 {
-	struct proc *p = current_proc();
+	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);
+	AUDIT_ARG(gid, gid);
+
+	if (proc_suser(p) != 0) {
+		return EPERM;
+	}
 
-	if (!p)
-		return (0);
+	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;
 
-	return (suser(p->p_ucred, &p->p_acflag) == 0);
+		/* 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);
+	}
+	/*
+	 * 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.
+	 */
+	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
-is_suser1(void)
+settid_with_pid(proc_t p, struct settid_with_pid_args *uap, __unused int32_t *retval)
 {
-	struct proc *p = current_proc();
+	proc_t target_proc;
+	struct uthread *uthread = get_bsdthread_info(current_thread());
+	kauth_cred_t my_cred, my_target_cred, my_new_cred;
+	posix_cred_t my_target_pcred;
 
-	if (!p)
-		return (0);
+	AUDIT_ARG(pid, uap->pid);
+	AUDIT_ARG(value32, uap->assume);
+
+	if (proc_suser(p) != 0) {
+		return EPERM;
+	}
 
-	return (suser(p->p_ucred, &p->p_acflag) == 0 ||
-			p->p_cred->p_ruid == 0 || p->p_cred->p_svuid == 0);
+	/*
+	 * 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_target_pcred = posix_cred_get(my_target_cred);
+		my_new_cred = kauth_cred_setuidgid(my_cred, my_target_pcred->cr_uid, my_target_pcred->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;
 }
 
+
 /*
- * Allocate a zeroed cred structure.
+ * 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.
  */
-struct ucred *
-crget()
+static int
+setgroups1(proc_t p, u_int ngrp, user_addr_t gidset, uid_t gmuid, __unused int32_t *retval)
 {
-	register struct ucred *cr;
+	gid_t   newgroups[NGROUPS] = { 0 };
+	int     error;
+
+	DEBUG_CRED_ENTER("setgroups1 (%d/%d): %d 0x%016x %d\n", p->p_pid,
+	    (p->p_pptr ? p->p_pptr->p_pid : 0), ngrp, gidset, gmuid);
+
+	if (ngrp > NGROUPS) {
+		return EINVAL;
+	}
 
-	MALLOC_ZONE(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK);
-	bzero((caddr_t)cr, sizeof(*cr));
-	cr->cr_ref = 1;
-	return (cr);
+	if (ngrp >= 1) {
+		error = copyin(gidset,
+		    (caddr_t)newgroups, ngrp * sizeof(gid_t));
+		if (error) {
+			return error;
+		}
+	}
+	return setgroups_internal(p, ngrp, newgroups, gmuid);
 }
 
+int
+setgroups_internal(proc_t p, u_int ngrp, gid_t *newgroups, uid_t gmuid)
+{
+	struct uthread *uthread = get_bsdthread_info(current_thread());
+	kauth_cred_t my_cred, my_new_cred;
+	int     error;
+
+	my_cred = kauth_cred_proc_ref(p);
+	if ((error = suser(my_cred, &p->p_acflag))) {
+		kauth_cred_unref(&my_cred);
+		return error;
+	}
+
+	if (ngrp < 1) {
+		ngrp = 1;
+		newgroups[0] = 0;
+	}
+
+	if ((uthread->uu_flag & UT_SETUID) != 0) {
+#if DEBUG_CRED
+		int my_cred_flags = uthread->uu_ucred->cr_flags;
+#endif  /* DEBUG_CRED */
+		kauth_cred_unref(&my_cred);
+
+		/*
+		 * If this thread is under an assumed identity, set the
+		 * supplementary grouplist on the thread credential instead
+		 * of the process one.  If we were the only reference holder,
+		 * the credential is updated in place, otherwise, our reference
+		 * is dropped and we get back a different cred with a reference
+		 * already held on it.  Because this is per-thread, we don't
+		 * need the referencing/locking/retry required for per-process.
+		 */
+		my_cred = uthread->uu_ucred;
+		uthread->uu_ucred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, gmuid);
+#if DEBUG_CRED
+		if (my_cred != uthread->uu_ucred) {
+			DEBUG_CRED_CHANGE("setgroups1(CH)%d: %p/0x%08x->%p/0x%08x\n", p->p_pid, my_cred, my_cred_flags, uthread->uu_ucred, uthread->uu_ucred->cr_flags);
+		}
+#endif  /* DEBUG_CRED */
+	} else {
+		/*
+		 * 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_setgroups(my_cred, &newgroups[0], ngrp, gmuid);
+			if (my_cred != my_new_cred) {
+				DEBUG_CRED_CHANGE("setgroups1(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_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);
+					/* 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 */
+		AUDIT_ARG(groupset, posix_cred_get(my_cred)->cr_groups, ngrp);
+		kauth_cred_unref(&my_cred);
+
+
+		set_security_token(p);
+	}
+
+	return 0;
+}
+
+
 /*
- * Free a cred structure.
- * Throws away space when ref count gets to 0.
+ * initgroups
+ *
+ * Description: Initialize the default supplementary groups list and set the
+ *		gmuid for use by the external group resolver (if any)
+ *
+ * Parameters:	uap->gidsetsize			Number of groups in set
+ *		uap->gidset			Pointer to group list
+ *		uap->gmuid			Base gid
+ *
+ * Returns:	0				Success
+ *	setgroups1:EPERM			Permision denied
+ *	setgroups1:EINVAL			Invalid gidsetsize value
+ *	setgroups1:EFAULT			Bad gidset or gidsetsize is
+ *
+ * Notes:	This function opts *IN* to memberd participation
+ *
+ *		The normal purpose of this function is for a privileged
+ *		process to indicate supplementary groups and identity for
+ *		participation in extended group membership resolution prior
+ *		to dropping privilege by assuming a specific user identity.
+ *
+ *		It is the first half of the primary mechanism whereby user
+ *		identity is established to the system by programs such as
+ *		/usr/bin/login.  The second half is the drop of uid privilege
+ *		for a specific uid corresponding to the user.
+ *
+ * See also:	setgroups1()
  */
-void
-crfree(cr)
-	struct ucred *cr;
+int
+initgroups(proc_t p, struct initgroups_args *uap, __unused int32_t *retval)
 {
-#if DIAGNOSTIC
-	if (cr == NOCRED || cr == FSCRED)
-		panic("crfree");
-#endif
-	if (--cr->cr_ref == 0)
-		FREE_ZONE((caddr_t)cr, sizeof *cr, M_CRED);
+	DEBUG_CRED_ENTER("initgroups\n");
+
+	return setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval);
 }
 
+
 /*
- * Copy cred structure to a new one and free the old one.
+ * setgroups
+ *
+ * Description: Initialize the default supplementary groups list
+ *
+ * Parameters:	gidsetsize			Number of groups in set
+ *		gidset				Pointer to group list
+ *
+ * Returns:	0				Success
+ *	setgroups1:EPERM			Permision denied
+ *	setgroups1:EINVAL			Invalid gidsetsize value
+ *	setgroups1:EFAULT			Bad gidset or gidsetsize is
+ *
+ * Notes:	This functions opts *OUT* of memberd participation.
+ *
+ *		This function exists for compatibility with POSIX.  Most user
+ *		programs should use initgroups() instead to ensure correct
+ *		participation in group membership resolution when utilizing
+ *		a directory service for authentication.
+ *
+ *		It is identical to an initgroups() call with a gmuid argument
+ *		of KAUTH_UID_NONE.
+ *
+ * See also:	setgroups1()
  */
-struct ucred *
-crcopy(cr)
-	struct ucred *cr;
+int
+setgroups(proc_t p, struct setgroups_args *uap, __unused int32_t *retval)
 {
-	struct ucred *newcr;
+	DEBUG_CRED_ENTER("setgroups\n");
 
-#if DIAGNOSTIC
-	if (cr == NOCRED || cr == FSCRED)
-		panic("crcopy");
-#endif
-	if (cr->cr_ref == 1)
-		return (cr);
-	newcr = crget();
-	*newcr = *cr;
-	crfree(cr);
-	newcr->cr_ref = 1;
-	return (newcr);
+	return setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval);
 }
 
+
 /*
- * Dup cred struct to a new held one.
+ * Set the per-thread/per-process supplementary groups list.
+ *
+ * XXX implement setsgroups
+ *
  */
-struct ucred *
-crdup(cr)
-	struct ucred *cr;
+
+int
+setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused int32_t *retval)
 {
-	struct ucred *newcr;
+	return ENOTSUP;
+}
 
-#if DIAGNOSTIC
-	if (cr == NOCRED || cr == FSCRED)
-		panic("crdup");
-#endif
-	newcr = crget();
-	*newcr = *cr;
-	newcr->cr_ref = 1;
-	return (newcr);
+/*
+ * Set the per-thread/per-process whiteout groups list.
+ *
+ * XXX implement setwgroups
+ *
+ */
+
+int
+setwgroups(__unused proc_t p, __unused struct setwgroups_args *uap, __unused int32_t *retval)
+{
+	return ENOTSUP;
 }
 
+
 /*
- * compare two cred structs
+ * Check if gid is a member of the group set.
+ *
+ * XXX This interface is going away; use kauth_cred_ismember_gid() directly
+ * XXX instead.
  */
 int
-crcmp(cr1, cr2)
-	struct ucred *cr1;
-	struct ucred *cr2;
+groupmember(gid_t gid, kauth_cred_t cred)
 {
-	int i;
+	int is_member;
 
-	if (cr1 == cr2)
-		return 0;
-	if (cr1 == NOCRED || cr1 == FSCRED ||
-	    cr2 == NOCRED || cr2 == FSCRED)
-		return 1;
-	if (cr1->cr_uid != cr2->cr_uid)
-		return 1;
-	if (cr1->cr_ngroups != cr2->cr_ngroups)
+	if (kauth_cred_ismember_gid(cred, gid, &is_member) == 0 && is_member) {
 		return 1;
-	// XXX assumes groups will always be listed in some order
-	for (i=0; i < cr1->cr_ngroups; i++)
-		if (cr1->cr_groups[i] != cr2->cr_groups[i])
-			return 1;
-	return (0);
+	}
+	return 0;
+}
+
+
+/*
+ * Test whether the specified credentials imply "super-user"
+ * privilege; if so, and we have accounting info, set the flag
+ * indicating use of super-powers.
+ * Returns 0 or error.
+ *
+ * XXX This interface is going away; use kauth_cred_issuser() directly
+ * XXX instead.
+ *
+ * Note:	This interface exists to implement the "has used privilege"
+ *		bit (ASU) in the p_acflags field of the process, which is
+ *		only externalized via private sysctl and in process accounting
+ *		records.  The flag is technically not required in either case.
+ */
+int
+suser(kauth_cred_t cred, u_short *acflag)
+{
+#if DIAGNOSTIC
+	if (!IS_VALID_CRED(cred)) {
+		panic("suser");
+	}
+#endif
+	if (kauth_cred_getuid(cred) == 0) {
+		if (acflag) {
+			*acflag |= ASU;
+		}
+		return 0;
+	}
+	return EPERM;
 }
 
+
 /*
- * Get login name, if available.
+ * getlogin
+ *
+ * Description:	Get login name, if available.
+ *
+ * Parameters:	uap->namebuf			User buffer for return
+ *		uap->namelen			User buffer length
+ *
+ * Returns:	0				Success
+ *	copyout:EFAULT
+ *
+ * Notes:	Intended to obtain a string containing the user name of the
+ *		user associated with the controlling terminal for the calling
+ *		process.
+ *
+ *		Not very useful on modern systems, due to inherent length
+ *		limitations for the static array in the session structure
+ *		which is used to store the login name.
+ *
+ *		Permitted to return NULL
+ *
+ * XXX:		Belongs in kern_proc.c
  */
-struct getlogin_args {
-	char 	*namebuf;
-	u_int	namelen;
-};
-/* ARGSUSED */
-getlogin(p, uap, retval)
-	struct proc *p;
-	struct getlogin_args *uap;
-	register_t *retval;
+int
+getlogin(proc_t p, struct getlogin_args *uap, __unused int32_t *retval)
 {
+	char buffer[MAXLOGNAME + 1];
+	struct session * sessp;
+
+	bzero(buffer, MAXLOGNAME + 1);
+
+	sessp = proc_session(p);
 
-	if (uap->namelen > sizeof (p->p_pgrp->pg_session->s_login))
-		uap->namelen = sizeof (p->p_pgrp->pg_session->s_login);
-	return (copyout((caddr_t) p->p_pgrp->pg_session->s_login,
-	    (caddr_t)uap->namebuf, uap->namelen));
+	if (uap->namelen > MAXLOGNAME) {
+		uap->namelen = MAXLOGNAME;
+	}
+
+	if (sessp != SESSION_NULL) {
+		session_lock(sessp);
+		bcopy( sessp->s_login, buffer, uap->namelen);
+		session_unlock(sessp);
+	}
+	session_rele(sessp);
+
+	return copyout((caddr_t)buffer, uap->namebuf, uap->namelen);
+}
+
+void
+setlogin_internal(proc_t p, const char login[static MAXLOGNAME])
+{
+	struct session *sessp = proc_session(p);
+
+	if (sessp != SESSION_NULL) {
+		session_lock(sessp);
+		bcopy(login, sessp->s_login, MAXLOGNAME);
+		session_unlock(sessp);
+		session_rele(sessp);
+	}
 }
 
 /*
- * Set login name.
+ * setlogin
+ *
+ * Description:	Set login name.
+ *
+ * Parameters:	uap->namebuf			User buffer containing name
+ *
+ * Returns:	0				Success
+ *	suser:EPERM				Permission denied
+ *	copyinstr:EFAULT			User buffer invalid
+ *	copyinstr:EINVAL			Supplied name was too long
+ *
+ * Notes:	This is a utility system call to support getlogin().
+ *
+ * XXX:		Belongs in kern_proc.c
  */
-struct setlogin_args {
-	char	*namebuf;
-};
-/* ARGSUSED */
-setlogin(p, uap, retval)
-	struct proc *p;
-	struct setlogin_args *uap;
-	register_t *retval;
+int
+setlogin(proc_t p, struct setlogin_args *uap, __unused int32_t *retval)
 {
 	int error;
-	int dummy=0;
-
-	if (error = suser(p->p_ucred, &p->p_acflag))
-		return (error);
-	 
-	error = copyinstr((caddr_t) uap->namebuf,
-	    (caddr_t) p->p_pgrp->pg_session->s_login,
-	    sizeof (p->p_pgrp->pg_session->s_login) - 1, (size_t *)&dummy);
-	if(!error)
-		AUDIT_ARG(text, p->p_pgrp->pg_session->s_login);
-	else if (error == ENAMETOOLONG)
+	size_t dummy = 0;
+	char buffer[MAXLOGNAME + 1];
+
+	if ((error = proc_suser(p))) {
+		return error;
+	}
+
+	bzero(&buffer[0], MAXLOGNAME + 1);
+
+
+	error = copyinstr(uap->namebuf,
+	    (caddr_t) &buffer[0],
+	    MAXLOGNAME - 1, (size_t *)&dummy);
+
+	setlogin_internal(p, buffer);
+
+	if (!error) {
+		AUDIT_ARG(text, buffer);
+	} else if (error == ENAMETOOLONG) {
 		error = EINVAL;
-	return (error);
+	}
+	return error;
 }
 
 
 /* Set the secrity token of the task with current euid and eguid */
-kern_return_t
-set_security_token(struct proc * p)
+/*
+ * XXX This needs to change to give the task a reference and/or an opaque
+ * XXX identifier.
+ */
+int
+set_security_token(proc_t p)
 {
-	security_token_t sec_token;
-	audit_token_t    audit_token;
+	return set_security_token_task_internal(p, p->task);
+}
 
-	sec_token.val[0] = p->p_ucred->cr_uid;
- 	sec_token.val[1] = p->p_ucred->cr_gid;
+static void
+proc_calc_audit_token(proc_t p, kauth_cred_t my_cred, audit_token_t *audit_token)
+{
+	posix_cred_t my_pcred = posix_cred_get(my_cred);
 
 	/*
 	 * The current layout of the Mach audit token explicitly
@@ -827,36 +2047,122 @@ set_security_token(struct proc * p)
 	 * the user of the trailer from future representation
 	 * changes.
 	 */
-	audit_token.val[0] = p->p_au->ai_auid;
-	audit_token.val[1] = p->p_ucred->cr_uid;
- 	audit_token.val[2] = p->p_ucred->cr_gid;
-	audit_token.val[3] = p->p_cred->p_ruid;
-        audit_token.val[4] = p->p_cred->p_rgid;
-	audit_token.val[5] = p->p_pid;
-	audit_token.val[6] = p->p_au->ai_asid;
-	audit_token.val[7] = p->p_au->ai_termid.port;
+	audit_token->val[0] = my_cred->cr_audit.as_aia_p->ai_auid;
+	audit_token->val[1] = my_pcred->cr_uid;
+	audit_token->val[2] = my_pcred->cr_gid;
+	audit_token->val[3] = my_pcred->cr_ruid;
+	audit_token->val[4] = my_pcred->cr_rgid;
+	audit_token->val[5] = p->p_pid;
+	audit_token->val[6] = my_cred->cr_audit.as_aia_p->ai_asid;
+	audit_token->val[7] = p->p_idversion;
+}
+
+/*
+ * Set the secrity token of the task with current euid and eguid
+ * The function takes a proc and a task, where proc->task might point to a
+ * different task if called from exec.
+ */
+
+int
+set_security_token_task_internal(proc_t p, void *t)
+{
+	kauth_cred_t my_cred;
+	security_token_t sec_token;
+	audit_token_t    audit_token;
+	host_priv_t host_priv;
+	task_t task = t;
+
+	/*
+	 * Don't allow a vfork child to override the parent's token settings
+	 * (since they share a task).  Instead, the child will just have to
+	 * suffer along using the parent's token until the exec().  It's all
+	 * undefined behavior anyway, right?
+	 */
+	if (task == current_task()) {
+		uthread_t        uthread;
+		uthread = (uthread_t)get_bsdthread_info(current_thread());
+		if (uthread->uu_flag & UT_VFORK) {
+			return 1;
+		}
+	}
+
+	my_cred = kauth_cred_proc_ref(p);
+
+	proc_calc_audit_token(p, my_cred, &audit_token);
+
+	/* XXX mach_init doesn't have a p_ucred when it calls this function */
+	if (IS_VALID_CRED(my_cred)) {
+		sec_token.val[0] = kauth_cred_getuid(my_cred);
+		sec_token.val[1] = kauth_cred_getgid(my_cred);
+	} else {
+		sec_token.val[0] = 0;
+		sec_token.val[1] = 0;
+	}
+
+	host_priv = (sec_token.val[0]) ? HOST_PRIV_NULL : host_priv_self();
+#if CONFIG_MACF
+	if (host_priv != HOST_PRIV_NULL && mac_system_check_host_priv(my_cred)) {
+		host_priv = HOST_PRIV_NULL;
+	}
+#endif
+	kauth_cred_unref(&my_cred);
+
+#if DEVELOPMENT || DEBUG
+	/*
+	 * Update the pid an proc name for importance base if any
+	 */
+	task_importance_update_owner_info(task);
+#endif
 
 	return host_security_set_task_token(host_security_self(),
-					   p->task,
-					   sec_token,
-					   audit_token,
-					   (sec_token.val[0]) ?
-						HOST_PRIV_NULL :
-						host_priv_self());
+	           task,
+	           sec_token,
+	           audit_token,
+	           host_priv) != KERN_SUCCESS;
+}
+
+void
+proc_parent_audit_token(proc_t p, audit_token_t *token_out)
+{
+	proc_t parent;
+	kauth_cred_t my_cred;
+
+	proc_list_lock();
+
+	parent = p->p_pptr;
+	my_cred = kauth_cred_proc_ref(parent);
+	proc_calc_audit_token(parent, my_cred, token_out);
+	kauth_cred_unref(&my_cred);
+
+	proc_list_unlock();
+}
+
+
+int get_audit_token_pid(audit_token_t *audit_token);
+
+int
+get_audit_token_pid(audit_token_t *audit_token)
+{
+	/* keep in-sync with set_security_token (above) */
+	if (audit_token) {
+		return (int)audit_token->val[5];
+	}
+	return -1;
 }
 
 
 /*
- * Fill in a struct xucred based on a struct ucred.
+ * Fill in a struct xucred based on a kauth_cred_t.
  */
 __private_extern__
 void
-cru2x(struct ucred *cr, struct xucred *xcr)
+cru2x(kauth_cred_t cr, struct xucred *xcr)
 {
+	posix_cred_t pcr = posix_cred_get(cr);
 
 	bzero(xcr, sizeof(*xcr));
 	xcr->cr_version = XUCRED_VERSION;
-	xcr->cr_uid = cr->cr_uid;
-	xcr->cr_ngroups = cr->cr_ngroups;
-	bcopy(cr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups));
+	xcr->cr_uid = kauth_cred_getuid(cr);
+	xcr->cr_ngroups = pcr->cr_ngroups;
+	bcopy(pcr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups));
 }