]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_prot.c
xnu-1228.12.14.tar.gz
[apple/xnu.git] / bsd / kern / kern_prot.c
index 8ed96a39095e89e8c38f1d025710168ad417572a..46ab8bf1bce557f9ec7da33cf08d57b2a2c9618c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  *     @(#)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.
+ */
 
 /*
  * System calls related to processes and protection
 
 #include <bsm/audit_kernel.h>
 
+#if CONFIG_LCTX
+#include <sys/lctx.h>
+#endif
+
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#if CONFIG_MACF_MACH
+#include <secuity/mac_mach_internal.h>
+#endif
+#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>
+
 
 int groupmember(gid_t gid, kauth_cred_t cred);
-int is_suser(void);
-int is_suser1(void);
 
-extern int prepare_profile_database(int user);
+/*
+ * 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 */
+
+
 
 /*
- * 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
  */
 int
-setprivexec(struct proc *p, struct setprivexec_args *uap, register_t *retval)
+setprivexec(proc_t p, struct setprivexec_args *uap, register_t *retval)
 {
        AUDIT_ARG(value, uap->flag);
        *retval = p->p_debugger;
@@ -109,93 +160,194 @@ setprivexec(struct proc *p, struct setprivexec_args *uap, register_t *retval)
        return(0);
 }
 
-/* ARGSUSED */
+
+/*
+ * getpid
+ *
+ * Description:        get the process ID
+ *
+ * Parameters: (void)
+ *
+ * Returns:    pid_t                           Current process ID
+ *
+ * XXX:                Belongs in kern_proc.c
+ */
 int
-getpid(struct proc *p, __unused struct getpid_args *uap, register_t *retval)
+getpid(proc_t p, __unused struct getpid_args *uap, register_t *retval)
 {
 
        *retval = p->p_pid;
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * getppid
+ *
+ * Description: get the parent process ID
+ *
+ * Parameters: (void)
+ *
+ * Returns:    pid_t                           Parent process ID
+ *
+ * XXX:                Belongs in kern_proc.c
+ */
 int
-getppid(struct proc *p, __unused struct getppid_args *uap, register_t *retval)
+getppid(proc_t p, __unused struct getppid_args *uap, register_t *retval)
 {
 
-       *retval = p->p_pptr->p_pid;
+       *retval = p->p_ppid;
        return (0);
 }
 
-/* Get process group ID; note that POSIX getpgrp takes no parameter */
+
+/*
+ * 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(struct proc *p, __unused struct getpgrp_args *uap, register_t *retval)
+getpgrp(proc_t p, __unused struct getpgrp_args *uap, register_t *retval)
 {
 
-       *retval = p->p_pgrp->pg_id;
+       *retval = p->p_pgrpid;
        return (0);
 }
 
-/* Get an arbitary pid's process group id */
+
+/*
+ * 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(struct proc *p, struct getpgid_args *uap, register_t *retval)
+getpgid(proc_t p, struct getpgid_args *uap, register_t *retval)
 {
-       struct proc *pt;
+       proc_t pt;
+       int refheld = 0;
 
        pt = p;
        if (uap->pid == 0)
                goto found;
 
-       if ((pt = pfind(uap->pid)) == 0)
+       if ((pt = proc_find(uap->pid)) == 0)
                return (ESRCH);
+       refheld = 1;
 found:
-       *retval = pt->p_pgrp->pg_id;
+       *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
  */
-
 int
-getsid(struct proc *p, struct getsid_args *uap, register_t *retval)
+getsid(proc_t p, struct getsid_args *uap, register_t *retval)
 {
-       struct proc *pt;
+       proc_t pt;
+       int refheld = 0;
+       struct session * sessp;
 
        pt = p;
        if (uap->pid == 0)
                goto found;
 
-       if ((pt = pfind(uap->pid)) == 0)
+       if ((pt = proc_find(uap->pid)) == 0)
                return (ESRCH);
+       refheld = 1;
 found:
-       *retval = pt->p_session->s_sid;
+       sessp = proc_session(pt);
+       *retval = sessp->s_sid;
+       session_rele(sessp);
+
+       if (refheld != 0)
+               proc_rele(pt);
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * getuid
+ *
+ * Description:        get real user ID for caller
+ *
+ * Parameters: (void)
+ *
+ * Returns:    uid_t                           The real uid of the caller
+ */
 int
-getuid(__unused struct proc *p, __unused struct getuid_args *uap, register_t *retval)
+getuid(__unused proc_t p, __unused struct getuid_args *uap, register_t *retval)
 {
 
        *retval = kauth_getruid();
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * geteuid
+ *
+ * Description:        get effective user ID for caller
+ *
+ * Parameters: (void)
+ *
+ * Returns:    uid_t                           The effective uid of the caller
+ */
 int
-geteuid(__unused struct proc *p, __unused struct geteuid_args *uap, register_t *retval)
+geteuid(__unused proc_t p, __unused struct geteuid_args *uap, register_t *retval)
 {
 
        *retval = kauth_getuid();
        return (0);
 }
 
+
 /*
- * Return the per-thread override identity.
+ * 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 struct proc *p, struct gettid_args *uap, register_t *retval)
+gettid(__unused proc_t p, struct gettid_args *uap, register_t *retval)
 {
        struct uthread *uthread = get_bsdthread_info(current_thread());
        int     error;
@@ -216,33 +368,83 @@ gettid(__unused struct proc *p, struct gettid_args *uap, register_t *retval)
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * getgid
+ *
+ * Description:        get the real group ID for the calling process
+ *
+ * Parameters: (void)
+ *
+ * Returns:    gid_t                           The real gid of the caller
+ */
 int
-getgid(__unused struct proc *p, __unused struct getgid_args *uap, register_t *retval)
+getgid(__unused proc_t p, __unused struct getgid_args *uap, register_t *retval)
 {
 
        *retval = kauth_getrgid();
        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.
+ * 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.
  */
-/* ARGSUSED */
 int
-getegid(struct proc *p, __unused struct getegid_args *uap, register_t *retval)
+getegid(__unused proc_t p, __unused struct getegid_args *uap, register_t *retval)
 {
 
        *retval = kauth_getgid();
        return (0);
 }
 
+
+/*
+ * 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 struct proc *p, struct getgroups_args *uap, register_t *retval)
+getgroups(__unused proc_t p, struct getgroups_args *uap, register_t *retval)
 {
-       register int ngrp;
+       int ngrp;
        int error;
        kauth_cred_t cred;
 
@@ -270,12 +472,13 @@ getgroups(__unused struct proc *p, struct getgroups_args *uap, register_t *retva
        return (0);
 }
 
+
 /*
  * Return the per-thread/per-process supplementary groups list.
  */
-#warning XXX implement
+#warning XXX implement getsgroups
 int
-getsgroups(__unused struct proc *p, __unused struct getsgroups_args *uap, __unused register_t *retval)
+getsgroups(__unused proc_t p, __unused struct getsgroups_args *uap, __unused register_t *retval)
 {
        /* XXX implement */
        return(ENOTSUP);
@@ -284,73 +487,186 @@ getsgroups(__unused struct proc *p, __unused struct getsgroups_args *uap, __unus
 /*
  * Return the per-thread/per-process whiteout groups list.
  */
-#warning XXX implement
+#warning XXX implement getwgroups
 int
-getwgroups(__unused struct proc *p, __unused struct getwgroups_args *uap, __unused register_t *retval)
+getwgroups(__unused proc_t p, __unused struct getwgroups_args *uap, __unused register_t *retval)
 {
        /* XXX implement */
        return(ENOTSUP);
 }
 
-/* ARGSUSED */
+
+/*
+ * 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(struct proc *p, __unused struct setsid_args *uap, register_t *retval)
+setsid(proc_t p, __unused struct setsid_args *uap, register_t *retval)
 {
+       struct pgrp * pg = PGRP_NULL;
 
-       if (p->p_pgid == p->p_pid || pgfind(p->p_pid) || p->p_flag & P_INVFORK) {
+       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);
                *retval = p->p_pid;
                return (0);
        }
 }
 
+
 /*
- * set process group (setpgid/old setpgrp)
- *
- * caller does setpgid(targpid, targpgid)
- *
- * 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)
- * ig pgid is -ve return EINVAL (as per SUV spec)
- * if pgid != pid
- *     there must exist some pid in same session having pgid (EPERM)
- * pid must not be session leader (EPERM)
+ * setpgid
+ *
+ * Description: set process group ID for job control
+ *
+ * Parameters: uap->pid                        Process to change
+ *             uap->pgid                       Process group to join or create
+ *
+ * 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
+ *
+ * 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
  */
-/* ARGSUSED */
 int
-setpgid(struct proc *curp, register struct setpgid_args *uap, __unused register_t *retval)
+setpgid(proc_t curp, register struct setpgid_args *uap, __unused register_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)
-               return(EINVAL);
+               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
-issetugid(struct proc *p, __unused struct issetugid_args *uap, register_t *retval)
+issetugid(proc_t p, __unused struct issetugid_args *uap, register_t *retval)
 {
        /*
         * Note: OpenBSD sets a P_SUGIDEXEC flag set at execve() time,
@@ -365,227 +681,676 @@ issetugid(struct proc *p, __unused struct issetugid_args *uap, register_t *retva
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * 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(struct proc *p, struct setuid_args *uap, __unused register_t *retval)
+setuid(proc_t p, struct setuid_args *uap, __unused register_t *retval)
 {
-       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;
 
+
        uid = uap->uid;
+
+       my_cred = kauth_cred_proc_ref(p);
+
+       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, 0, 0, 0);
-       if (uid != p->p_ucred->cr_ruid &&
-           (error = suser(p->p_ucred, &p->p_acflag)))
+
+       if (uid != my_cred->cr_ruid &&  /* allow setuid(getuid()) */
+           uid != my_cred->cr_svuid && /* allow setuid(saved uid) */
+           (error = suser(my_cred, &p->p_acflag))) {
+               kauth_cred_unref(&my_cred);
                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);
-       (void)chgproccnt(kauth_getruid(), -1);
-       (void)chgproccnt(uid, 1);
+       /*
+        * If we are priviledged, 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;
+               /*
+                * Transfer proc count to new user.
+                * chgproccnt uses list lock for protection
+                */
+               (void)chgproccnt(uid, 1);
+               (void)chgproccnt(kauth_getruid(), -1);
+       }
 
        /* get current credential and take a reference while we muck with it */
        for (;;) {
-               my_cred = kauth_cred_proc_ref(p);
-               
-               /* 
+               /*
+                * 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_cred->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_setuid(my_cred, uid);
+                */
+               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_cred->cr_flags, my_new_cred, my_new_cred->cr_flags);
+
                        proc_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.
+                       /*
+                        * 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;
-                       p->p_flag |= P_SUGID;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
                        proc_unlock(p);
                }
-               /* drop old proc reference or our extra reference */
+               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 register_t *retval)
+{
+       uid_t euid;
+       int error;
+       kauth_cred_t my_cred, my_new_cred;
+
+       DEBUG_CRED_ENTER("seteuid: %d\n", uap->euid);
+
+       euid = uap->euid;
+       AUDIT_ARG(uid, 0, euid, 0, 0);
+
+       my_cred = kauth_cred_proc_ref(p);
+
+       if (euid != my_cred->cr_ruid && euid != my_cred->cr_svuid &&
+           (error = suser(my_cred, &p->p_acflag))) {
                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 (;;) {
+               /* 
+                * 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_cred->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_cred->cr_flags, my_new_cred, 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;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
+                       proc_unlock(p);
+               }
                break;
        }
+       /* 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 register_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(uid, euid, ruid, 0, 0);
+
+       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;
        
+               /* 
+                * 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_cred->cr_uid != euid) {
+                       /* changing the effective UID */
+                       new_euid = euid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
+               }
+               if (ruid != KAUTH_UID_NONE && my_cred->cr_ruid != ruid) {
+                       /* changing the real UID; must do user accounting */
+                       /* chgproccnt uses list lock for protection */
+                       (void)chgproccnt(ruid, 1);
+                       (void)chgproccnt(my_cred->cr_ruid, -1);
+                       new_ruid = ruid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&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_cred->cr_svuid != uap->ruid &&
+                   my_cred->cr_svuid != uap->euid) {
+                       svuid = new_euid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
+               }
+
+               my_new_cred = kauth_cred_setresuid(my_cred, ruid, euid, svuid, my_cred->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_cred->cr_flags, my_new_cred, 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;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&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);
 }
 
-/* ARGSUSED */
+
+/*
+ * 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
-seteuid(struct proc *p, struct seteuid_args *uap, __unused register_t *retval)
+setgid(proc_t p, struct setgid_args *uap, __unused register_t *retval)
 {
-       register uid_t euid;
+       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;
 
-       euid = uap->euid;
-       AUDIT_ARG(uid, 0, euid, 0, 0);
-       if (euid != p->p_ucred->cr_ruid && euid != p->p_ucred->cr_svuid &&
-           (error = suser(p->p_ucred, &p->p_acflag)))
+       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, 0, 0, 0);
+
+       my_cred = kauth_cred_proc_ref(p);
+
+       if (gid != my_cred->cr_rgid &&  /* allow setgid(getgid()) */
+           gid != my_cred->cr_svgid && /* allow setgid(saved gid) */
+           (error = suser(my_cred, &p->p_acflag))) {
+               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
+        * If we are priviledged, 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;
+       }
+
+       /* get current credential and take a reference while we muck with it */
        for (;;) {
-               my_cred = kauth_cred_proc_ref(p);
-       
-               /* 
+               
+               /* 
                 * 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_seteuid(my_cred, euid);
-       
+                */
+               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_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.
+                        * 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);
                                /* try again */
+                               my_cred = kauth_cred_proc_ref(p);
                                continue;
                        }
                        p->p_ucred = my_new_cred;
-                       p->p_flag |= P_SUGID;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
                        proc_unlock(p);
                }
-               /* drop old proc reference or our extra reference */
-               kauth_cred_unref(&my_cred);
                break;
        }
-
+       /* Drop old proc reference or our extra reference */
+       kauth_cred_unref(&my_cred);
+       
        set_security_token(p);
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * 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.
+ */
 int
-setgid(struct proc *p, struct setgid_args *uap, __unused register_t *retval)
+setegid(proc_t p, struct setegid_args *uap, __unused register_t *retval)
 {
-       register gid_t gid;
+       gid_t egid;
        int error;
        kauth_cred_t my_cred, my_new_cred;
 
-       gid = uap->gid;
-       AUDIT_ARG(gid, gid, 0, 0, 0);
-       if (gid != p->p_ucred->cr_rgid && (error = suser(p->p_ucred, &p->p_acflag)))
+       DEBUG_CRED_ENTER("setegid %d\n", uap->egid);
+
+       egid = uap->egid;
+       AUDIT_ARG(gid, 0, egid, 0, 0);
+
+       my_cred = kauth_cred_proc_ref(p);
+
+       if (egid != my_cred->cr_rgid &&
+           egid != my_cred->cr_svgid &&
+           (error = suser(my_cred, &p->p_acflag))) {
+               kauth_cred_unref(&my_cred);
                return (error);
+       }
 
        /* get current credential and take a reference while we muck with it */
        for (;;) {
-               my_cred = kauth_cred_proc_ref(p);
-               
-               /* 
+               /* 
                 * 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_setgid(my_cred, gid);
+                */
+               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_cred->cr_flags, my_new_cred, 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.
+                        * 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);
                                /* try again */
+                               my_cred = kauth_cred_proc_ref(p);
                                continue;
                        }
                        p->p_ucred = my_new_cred;
-                       p->p_flag |= P_SUGID;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
                        proc_unlock(p);
                }
-               /* drop old proc reference or our extra reference */
-               kauth_cred_unref(&my_cred);
                break;
        }
-       
+
+       /* Drop old proc reference or our extra reference */
+       kauth_cred_unref(&my_cred);
+
        set_security_token(p);
        return (0);
 }
 
-/* ARGSUSED */
+/*
+ * 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.
+ */
 int
-setegid(struct proc *p, struct setegid_args *uap, __unused register_t *retval)
+setregid(proc_t p, struct setregid_args *uap, __unused register_t *retval)
 {
-       register gid_t egid;
+       gid_t rgid, egid;
        int error;
        kauth_cred_t my_cred, my_new_cred;
 
+       DEBUG_CRED_ENTER("setregid %d %d\n", uap->rgid, uap->egid);
+
+       rgid = uap->rgid;
        egid = uap->egid;
-       AUDIT_ARG(gid, 0, egid, 0, 0);
-       if (egid != p->p_ucred->cr_rgid && egid != p->p_ucred->cr_svgid &&
-           (error = suser(p->p_ucred, &p->p_acflag)))
+
+       if (rgid == (uid_t)-1)
+               rgid = KAUTH_GID_NONE;
+       if (egid == (uid_t)-1)
+               egid = KAUTH_GID_NONE;
+       AUDIT_ARG(gid, egid, rgid, 0, 0);
+
+       my_cred = kauth_cred_proc_ref(p);
+
+       if (((rgid != KAUTH_UID_NONE &&         /* allow no change of rgid */
+             rgid != my_cred->cr_rgid &&       /* allow rgid = rgid */
+             rgid != my_cred->cr_gid &&        /* allow rgid = egid */
+             rgid != my_cred->cr_svgid) ||     /* allow rgid = svgid */
+            (egid != KAUTH_UID_NONE &&         /* allow no change of egid */
+             egid != my_cred->cr_groups[0] &&  /* allow no change of egid */
+             egid != my_cred->cr_gid &&        /* allow egid = egid */
+             egid != my_cred->cr_rgid &&       /* allow egid = rgid */
+             egid != my_cred->cr_svgid)) &&    /* allow egid = svgid */
+           (error = suser(my_cred, &p->p_acflag))) { /* allow root user any */
+               kauth_cred_unref(&my_cred);
                return (error);
+       }
 
        /* get current credential and take a reference while we muck with it */
        for (;;) {
-               my_cred = kauth_cred_proc_ref(p);
+               uid_t new_egid = my_cred->cr_gid;
+               uid_t new_rgid = my_cred->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_cred->cr_groups[0] != egid) {
+                       /* changing the effective GID */
+                       new_egid = egid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
+               }
+               if (rgid != KAUTH_UID_NONE && my_cred->cr_rgid != rgid) {
+                       /* changing the real GID */
+                       new_rgid = rgid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&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.
                 */
-               my_new_cred = kauth_cred_setegid(my_cred, egid);
+               if (my_cred->cr_svgid != uap->rgid &&
+                   my_cred->cr_svgid != uap->egid) {
+                       svgid = new_egid;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&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_cred->cr_flags, my_new_cred, my_new_cred->cr_flags);
+
                        proc_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.
+                       /* 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);
                                /* try again */
+                               my_cred = kauth_cred_proc_ref(p);
                                continue;
                        }
                        p->p_ucred = my_new_cred;
-                       p->p_flag |= P_SUGID;
+                       OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag); /* XXX redundant? */
                        proc_unlock(p);
                }
-               /* drop old proc reference or our extra reference */
-               kauth_cred_unref(&my_cred);
                break;
        }
+       /* 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 priviledged, it
@@ -595,20 +1360,19 @@ setegid(struct proc *p, struct setegid_args *uap, __unused register_t *retval)
  * thread to the requested UID and single GID, and clears all other GIDs.
  */
 int
-settid(struct proc *p, struct settid_args *uap, __unused register_t *retval)
+settid(proc_t p, struct settid_args *uap, __unused register_t *retval)
 {
        kauth_cred_t uc;
        struct uthread *uthread = get_bsdthread_info(current_thread());
-       register uid_t uid;
-       register gid_t gid;
+       uid_t uid;
+       gid_t gid;
 
        uid = uap->uid;
        gid = uap->gid;
        AUDIT_ARG(uid, uid, gid, gid, 0);
 
-       if (suser(p->p_ucred, &p->p_acflag) != 0) {
+       if (proc_suser(p) != 0) 
                return (EPERM);
-       }
        
        if (uid == KAUTH_UID_NONE) {
 
@@ -630,9 +1394,11 @@ settid(struct proc *p, struct settid_args *uap, __unused register_t *retval)
                }
 
                /*
-                * get a new credential instance from the old if this one changes else
-                * kauth_cred_setuidgid returns the same credential.  we take an extra 
-                * reference on the current credential while we muck wit it here.
+                * 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;
@@ -641,7 +1407,7 @@ settid(struct proc *p, struct settid_args *uap, __unused register_t *retval)
                        uthread->uu_ucred = my_new_cred;
                uthread->uu_flag |= UT_SETUID;
 
-               /* drop our extra reference */
+               /* Drop old uthread reference or our extra reference */
                kauth_cred_unref(&my_cred);
        }
        /*
@@ -653,16 +1419,19 @@ settid(struct proc *p, struct settid_args *uap, __unused register_t *retval)
        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 
+ * 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 
+ *
+ * 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(struct proc *p, struct settid_with_pid_args *uap, __unused register_t *retval)
+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());
@@ -671,7 +1440,7 @@ settid_with_pid(struct proc *p, struct settid_with_pid_args *uap, __unused regis
        AUDIT_ARG(pid, uap->pid);
        AUDIT_ARG(value, uap->assume);
 
-       if (suser(p->p_ucred, &p->p_acflag) != 0) {
+       if (proc_suser(p) != 0) {
                return (EPERM);
        }
 
@@ -691,15 +1460,25 @@ settid_with_pid(struct proc *p, struct settid_with_pid_args *uap, __unused regis
                if ((uthread->uu_flag & UT_SETUID) != 0)
                        return (EPERM);
        
-               target_proc = pfind(uap->pid);
+               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.
+                * 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;
@@ -710,15 +1489,18 @@ settid_with_pid(struct proc *p, struct settid_with_pid_args *uap, __unused regis
        
                uthread->uu_flag |= UT_SETUID;
                
-               /* drop our extra references */
+               /* 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);
        }
        
-       /* we are reverting back to normal mode of operation where delayed binding
-        * of the process credential sets the credential in the thread (uu_ucred)
+       /*
+        * 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);
@@ -732,26 +1514,60 @@ settid_with_pid(struct proc *p, struct settid_with_pid_args *uap, __unused regis
        return (0);
 }
 
-/* ARGSUSED */
+
+/*
+ * 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(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused register_t *retval)
+setgroups1(proc_t p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __unused register_t *retval)
 {
-       register u_int ngrp;
+       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());
 
-       if ((error = suser(p->p_ucred, &p->p_acflag)))
-               return (error);
+       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 {
+       } else {
                error = copyin(gidset,
                        (caddr_t)newgroups, ngrp * sizeof(gid_t));
                if (error) {
@@ -759,7 +1575,18 @@ setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __
                }
        }
 
+       my_cred = kauth_cred_proc_ref(p);
+       if ((error = suser(my_cred, &p->p_acflag))) {
+               kauth_cred_unref(&my_cred);
+               return (error);
+       }
+
        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
@@ -768,13 +1595,14 @@ setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __
                 * 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.
-                *
-                * Hack: this opts into memberd to avoid needing to use a per
-                * thread credential initgroups() instead of setgroups() in
-                * AFP server to address <rdar://4561060>
                 */
                my_cred = uthread->uu_ucred;
-               uthread->uu_ucred = kauth_cred_setgroups(my_cred, &newgroups[0], ngrp, my_cred->cr_gmuid);
+               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 {
 
                /*
@@ -782,8 +1610,6 @@ setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __
                 * with it
                 */
                for (;;) {
-                       my_cred = kauth_cred_proc_ref(p);
-
                        /* 
                         * Set the credential with new info.  If there is no
                         * change, we get back the same credential we passed
@@ -794,9 +1620,12 @@ setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __
                         */
                        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_lock(p);
                                /*
-                                * need to protect for a race where another
+                                * 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
@@ -805,43 +1634,106 @@ setgroups1(struct proc *p, u_int gidsetsize, user_addr_t gidset, uid_t gmuid, __
                                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;
-                               p->p_flag |= P_SUGID;
+                               OSBitOrAtomic(P_SUGID, (UInt32 *)&p->p_flag);
                                proc_unlock(p);
                        }
-                       /* drop old proc reference or our extra reference */
-                       kauth_cred_unref(&my_cred);
                        break;
                }
+               /* Drop old proc reference or our extra reference */
+               AUDIT_ARG(groupset, my_cred->cr_groups, ngrp);
+               kauth_cred_unref(&my_cred);
+
 
-               AUDIT_ARG(groupset, p->p_ucred->cr_groups, ngrp);
                set_security_token(p);
        }
 
        return (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()
+ */
 int
-initgroups(struct proc *p, struct initgroups_args *uap, __unused register_t *retval)
+initgroups(proc_t p, struct initgroups_args *uap, __unused register_t *retval)
 {
+       DEBUG_CRED_ENTER("initgroups\n");
+
        return(setgroups1(p, uap->gidsetsize, uap->gidset, uap->gmuid, retval));
 }
 
+
+/*
+ * 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()
+ */
 int
-setgroups(struct proc *p, struct setgroups_args *uap, __unused register_t *retval)
+setgroups(proc_t p, struct setgroups_args *uap, __unused register_t *retval)
 {
+       DEBUG_CRED_ENTER("setgroups\n");
+
        return(setgroups1(p, uap->gidsetsize, uap->gidset, KAUTH_UID_NONE, retval));
 }
 
+
 /*
  * Set the per-thread/per-process supplementary groups list.
  */
-#warning XXX implement
+#warning XXX implement setsgroups
 int
-setsgroups(__unused struct proc *p, __unused struct setsgroups_args *uap, __unused register_t *retval)
+setsgroups(__unused proc_t p, __unused struct setsgroups_args *uap, __unused register_t *retval)
 {
        return(ENOTSUP);
 }
@@ -849,17 +1741,19 @@ setsgroups(__unused struct proc *p, __unused struct setsgroups_args *uap, __unus
 /*
  * Set the per-thread/per-process whiteout groups list.
  */
-#warning XXX implement
+#warning XXX implement setwgroups
 int
-setwgroups(__unused struct proc *p, __unused struct setwgroups_args *uap, __unused register_t *retval)
+setwgroups(__unused proc_t p, __unused struct setwgroups_args *uap, __unused register_t *retval)
 {
        return(ENOTSUP);
 }
 
+
 /*
  * Check if gid is a member of the group set.
  *
- * XXX This interface is going away
+ * XXX This interface is going away; use kauth_cred_ismember_gid() directly
+ * XXX instead.
  */
 int
 groupmember(gid_t gid, kauth_cred_t cred)
@@ -871,13 +1765,20 @@ groupmember(gid_t gid, kauth_cred_t cred)
        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
+ * 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)
@@ -894,62 +1795,140 @@ suser(kauth_cred_t cred, u_short *acflag)
        return (EPERM);
 }
 
+
+/*
+ * XXX This interface is going away; use kauth_cred_issuser() directly
+ * XXX instead.
+ */
 int
 is_suser(void)
 {
-       struct proc *p = current_proc();
+       proc_t p = current_proc();
 
        if (!p)
                return (0);
 
-       return (suser(p->p_ucred, &p->p_acflag) == 0);
+       return (proc_suser(p) == 0);
 }
 
+
+/*
+ * XXX This interface is going away; use kauth_cred_issuser() directly
+ * XXX instead.
+ */
 int
 is_suser1(void)
 {
-       struct proc *p = current_proc();
+       proc_t p = current_proc();
+       kauth_cred_t my_cred;
+       int err;
 
        if (!p)
                return (0);
 
-       return (suser(p->p_ucred, &p->p_acflag) == 0 ||
-                       p->p_ucred->cr_ruid == 0 || p->p_ucred->cr_svuid == 0);
+       my_cred = kauth_cred_proc_ref(p);
+
+       err =  (suser(my_cred, &p->p_acflag) == 0 ||
+                       my_cred->cr_ruid == 0 || my_cred->cr_svuid == 0);
+       kauth_cred_unref(&my_cred);
+       return(err);
 }
 
+
 /*
- * 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
  */
-/* ARGSUSED */
 int
-getlogin(struct proc *p, struct getlogin_args *uap, __unused register_t *retval)
+getlogin(proc_t p, struct getlogin_args *uap, __unused register_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,
-                                       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));
 }
 
+
 /*
- * 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
  */
-/* ARGSUSED */
 int
-setlogin(struct proc *p, struct setlogin_args *uap, __unused register_t *retval)
+setlogin(proc_t p, struct setlogin_args *uap, __unused register_t *retval)
 {
        int error;
        int dummy=0;
+       char buffer[MAXLOGNAME+1];
+       struct session * sessp;
 
-       if ((error = suser(p->p_ucred, &p->p_acflag)))
+       if ((error = proc_suser(p)))
                return (error);
-        
+
+       bzero(&buffer[0], MAXLOGNAME+1);
+
+
        error = copyinstr(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)
+           (caddr_t) &buffer[0],
+           MAXLOGNAME - 1, (size_t *)&dummy);
+
+       sessp = proc_session(p);
+
+       if (sessp != SESSION_NULL) {
+               session_lock(sessp);
+               bcopy(buffer, sessp->s_login, MAXLOGNAME);
+               session_unlock(sessp);
+               session_rele(sessp);
+       }
+
+
+       if (!error) {
+               AUDIT_ARG(text, buffer);
+        } else if (error == ENAMETOOLONG)
                error = EINVAL;
        return (error);
 }
@@ -961,10 +1940,12 @@ setlogin(struct proc *p, struct setlogin_args *uap, __unused register_t *retval)
  * XXX identifier.
  */
 int
-set_security_token(struct proc * p)
+set_security_token(proc_t p)
 {
        security_token_t sec_token;
        audit_token_t    audit_token;
+       kauth_cred_t my_cred;
+       host_priv_t host_priv;
 
        /*
         * Don't allow a vfork child to override the parent's token settings
@@ -979,10 +1960,11 @@ set_security_token(struct proc * p)
                        return (1);
        }
                
+       my_cred = kauth_cred_proc_ref(p);
        /* XXX mach_init doesn't have a p_ucred when it calls this function */
-       if (IS_VALID_CRED(p->p_ucred)) {
-               sec_token.val[0] = kauth_cred_getuid(p->p_ucred);
-               sec_token.val[1] = p->p_ucred->cr_gid;
+       if (IS_VALID_CRED(my_cred)) {
+               sec_token.val[0] = kauth_cred_getuid(my_cred);
+               sec_token.val[1] = my_cred->cr_gid;
        } else {
                sec_token.val[0] = 0;
                sec_token.val[1] = 0;
@@ -997,22 +1979,31 @@ set_security_token(struct proc * p)
         * the user of the trailer from future representation
         * changes.
         */
-       audit_token.val[0] = p->p_ucred->cr_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_ucred->cr_ruid;
-       audit_token.val[4] = p->p_ucred->cr_rgid;
+       audit_token.val[0] = my_cred->cr_au.ai_auid;
+       audit_token.val[1] = my_cred->cr_uid;
+       audit_token.val[2] = my_cred->cr_gid;
+       audit_token.val[3] = my_cred->cr_ruid;
+       audit_token.val[4] = my_cred->cr_rgid;
        audit_token.val[5] = p->p_pid;
-       audit_token.val[6] = p->p_ucred->cr_au.ai_asid;
-       audit_token.val[7] = p->p_ucred->cr_au.ai_termid.port;
+       audit_token.val[6] = my_cred->cr_au.ai_asid;
+       audit_token.val[7] = p->p_idversion;
+
+#if CONFIG_MACF_MACH
+       mac_task_label_update_cred(my_cred, p->task);
+#endif
+       
+       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);
 
        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()) != KERN_SUCCESS);
+                                          host_priv) != KERN_SUCCESS);
 }
 
 
@@ -1030,3 +2021,170 @@ cru2x(kauth_cred_t cr, struct xucred *xcr)
        xcr->cr_ngroups = cr->cr_ngroups;
        bcopy(cr->cr_groups, xcr->cr_groups, sizeof(xcr->cr_groups));
 }
+
+#if CONFIG_LCTX
+
+/*
+ * Set Login Context ID
+ */
+/*
+ * MPSAFE - assignment of (visible) process to context protected by ALLLCTX_LOCK,
+ *         LCTX by its own locks.
+ */
+int
+setlcid(proc_t p0, struct setlcid_args *uap, __unused register_t *retval)
+{
+       proc_t p;
+       struct lctx *l;
+       int error = 0;
+       int refheld = 0;
+
+       AUDIT_ARG(pid, uap->pid);
+       AUDIT_ARG(value, uap->lcid);
+       if (uap->pid == LCID_PROC_SELF) {       /* Create/Join/Leave */
+               p = p0;
+       } else {                                /* Adopt/Orphan */
+               p = proc_find(uap->pid);
+               if (p == NULL)
+                       return (ESRCH);
+               refheld = 1;
+       }
+
+#if CONFIG_MACF
+       error = mac_proc_check_setlcid(p0, p, uap->pid, uap->lcid);
+       if (error)
+               goto out;
+#endif
+
+       switch (uap->lcid) {
+       /* Leave/Orphan */
+       case LCID_REMOVE:
+
+               /* Only root may Leave/Orphan. */
+               if (!is_suser1()) {
+                       error = EPERM;
+                       goto out;
+               }
+
+               /* Process not in login context. */
+               if (p->p_lctx == NULL) {
+                       error = ENOATTR;
+                       goto out;
+               }
+
+               l = NULL;
+
+               break;
+
+       /* Create */
+       case LCID_CREATE:
+
+               /* Create only valid for self! */
+               if (uap->pid != LCID_PROC_SELF) {
+                       error = EPERM;
+                       goto out;
+               }
+
+               /* Already in a login context. */
+               if (p->p_lctx != NULL) {
+                       error = EPERM;
+                       goto out;
+               }
+
+               l = lccreate();
+               if (l == NULL) {
+                       error = ENOMEM;
+                       goto out;
+               }
+
+               LCTX_LOCK(l);
+
+               break;
+
+       /* Join/Adopt */
+       default:
+
+               /* Only root may Join/Adopt. */
+               if (!is_suser1()) {
+                       error = EPERM;
+                       goto out;
+               }
+
+               l = lcfind(uap->lcid);
+               if (l == NULL) {
+                       error = ENOATTR;
+                       goto out;
+               }
+
+               break;
+       }
+
+       ALLLCTX_LOCK;
+       leavelctx(p);
+       enterlctx(p, l, (uap->lcid == LCID_CREATE) ? 1 : 0);
+       ALLLCTX_UNLOCK;
+
+out:
+       if (refheld != 0)
+               proc_rele(p);
+       return (error);
+}
+
+/*
+ * Get Login Context ID
+ */
+/*
+ * MPSAFE - membership of (visible) process in a login context
+ *         protected by the all-context lock.
+ */
+int
+getlcid(proc_t p0, struct getlcid_args *uap, register_t *retval)
+{
+       proc_t p;
+       int error = 0;
+       int refheld = 0;
+
+       AUDIT_ARG(pid, uap->pid);
+       if (uap->pid == LCID_PROC_SELF) {
+               p = p0;
+       } else {
+               p = proc_find(uap->pid);
+               if (p == NULL)
+                       return (ESRCH);
+               refheld = 1;
+       }
+
+#if CONFIG_MACF
+       error = mac_proc_check_getlcid(p0, p, uap->pid);
+       if (error)
+               goto out;
+#endif
+       ALLLCTX_LOCK;
+       if (p->p_lctx == NULL) {
+               error = ENOATTR;
+               ALLLCTX_UNLOCK;
+               goto out;
+       }
+       *retval = p->p_lctx->lc_id;
+       ALLLCTX_UNLOCK;
+ out:
+       if (refheld != 0)
+               proc_rele(p);
+
+       return (error);
+}
+#else  /* LCTX */
+int
+setlcid(proc_t p0, struct setlcid_args *uap, register_t *retval)
+{
+
+       return (ENOSYS);
+}
+
+int
+getlcid(proc_t p0, struct getlcid_args *uap, register_t *retval)
+{
+
+       return (ENOSYS);
+}
+#endif /* !LCTX */