]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_credential.c
xnu-2782.20.48.tar.gz
[apple/xnu.git] / bsd / kern / kern_credential.c
index 4ab7311ae2dec258cbd437a78f18e907435b9aac..b3c0d357afa8c42c01071e9257352ec9f76fb44d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
 #include <sys/malloc.h>
 #include <sys/kauth.h>
 #include <sys/kernel.h>
+#include <sys/sdt.h>
 
 #include <security/audit/audit.h>
 
 #include <sys/mount.h>
+#include <sys/stat.h>  /* For manifest constants in posix_cred_access */
 #include <sys/sysproto.h>
-#include <sys/kern_callout.h>
 #include <mach/message.h>
 #include <mach/host_security.h>
 
-/* mach_absolute_time() */
-#include <mach/clock_types.h>
-#include <mach/mach_types.h>
-#include <mach/mach_time.h>
-
 #include <libkern/OSAtomic.h>
 
 #include <kern/task.h>
-#include <kern/lock.h>
+#include <kern/locks.h>
 #ifdef MACH_ASSERT
 # undef MACH_ASSERT
 #endif
@@ -75,6 +71,7 @@
 #if CONFIG_MACF
 #include <security/mac.h>
 #include <security/mac_framework.h>
+#include <security/_label.h>
 #endif
 
 void mach_kauth_cred_uthread_update( void );
@@ -83,6 +80,25 @@ void mach_kauth_cred_uthread_update( void );
 
 # define NULLCRED_CHECK(_c)    do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
 
+/* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */
+#if 0
+#ifdef KAUTH_DEBUG
+#undef KAUTH_DEBUG
+#endif
+
+#ifdef K_UUID_FMT
+#undef K_UUID_FMT
+#endif
+
+#ifdef K_UUID_ARG
+#undef K_UUID_ARG
+#endif
+
+# define K_UUID_FMT "%08x:%08x:%08x:%08x"
+# define K_UUID_ARG(_u) *(int *)&_u.g_guid[0],*(int *)&_u.g_guid[4],*(int *)&_u.g_guid[8],*(int *)&_u.g_guid[12]
+# define KAUTH_DEBUG(fmt, args...)      do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0)
+#endif
+
 /*
  * Credential debugging; we can track entry into a function that might
  * change a credential, and we can track actual credential changes that
@@ -129,6 +145,7 @@ cred_debug_buffer * cred_debug_buf_p = NULL;
 
 #endif /* !DEBUG_CRED */
 
+#if CONFIG_EXT_RESOLVER
 /*
  * Interface to external identity resolver.
  *
@@ -143,6 +160,7 @@ static lck_mtx_t *kauth_resolver_mtx;
 #define KAUTH_RESOLVER_UNLOCK()        lck_mtx_unlock(kauth_resolver_mtx);
 
 static volatile pid_t  kauth_resolver_identity;
+static int     kauth_identitysvc_has_registered;
 static int     kauth_resolver_registered;
 static uint32_t        kauth_resolver_sequence;
 static int     kauth_resolver_timeout = 30;    /* default: 30 seconds */
@@ -150,8 +168,8 @@ static int  kauth_resolver_timeout = 30;    /* default: 30 seconds */
 struct kauth_resolver_work {
        TAILQ_ENTRY(kauth_resolver_work) kr_link;
        struct kauth_identity_extlookup kr_work;
+       uint64_t        kr_extend;
        uint32_t        kr_seqno;
-       uint64_t        kr_subtime;     /* submission time */
        int             kr_refs;
        int             kr_flags;
 #define KAUTH_REQUEST_UNSUBMITTED      (1<<0)
@@ -164,10 +182,87 @@ TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_
 TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted;
 TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work)      kauth_resolver_done;
 
-static int     kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
+/* Number of resolver timeouts between logged complaints */
+#define KAUTH_COMPLAINT_INTERVAL 1000
+int kauth_resolver_timeout_cnt = 0;
+
+static int     kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data);
 static int     kauth_resolver_complete(user_addr_t message);
 static int     kauth_resolver_getwork(user_addr_t message);
 static int     kauth_resolver_getwork2(user_addr_t message);
+static __attribute__((noinline)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
+       struct kauth_resolver_work *); 
+
+#define        KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
+
+struct kauth_identity {
+       TAILQ_ENTRY(kauth_identity) ki_link;
+       int     ki_valid;
+       uid_t   ki_uid;
+       gid_t   ki_gid;
+       int     ki_supgrpcnt;
+       gid_t   ki_supgrps[NGROUPS];
+       guid_t  ki_guid;
+       ntsid_t ki_ntsid;
+       const char      *ki_name;       /* string name from string cache */
+       /*
+        * Expiry times are the earliest time at which we will disregard the
+        * cached state and go to userland.  Before then if the valid bit is
+        * set, we will return the cached value.  If it's not set, we will
+        * not go to userland to resolve, just assume that there is no answer
+        * available.
+        */
+       time_t  ki_groups_expiry;
+       time_t  ki_guid_expiry;
+       time_t  ki_ntsid_expiry;
+};
+
+static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
+static lck_mtx_t *kauth_identity_mtx;
+#define KAUTH_IDENTITY_LOCK()  lck_mtx_lock(kauth_identity_mtx);
+#define KAUTH_IDENTITY_UNLOCK()        lck_mtx_unlock(kauth_identity_mtx);
+#define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100    /* XXX default sizing? */
+static int kauth_identity_cachemax = KAUTH_IDENTITY_CACHEMAX_DEFAULT;
+static int kauth_identity_count;
+
+static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
+       ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry,
+       const char *name, int nametype);
+static void    kauth_identity_register_and_free(struct kauth_identity *kip);
+static void    kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip, uint64_t extend_data);
+static void    kauth_identity_trimcache(int newsize);
+static void    kauth_identity_lru(struct kauth_identity *kip);
+static int     kauth_identity_guid_expired(struct kauth_identity *kip);
+static int     kauth_identity_ntsid_expired(struct kauth_identity *kip);
+static int     kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname);
+static int     kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir, char *getname);
+static int     kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname);
+static int     kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname);
+static int     kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir);
+
+struct kauth_group_membership {
+       TAILQ_ENTRY(kauth_group_membership) gm_link;
+       uid_t   gm_uid;         /* the identity whose membership we're recording */
+       gid_t   gm_gid;         /* group of which they are a member */
+       time_t  gm_expiry;      /* TTL for the membership, or 0 for persistent entries */
+       int     gm_flags;
+#define KAUTH_GROUP_ISMEMBER   (1<<0)
+};
+
+TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
+static lck_mtx_t *kauth_groups_mtx;
+#define KAUTH_GROUPS_LOCK()    lck_mtx_lock(kauth_groups_mtx);
+#define KAUTH_GROUPS_UNLOCK()  lck_mtx_unlock(kauth_groups_mtx);
+#define KAUTH_GROUPS_CACHEMAX_DEFAULT 100      /* XXX default sizing? */
+static int kauth_groups_cachemax = KAUTH_GROUPS_CACHEMAX_DEFAULT;
+static int kauth_groups_count;
+
+static int     kauth_groups_expired(struct kauth_group_membership *gm);
+static void    kauth_groups_lru(struct kauth_group_membership *gm);
+static void    kauth_groups_updatecache(struct kauth_identity_extlookup *el);
+static void    kauth_groups_trimcache(int newsize);
+
+#endif /* CONFIG_EXT_RESOLVER */
 
 static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = KAUTH_CRED_PRIMES;
 static int     kauth_cred_primes_index = 0;
@@ -176,17 +271,14 @@ static int        kauth_cred_table_size = 0;
 TAILQ_HEAD(kauth_cred_entry_head, ucred);
 static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
 
-/* Weighted moving average for resolver response time */
-static struct kco_moving_average resolver_ma;
-
 #define KAUTH_CRED_HASH_DEBUG  0
 
 static int kauth_cred_add(kauth_cred_t new_cred);
-static void kauth_cred_remove(kauth_cred_t cred);
+static boolean_t kauth_cred_remove(kauth_cred_t cred);
 static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
 static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
 static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
-static void kauth_cred_unref_hashlocked(kauth_cred_t *credp);
+static boolean_t kauth_cred_unref_hashlocked(kauth_cred_t *credp);
 
 #if KAUTH_CRED_HASH_DEBUG
 static int     kauth_cred_count = 0;
@@ -194,6 +286,50 @@ static void kauth_cred_hash_print(void);
 static void kauth_cred_print(kauth_cred_t cred);
 #endif
 
+#if CONFIG_EXT_RESOLVER
+
+/*
+ *  __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
+ *
+ * Description:  Waits for the user space daemon to respond to the request
+ *               we made. Function declared non inline to be visible in 
+ *               stackshots and spindumps as well as debugging.
+ *
+ * Parameters:   workp                     Work queue entry.
+ *
+ * Returns:      0                         on Success.
+ *               EIO                       if Resolver is dead.
+ *               EINTR                     thread interrupted in msleep
+ *               EWOULDBLOCK               thread timed out in msleep
+ *               ERESTART                  returned by msleep.
+ *
+ */
+static __attribute__((noinline)) int 
+__KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
+       struct kauth_resolver_work  *workp)
+{
+       int error = 0;
+       struct timespec ts;
+       for (;;) {
+               /* we could compute a better timeout here */
+               ts.tv_sec = kauth_resolver_timeout;
+               ts.tv_nsec = 0;
+               error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
+               /* request has been completed? */
+               if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
+                       break;
+               /* woken because the resolver has died? */
+               if (kauth_resolver_identity == 0) {
+                       error = EIO;
+                       break;
+               }
+               /* an error? */
+               if (error != 0)
+                       break;
+       }
+       return error;
+}
+
 
 /*
  * kauth_resolver_init
@@ -204,7 +340,7 @@ static void kauth_cred_print(kauth_cred_t cred);
  *
  * Returns:    (void)
  *
- * Notes:      Intialize the credential identity resolver for use; the
+ * Notes:      Initialize the credential identity resolver for use; the
  *             credential identity resolver is the KPI used by the user
  *             space credential identity resolver daemon to communicate
  *             with the kernel via the identitysvc() system call..
@@ -230,11 +366,6 @@ kauth_resolver_init(void)
        TAILQ_INIT(&kauth_resolver_done);
        kauth_resolver_sequence = 31337;
        kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
-
-       /*
-        * 110% of average response time is "too long" and should be reported
-        */
-       kco_ma_init(&resolver_ma, 110, KCO_MA_F_WMA);
 }
 
 
@@ -246,26 +377,41 @@ kauth_resolver_init(void)
  *
  * Parameters: lkp                             A pointer to an external
  *                                             lookup request
+ *             extend_data                     extended data for kr_extend
  *
  * Returns:    0                               Success
  *             EWOULDBLOCK                     No resolver registered
  *             EINTR                           Operation interrupted (e.g. by
  *                                             a signal)
  *             ENOMEM                          Could not allocate work item
+ *     copyinstr:EFAULT                        Bad message from user space
  *     workp->kr_result:???                    An error from the user space
  *                                             daemon (includes ENOENT!)
  *
+ * Implicit returns:
+ *             *lkp                            Modified
+ *
  * Notes:      Allocate a work queue entry, submit the work and wait for
  *             the operation to either complete or time out.  Outstanding
  *             operations may also be cancelled.
+ *
+ *             Submission is by means of placing the item on a work queue
+ *             which is serviced by an external resolver thread calling
+ *             into the kernel.  The caller then sleeps until timeout,
+ *             cancellation, or an external resolver thread calls in with
+ *             a result message to kauth_resolver_complete().  All of these
+ *             events wake the caller back up.
+ *
+ *             This code is called from either kauth_cred_ismember_gid()
+ *             for a group membership request, or it is called from
+ *             kauth_cred_cache_lookup() when we get a cache miss.
  */
 static int
-kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
+kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data)
 {
        struct kauth_resolver_work *workp, *killp;
        struct timespec ts;
        int     error, shouldfree;
-       uint64_t        duration;
        
        /* no point actually blocking if the resolver isn't up yet */
        if (kauth_resolver_identity == 0) {
@@ -294,6 +440,7 @@ kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
                return(ENOMEM);
 
        workp->kr_work = *lkp;
+       workp->kr_extend = extend_data;
        workp->kr_refs = 1;
        workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
        workp->kr_result = 0;
@@ -307,80 +454,60 @@ kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
        workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
 
        /*
-        * XXX As an optimisation, we could check the queue for identical
-        * XXX items and coalesce them
+        * XXX We *MUST NOT* attempt to coalesce identical work items due to
+        * XXX the inability to ensure order of update of the request item
+        * XXX extended data vs. the wakeup; instead, we let whoever is waiting
+        * XXX for each item repeat the update when they wake up.
         */
        TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
 
-       wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
-       for (;;) {
-               /* we could compute a better timeout here */
-               ts.tv_sec = kauth_resolver_timeout;
-               ts.tv_nsec = 0;
-               error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
-               /* request has been completed? */
-               if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
-                       break;
-               /* woken because the resolver has died? */
-               if (kauth_resolver_identity == 0) {
-                       error = EIO;
-                       break;
-               }
-               /* an error? */
-               if (error != 0)
-                       break;
-       }
-
        /*
-        * Update the moving average of how long it took; if it took longer
-        * than the time threshold, then we complain about it being slow.
+        * Wake up an external resolver thread to deal with the new work; one
+        * may not be available, and if not, then the request will be grabbed
+        * when a resolver thread comes back into the kernel to request new
+        * work.
         */
-       duration = mach_absolute_time() - workp->kr_subtime;
-       if (kco_ma_addsample(&resolver_ma, duration)) {
-               uint64_t        average;
-               uint64_t        old_average;
-               int32_t         threshold;
-               int             count;
-
-               /* If we can't get information, don't log anything */
-               if (kco_ma_info(&resolver_ma, KCO_MA_F_WMA, &average, &old_average, &threshold, &count)) {
-                       char pname[MAXCOMLEN+1] = "(NULL)";
-                       proc_name(kauth_resolver_identity, pname, sizeof(pname));
-                       // <rdar://6276265>  printf("kauth_resolver_submit: External resolver pid %d (name %s) response time %lld, average %lld new %lld threshold %d%% actual %d%% count %d\n", kauth_resolver_identity, pname, duration, old_average, average, threshold, (int)((duration * 100) / old_average), count);
-               }
-       }
+       wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
+       error = __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp);
 
        /* if the request was processed, copy the result */
        if (error == 0)
                *lkp = workp->kr_work;
        
-       /*
-        * If the request timed out and was never collected, the resolver
-        * is dead and probably not coming back anytime soon.  In this
-        * case we revert to no-resolver behaviour, and punt all the other
-        * sleeping requests to clear the backlog.
-        */
-       if ((error == EWOULDBLOCK) && (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED)) {
-               KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
+       if (error == EWOULDBLOCK) {
+               if ((kauth_resolver_timeout_cnt++ % KAUTH_COMPLAINT_INTERVAL) == 0) {
+                        printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
+                                kauth_resolver_timeout_cnt, kauth_resolver_timeout);
+                }
+                
+               if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
+                       /*
+                        * If the request timed out and was never collected, the resolver
+                        * is dead and probably not coming back anytime soon.  In this
+                        * case we revert to no-resolver behaviour, and punt all the other
+                        * sleeping requests to clear the backlog.
+                        */
+                        KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
+
+                        /*
+                        * Make the current resolver non-authoritative, and mark it as
+                        * no longer registered to prevent kauth_cred_ismember_gid()
+                        * enqueueing more work until a new one is registered.  This
+                        * mitigates the damage a crashing resolver may inflict.
+                        */
+                        kauth_resolver_identity = 0;
+                        kauth_resolver_registered = 0;
+
+                        /* kill all the other requestes that are waiting as well */
+                        TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
+                                wakeup(killp);
+                        TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
+                                wakeup(killp);
+                        /* Cause all waiting-for-work threads to return EIO */
+                        wakeup((caddr_t)&kauth_resolver_unsubmitted);
+                }
+        }
 
-               /*
-                * Make the current resolver non-authoritative, and mark it as
-                * no longer registered to prevent kauth_cred_ismember_gid()
-                * enqueueing more work until a new one is registered.  This
-                * mitigates the damage a crashing resolver may inflict.
-                */
-               kauth_resolver_identity = 0;
-               kauth_resolver_registered = 0;
-
-               /* kill all the other requestes that are waiting as well */
-               TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
-                   wakeup(killp);
-               TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
-                   wakeup(killp);
-               /* Cause all waiting-for-work threads to return EIO */
-               wakeup((caddr_t)&kauth_resolver_unsubmitted);
-       }
-       
        /*
         * drop our reference on the work item, and note whether we should
         * free it or not
@@ -401,15 +528,19 @@ kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
                /* someone else still has a reference on this request */
                shouldfree = 0;
        }
+
        /* collect request result */
-       if (error == 0)
+       if (error == 0) {
                error = workp->kr_result;
+       }
        KAUTH_RESOLVER_UNLOCK();
+
        /*
         * If we dropped the last reference, free the request.
         */
-       if (shouldfree)
+       if (shouldfree) {
                FREE(workp, M_KAUTH);
+       }
 
        KAUTH_DEBUG("RESOLVER - returning %d", error);
        return(error);
@@ -445,6 +576,7 @@ identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int3
        int opcode = uap->opcode;
        user_addr_t message = uap->message;
        struct kauth_resolver_work *workp;
+       struct kauth_cache_sizes sz_arg;
        int error;
        pid_t new_id;
 
@@ -473,12 +605,13 @@ identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int3
                         * Allow user space resolver to override the
                         * external resolution timeout
                         */
-                       if (message >= 30 && message <= 10000) {
+                       if (message > 30 && message < 10000) {
                                kauth_resolver_timeout = message;
                                KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message);
                        }
                        kauth_resolver_identity = new_id;
                        kauth_resolver_registered = 1;
+                       kauth_identitysvc_has_registered = 1;
                        wakeup(&kauth_resolver_unsubmitted);
                }
                KAUTH_RESOLVER_UNLOCK();
@@ -486,14 +619,58 @@ identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int3
        }
 
        /*
-        * Beyond this point, we must be the resolver process.
+        * Beyond this point, we must be the resolver process. We verify this
+        * by confirming the resolver credential and pid.
         */
-       if (current_proc()->p_pid != kauth_resolver_identity) {
+       if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid != kauth_resolver_identity)) {
                KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
                return(EPERM);
        }
+       
+       if (opcode == KAUTH_GET_CACHE_SIZES) {
+               KAUTH_IDENTITY_LOCK();
+               sz_arg.kcs_id_size = kauth_identity_cachemax;
+               KAUTH_IDENTITY_UNLOCK();
+               
+               KAUTH_GROUPS_LOCK();
+               sz_arg.kcs_group_size = kauth_groups_cachemax;
+               KAUTH_GROUPS_UNLOCK();
 
-       if (opcode == KAUTH_EXTLOOKUP_DEREGISTER) {
+               if ((error = copyout(&sz_arg, uap->message, sizeof (sz_arg))) != 0) {
+                       return (error);
+               }
+               
+               return (0);
+       } else if (opcode == KAUTH_SET_CACHE_SIZES) {
+               if ((error = copyin(uap->message, &sz_arg, sizeof (sz_arg))) != 0) {
+                       return (error);
+               }
+               
+               if ((sz_arg.kcs_group_size > KAUTH_CACHES_MAX_SIZE) ||
+                   (sz_arg.kcs_id_size > KAUTH_CACHES_MAX_SIZE)) {
+                       return (EINVAL);
+               }
+               
+               KAUTH_IDENTITY_LOCK();
+               kauth_identity_cachemax = sz_arg.kcs_id_size;
+               kauth_identity_trimcache(kauth_identity_cachemax);
+               KAUTH_IDENTITY_UNLOCK();
+               
+               KAUTH_GROUPS_LOCK();
+               kauth_groups_cachemax = sz_arg.kcs_group_size;
+               kauth_groups_trimcache(kauth_groups_cachemax);
+               KAUTH_GROUPS_UNLOCK();
+               
+               return (0);
+       } else if (opcode == KAUTH_CLEAR_CACHES) {
+               KAUTH_IDENTITY_LOCK();
+               kauth_identity_trimcache(0);
+               KAUTH_IDENTITY_UNLOCK();
+               
+               KAUTH_GROUPS_LOCK();
+               kauth_groups_trimcache(0);
+               KAUTH_GROUPS_UNLOCK();
+       } else if (opcode == KAUTH_EXTLOOKUP_DEREGISTER) {
                /*
                 * Terminate outstanding requests; without an authoritative
                 * resolver, we are now back on our own authority.
@@ -588,7 +765,7 @@ kauth_resolver_getwork_continue(int result)
 
        thread = current_thread();
        ut = get_bsdthread_info(thread);
-       message = ut->uu_kauth.message;
+       message = ut->uu_kevent.uu_kauth.message;
        return(kauth_resolver_getwork2(message));
 }
 
@@ -606,7 +783,7 @@ kauth_resolver_getwork_continue(int result)
  *             EFAULT                          Bad user space message address
  *
  * Notes:      This common function exists to permit the use of continuations
- *             in the identity resoultion process.  This frees up the stack
+ *             in the identity resolution process.  This frees up the stack
  *             while we are waiting for the user space resolver to complete
  *             a request.  This is specifically used so that our per thread
  *             cost can be small, and we will therefore be willing to run a
@@ -625,14 +802,57 @@ kauth_resolver_getwork2(user_addr_t message)
         */
        workp = TAILQ_FIRST(&kauth_resolver_unsubmitted);
 
-       if ((error = copyout(&workp->kr_work, message, sizeof(workp->kr_work))) != 0) {
+       /*
+        * Copy out the external lookup structure for the request, not
+        * including the el_extend field, which contains the address of the
+        * external buffer provided by the external resolver into which we
+        * copy the extension request information.
+        */
+       /* BEFORE FIELD */
+       if ((error = copyout(&workp->kr_work, message, offsetof(struct kauth_identity_extlookup, el_extend))) != 0) {
                KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
                goto out;
        }
+       /* AFTER FIELD */
+       if ((error = copyout(&workp->kr_work.el_info_reserved_1,
+                       message + offsetof(struct kauth_identity_extlookup, el_info_reserved_1),
+               sizeof(struct kauth_identity_extlookup) - offsetof(struct kauth_identity_extlookup, el_info_reserved_1))) != 0) {
+               KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
+               goto out;
+       }
+
+       /*
+        * Handle extended requests here; if we have a request of a type where
+        * the kernel wants a translation of extended information, then we need
+        * to copy it out into the extended buffer, assuming the buffer is
+        * valid; we only attempt to get the buffer address if we have request
+        * data to copy into it.
+        */
+
+       /*
+        * translate a user@domain string into a uid/gid/whatever
+        */
+       if (workp->kr_work.el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) {
+               uint64_t uaddr;
+
+               error = copyin(message + offsetof(struct kauth_identity_extlookup, el_extend), &uaddr, sizeof(uaddr));
+               if (!error) {
+                       size_t actual;  /* not used */
+                       /*
+                        * Use copyoutstr() to reduce the copy size; we let
+                        * this catch a NULL uaddr because we shouldn't be
+                        * asking in that case anyway.
+                        */
+                       error = copyoutstr(CAST_DOWN(void *,workp->kr_extend), uaddr, MAXPATHLEN, &actual);
+               }
+               if (error) {
+                       KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
+                       goto out;
+               }
+       }
        TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
        workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
        workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
-       workp->kr_subtime = mach_absolute_time();
        TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
 
 out:
@@ -658,7 +878,7 @@ out:
  *             identity resolution daemon makes a request for work.  This
  *             permits a large number of threads to be used by the daemon,
  *             without using a lot of wired kernel memory when there are no
- *             acctual request outstanding.
+ *             actual request outstanding.
  */
 static int
 kauth_resolver_getwork(user_addr_t message)
@@ -672,7 +892,7 @@ kauth_resolver_getwork(user_addr_t message)
                thread_t thread = current_thread();
                struct uthread *ut = get_bsdthread_info(thread);
 
-               ut->uu_kauth.message = message;
+               ut->uu_kevent.uu_kauth.message = message;
                error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue);
                KAUTH_RESOLVER_UNLOCK();
                /*
@@ -704,8 +924,12 @@ kauth_resolver_complete(user_addr_t message)
        struct kauth_identity_extlookup extl;
        struct kauth_resolver_work *workp;
        struct kauth_resolver_work *killp;
-       int error, result;
+       int error, result, request_flags;
 
+       /*
+        * Copy in the mesage, including the extension field, since we are
+        * copying into a local variable.
+        */
        if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
                KAUTH_DEBUG("RESOLVER - error getting completed work\n");
                return(error);
@@ -771,22 +995,78 @@ kauth_resolver_complete(user_addr_t message)
        }
 
        /*
-        * In the case of a fatal error, we assume that the resolver will restart
-        * quickly and re-collect all of the outstanding requests.  Thus, we don't
-        * complete the request which returned the fatal error status.
+        * In the case of a fatal error, we assume that the resolver will
+        * restart quickly and re-collect all of the outstanding requests.
+        * Thus, we don't complete the request which returned the fatal
+        * error status.
         */
        if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
                /* scan our list for this request */
                TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
                        /* found it? */
                        if (workp->kr_seqno == extl.el_seqno) {
-                               /* copy result */
-                               workp->kr_work = extl;
-                               /* move onto completed list and wake up requester(s) */
+                               /*
+                                * Take a snapshot of the original request flags.
+                                */
+                               request_flags = workp->kr_work.el_flags;
+
+                               /*
+                                * Get the request of the submitted queue so
+                                * that it is not cleaned up out from under
+                                * us by a timeout.
+                                */
                                TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
                                workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
                                workp->kr_flags |= KAUTH_REQUEST_DONE;
                                workp->kr_result = result;
+
+                               /* Copy the result message to the work item. */
+                               memcpy(&workp->kr_work, &extl, sizeof(struct kauth_identity_extlookup));
+
+                               /*
+                                * Check if we have a result in the extension
+                                * field; if we do, then we need to separately
+                                * copy the data from the message el_extend
+                                * into the request buffer that's in the work
+                                * item.  We have to do it here because we do
+                                * not want to wake up the waiter until the
+                                * data is in their buffer, and because the
+                                * actual request response may be destroyed
+                                * by the time the requester wakes up, and they
+                                * do not have access to the user space buffer
+                                * address.
+                                *
+                                * It is safe to drop and reacquire the lock
+                                * here because we've already removed the item
+                                * from the submission queue, but have not yet
+                                * moved it to the completion queue.  Note that
+                                * near simultaneous requests may result in
+                                * duplication of requests for items in this
+                                * window. This should not be a performance
+                                * issue and is easily detectable by comparing
+                                * time to live on last response vs. time of
+                                * next request in the resolver logs.
+                                *
+                                * A malicious/faulty resolver could overwrite
+                                * part of a user's address space if they return
+                                * flags that mismatch the original request's flags.
+                                */
+                               if ((extl.el_flags & request_flags) & (KAUTH_EXTLOOKUP_VALID_PWNAM|KAUTH_EXTLOOKUP_VALID_GRNAM)) {
+                                       size_t actual;  /* notused */
+
+                                       KAUTH_RESOLVER_UNLOCK();
+                                       error = copyinstr(extl.el_extend, CAST_DOWN(void *, workp->kr_extend), MAXPATHLEN, &actual);
+                                       KAUTH_RESOLVER_LOCK();
+                               } else if (extl.el_flags &  (KAUTH_EXTLOOKUP_VALID_PWNAM|KAUTH_EXTLOOKUP_VALID_GRNAM)) {
+                                       error = EFAULT;
+                                       KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
+                                                       extl.el_flags, request_flags);
+                               }
+
+                               /*
+                                * Move the completed work item to the
+                                * completion queue and wake up requester(s)
+                                */
                                TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
                                wakeup(workp);
                                break;
@@ -801,55 +1081,22 @@ kauth_resolver_complete(user_addr_t message)
        
        return(error);
 }
+#endif /* CONFIG_EXT_RESOLVER */
 
 
 /*
  * Identity cache.
  */
 
-struct kauth_identity {
-       TAILQ_ENTRY(kauth_identity) ki_link;
-       int     ki_valid;
 #define        KI_VALID_UID    (1<<0)          /* UID and GID are mutually exclusive */
 #define KI_VALID_GID   (1<<1)
 #define KI_VALID_GUID  (1<<2)
 #define KI_VALID_NTSID (1<<3)
-       uid_t   ki_uid;
-       gid_t   ki_gid;
-       guid_t  ki_guid;
-       ntsid_t ki_ntsid;
-       /*
-        * Expiry times are the earliest time at which we will disregard the cached state and go to
-        * userland.  Before then if the valid bit is set, we will return the cached value.  If it's
-        * not set, we will not go to userland to resolve, just assume that there is no answer
-        * available.
-        */
-       time_t  ki_guid_expiry;
-       time_t  ki_ntsid_expiry;
-};
-
-static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
-#define KAUTH_IDENTITY_CACHEMAX                100     /* XXX sizing? */
-static int kauth_identity_count;
-
-static lck_mtx_t *kauth_identity_mtx;
-#define KAUTH_IDENTITY_LOCK()  lck_mtx_lock(kauth_identity_mtx);
-#define KAUTH_IDENTITY_UNLOCK()        lck_mtx_unlock(kauth_identity_mtx);
-
-
-static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
-    ntsid_t *ntsidp, time_t ntsid_expiry);
-static void    kauth_identity_register_and_free(struct kauth_identity *kip);
-static void    kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
-static void    kauth_identity_lru(struct kauth_identity *kip);
-static int     kauth_identity_guid_expired(struct kauth_identity *kip);
-static int     kauth_identity_ntsid_expired(struct kauth_identity *kip);
-static int     kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
-static int     kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
-static int     kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
-static int     kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
-
+#define KI_VALID_PWNAM (1<<4)  /* Used for translation */
+#define KI_VALID_GRNAM (1<<5)  /* Used for translation */
+#define KI_VALID_GROUPS (1<<6)
 
+#if CONFIG_EXT_RESOLVER
 /*
  * kauth_identity_init
  *
@@ -859,7 +1106,7 @@ static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
  *
  * Returns:    (void)
  *
- * Notes:      Intialize the credential identity resolver for use; the
+ * Notes:      Initialize the credential identity resolver for use; the
  *             credential identity resolver is the KPI used to communicate
  *             with a user space credential identity resolver daemon.
  *
@@ -883,16 +1130,18 @@ kauth_identity_init(void)
  * Parameters: uid
  *
  * Returns:    NULL                            Insufficient memory to satisfy
- *                                             the request
- *             !NULL                           A pointer to the applocated
+ *                                             the request or bad parameters
+ *             !NULL                           A pointer to the allocated
  *                                             structure, filled in
  *
  * Notes:      It is illegal to translate between UID and GID; any given UUID
- *             or NTSID can oly refer to an NTSIDE or UUID (respectively),
+ *             or NTSID can only refer to an NTSID or UUID (respectively),
  *             and *either* a UID *or* a GID, but not both.
  */
 static struct kauth_identity *
-kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, ntsid_t *ntsidp, time_t ntsid_expiry)
+kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
+       ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry,
+       const char *name, int nametype)
 {
        struct kauth_identity *kip;
        
@@ -909,6 +1158,24 @@ kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, nt
                        kip->ki_uid = uid;
                        kip->ki_valid = KI_VALID_UID;
                }
+               if (supgrpcnt) {
+                       /*
+                        * A malicious/faulty resolver could return bad values
+                        */
+                       assert(supgrpcnt >= 0);
+                       assert(supgrpcnt <= NGROUPS);
+                       assert(supgrps != NULL);
+
+                       if ((supgrpcnt < 0) || (supgrpcnt > NGROUPS) || (supgrps == NULL)) {
+                               return NULL;
+                       }
+                       if (kip->ki_valid & KI_VALID_GID)
+                               panic("can't allocate kauth identity with both gid and supplementary groups");
+                       kip->ki_supgrpcnt = supgrpcnt;
+                       memcpy(kip->ki_supgrps, supgrps, sizeof(supgrps[0]) * supgrpcnt);
+                       kip->ki_valid |= KI_VALID_GROUPS;
+               }
+               kip->ki_groups_expiry = groups_expiry;
                if (guidp != NULL) {
                        kip->ki_guid = *guidp;
                        kip->ki_valid |= KI_VALID_GUID;
@@ -919,6 +1186,10 @@ kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, nt
                        kip->ki_valid |= KI_VALID_NTSID;
                }
                kip->ki_ntsid_expiry = ntsid_expiry;
+               if (name != NULL) {
+                       kip->ki_name = name;
+                       kip->ki_valid |= nametype;
+               }
        }
        return(kip);
 }
@@ -928,7 +1199,7 @@ kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, nt
  * kauth_identity_register_and_free
  *
  * Description:        Register an association between identity tokens.  The passed
- *             'kip' is freed by this function.
+ *             'kip' is consumed by this function.
  *
  * Parameters: kip                             Pointer to kauth_identity
  *                                             structure to register
@@ -975,22 +1246,38 @@ kauth_identity_register_and_free(struct kauth_identity *kip)
                        ip->ki_valid |= KI_VALID_NTSID;
                }
                ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
-               /* and discard the incoming identity */
-               FREE(kip, M_KAUTH);
-               ip = NULL;
+               /* a valid ki_name field overwrites the previous name field */
+               if (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)) {
+                       /* if there's an old one, discard it */
+                       const char *oname = NULL;
+                       if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))
+                               oname = ip->ki_name;
+                       ip->ki_name = kip->ki_name;
+                       kip->ki_name = oname;
+               }
+               /* and discard the incoming entry */
+               ip = kip;
        } else {
-               /* don't have any information on this identity, so just add it */
+               /*
+                * if we don't have any information on this identity, add it;
+                * if it pushes us over our limit, discard the oldest one.
+                */
                TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
-               if (++kauth_identity_count > KAUTH_IDENTITY_CACHEMAX) {
+               if (++kauth_identity_count > kauth_identity_cachemax) {
                        ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
                        TAILQ_REMOVE(&kauth_identities, ip, ki_link);
                        kauth_identity_count--;
                }
        }
        KAUTH_IDENTITY_UNLOCK();
-       /* have to drop lock before freeing expired entry */
-       if (ip != NULL)
+       /* have to drop lock before freeing expired entry (it may be in use) */
+       if (ip != NULL) {
+               /* if the ki_name field is used, clear it first */
+               if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))
+                       vfs_removename(ip->ki_name);
+               /* free the expired entry */
                FREE(ip, M_KAUTH);
+       }
 }
 
 
@@ -998,25 +1285,51 @@ kauth_identity_register_and_free(struct kauth_identity *kip)
  * kauth_identity_updatecache
  *
  * Description:        Given a lookup result, add any associations that we don't
- *             currently have.
+ *             currently have; replace ones which have changed.
  *
  * Parameters: elp                             External lookup result from
  *                                             user space daemon to kernel
  *             rkip                            pointer to returned kauth
  *                                             identity, or NULL
+ *             extend_data                     Extended data (can vary)
  *
  * Returns:    (void)
  *
  * Implicit returns:
  *             *rkip                           Modified (if non-NULL)
+ *
+ * Notes:      For extended information requests, this code relies on the fact
+ *             that elp->el_flags is never used as an rvalue, and is only
+ *             ever bit-tested for valid lookup information we are willing
+ *             to cache.
+ *
+ * XXX:                We may have to do the same in the case that extended data was
+ *             passed out to user space to ensure that the request string
+ *             gets cached; we may also be able to use the rkip as an
+ *             input to avoid this.  The jury is still out.
+ *
+ * XXX:                This codes performance could be improved for multiple valid
+ *             results by combining the loop iteration in a single loop.
  */
 static void
-kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip)
+kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip, uint64_t extend_data)
 {
        struct timeval tv;
        struct kauth_identity *kip;
+       const char *speculative_name = NULL;
 
        microuptime(&tv);
+
+       /*
+        * If there is extended data, and that data represents a name rather
+        * than something else, speculatively create an entry for it in the
+        * string cache.  We do this to avoid holding the KAUTH_IDENTITY_LOCK
+        * over the allocation later.
+        */
+       if (elp->el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) {
+               const char *tmp = CAST_DOWN(const char *,extend_data);
+               speculative_name = vfs_addname(tmp, strnlen(tmp, MAXPATHLEN - 1), 0, 0);
+       }
        
        /* user identity? */
        if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
@@ -1024,16 +1337,36 @@ kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_id
                TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
                        /* matching record */
                        if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
+                               if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) {
+                                       assert(elp->el_sup_grp_cnt <= NGROUPS);
+                                       kip->ki_supgrpcnt = elp->el_sup_grp_cnt;
+                                       memcpy(kip->ki_supgrps, elp->el_sup_groups, sizeof(elp->el_sup_groups[0]) * kip->ki_supgrpcnt);
+                                       kip->ki_valid |= KI_VALID_GROUPS;
+                                       kip->ki_groups_expiry = (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0;
+                               }
                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
                                        kip->ki_guid = elp->el_uguid;
                                        kip->ki_valid |= KI_VALID_GUID;
                                }
-                               kip->ki_guid_expiry = tv.tv_sec + elp->el_uguid_valid;
+                               kip->ki_guid_expiry = (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0;
                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
                                        kip->ki_ntsid = elp->el_usid;
                                        kip->ki_valid |= KI_VALID_NTSID;
                                }
-                               kip->ki_ntsid_expiry = tv.tv_sec + elp->el_usid_valid;
+                               kip->ki_ntsid_expiry = (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0;
+                               if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) {
+                                       const char *oname = kip->ki_name;
+                                       kip->ki_name = speculative_name;
+                                       speculative_name = NULL;
+                                       kip->ki_valid |= KI_VALID_PWNAM;
+                                       if (oname) {
+                                               /*
+                                                * free oname (if any) outside
+                                                * the lock
+                                                */
+                                               speculative_name = oname;
+                                       }
+                               }
                                kauth_identity_lru(kip);
                                if (rkip != NULL)
                                        *rkip = *kip;
@@ -1046,20 +1379,27 @@ kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_id
                if (kip == NULL) {
                        kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
-                           tv.tv_sec + elp->el_uguid_valid,
+                           (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0,
                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
-                           tv.tv_sec + elp->el_usid_valid);
+                           (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL,
+                           (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) ? speculative_name : NULL,
+                           KI_VALID_PWNAM);
                        if (kip != NULL) {
                                if (rkip != NULL)
                                        *rkip = *kip;
+                               if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM)
+                                       speculative_name = NULL;
                                KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
                                kauth_identity_register_and_free(kip);
                        }
                }
        }
 
-       /* group identity? */
-       if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID) {
+       /* group identity? (ignore, if we already processed it as a user) */
+       if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID && !(elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID)) {
                KAUTH_IDENTITY_LOCK();
                TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
                        /* matching record */
@@ -1068,12 +1408,25 @@ kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_id
                                        kip->ki_guid = elp->el_gguid;
                                        kip->ki_valid |= KI_VALID_GUID;
                                }
-                               kip->ki_guid_expiry = tv.tv_sec + elp->el_gguid_valid;
+                               kip->ki_guid_expiry = (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0;
                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
                                        kip->ki_ntsid = elp->el_gsid;
                                        kip->ki_valid |= KI_VALID_NTSID;
                                }
-                               kip->ki_ntsid_expiry = tv.tv_sec + elp->el_gsid_valid;
+                               kip->ki_ntsid_expiry = (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0;
+                               if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) {
+                                       const char *oname = kip->ki_name;
+                                       kip->ki_name = speculative_name;
+                                       speculative_name = NULL;
+                                       kip->ki_valid |= KI_VALID_GRNAM;
+                                       if (oname) {
+                                               /*
+                                                * free oname (if any) outside
+                                                * the lock
+                                                */
+                                               speculative_name = oname;
+                                       }
+                               }
                                kauth_identity_lru(kip);
                                if (rkip != NULL)
                                        *rkip = *kip;
@@ -1086,21 +1439,51 @@ kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_id
                if (kip == NULL) {
                        kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
-                           tv.tv_sec + elp->el_gguid_valid,
+                           (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0,
                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
-                           tv.tv_sec + elp->el_gsid_valid);
+                           (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL,
+                           (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0,
+                           (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) ? speculative_name : NULL,
+                           KI_VALID_GRNAM);
                        if (kip != NULL) {
                                if (rkip != NULL)
                                        *rkip = *kip;
+                               if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM)
+                                       speculative_name = NULL;
                                KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
                                kauth_identity_register_and_free(kip);
                        }
                }
        }
 
+       /* If we have a name reference to drop, drop it here */
+       if (speculative_name != NULL) {
+               vfs_removename(speculative_name);
+       }
 }
 
 
+/*
+ * Trim older entries from the identity cache.
+ *
+ * Must be called with the identity cache lock held.
+ */
+static void
+kauth_identity_trimcache(int newsize) {
+       struct kauth_identity           *kip;
+       
+       lck_mtx_assert(kauth_identity_mtx, LCK_MTX_ASSERT_OWNED);
+               
+       while (kauth_identity_count > newsize) {
+               kip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
+               TAILQ_REMOVE(&kauth_identities, kip, ki_link);
+               kauth_identity_count--;
+               FREE(kip, M_KAUTH);
+       }
+}
+
 /*
  * kauth_identity_lru
  *
@@ -1144,8 +1527,15 @@ kauth_identity_guid_expired(struct kauth_identity *kip)
 {
        struct timeval tv;
 
+       /*
+        * Expiration time of 0 means this entry is persistent.
+        */
+       if (kip->ki_guid_expiry == 0)
+               return (0);
+
        microuptime(&tv);
-       KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip->ki_guid_expiry, tv.tv_sec);
+       KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip->ki_guid_expiry, tv.tv_sec);
+
        return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
 }
 
@@ -1166,11 +1556,45 @@ kauth_identity_ntsid_expired(struct kauth_identity *kip)
 {
        struct timeval tv;
 
+       /*
+        * Expiration time of 0 means this entry is persistent.
+        */
+       if (kip->ki_ntsid_expiry == 0)
+               return (0);
+
        microuptime(&tv);
-       KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip->ki_ntsid_expiry, tv.tv_sec);
+       KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip->ki_ntsid_expiry, tv.tv_sec);
+
        return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
 }
 
+/*
+ * kauth_identity_groups_expired
+ *
+ * Description:        Handle lazy expiration of supplemental group translations.
+ *
+ * Parameters: kip                             kauth identity to check for
+ *                                             groups expiration
+ *
+ * Returns:    1                               Expired
+ *             0                               Not expired
+ */
+static int
+kauth_identity_groups_expired(struct kauth_identity *kip)
+{
+       struct timeval tv;
+
+       /*
+        * Expiration time of 0 means this entry is persistent.
+        */
+       if (kip->ki_groups_expiry == 0)
+               return (0);
+
+       microuptime(&tv);
+       KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip->ki_groups_expiry, tv.tv_sec);
+
+       return((kip->ki_groups_expiry <= tv.tv_sec) ? 1 : 0);
+}
 
 /*
  * kauth_identity_find_uid
@@ -1179,6 +1603,7 @@ kauth_identity_ntsid_expired(struct kauth_identity *kip)
  *
  * Parameters: uid                             UID to find
  *             kir                             Pointer to return area
+ *             getname                         Name buffer, if ki_name wanted
  *
  * Returns:    0                               Found
  *             ENOENT                          Not found
@@ -1187,7 +1612,7 @@ kauth_identity_ntsid_expired(struct kauth_identity *kip)
  *             *klr                            Modified, if found
  */
 static int
-kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
+kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname)
 {
        struct kauth_identity *kip;
 
@@ -1197,6 +1622,9 @@ kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
                        kauth_identity_lru(kip);
                        /* Copy via structure assignment */
                        *kir = *kip;
+                       /* If a name is wanted and one exists, copy it out */
+                       if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
+                               strlcpy(getname, kip->ki_name, MAXPATHLEN);
                        break;
                }
        }
@@ -1206,12 +1634,13 @@ kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
 
 
 /*
- * kauth_identity_find_uid
+ * kauth_identity_find_gid
  *
  * Description: Search for an entry by GID
  *
  * Parameters: gid                             GID to find
  *             kir                             Pointer to return area
+ *             getname                         Name buffer, if ki_name wanted
  *
  * Returns:    0                               Found
  *             ENOENT                          Not found
@@ -1220,7 +1649,7 @@ kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
  *             *klr                            Modified, if found
  */
 static int
-kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
+kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir, char *getname)
 {
        struct kauth_identity *kip;
 
@@ -1230,6 +1659,9 @@ kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
                        kauth_identity_lru(kip);
                        /* Copy via structure assignment */
                        *kir = *kip;
+                       /* If a name is wanted and one exists, copy it out */
+                       if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
+                               strlcpy(getname, kip->ki_name, MAXPATHLEN);
                        break;
                }
        }
@@ -1245,6 +1677,7 @@ kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
  *
  * Parameters: guidp                           Pointer to GUID to find
  *             kir                             Pointer to return area
+ *             getname                         Name buffer, if ki_name wanted
  *
  * Returns:    0                               Found
  *             ENOENT                          Not found
@@ -1256,13 +1689,49 @@ kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
  *             may elect to call out to userland to revalidate.
  */
 static int
-kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
+kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname)
 {
        struct kauth_identity *kip;
 
        KAUTH_IDENTITY_LOCK();
        TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
                if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
+                       kauth_identity_lru(kip);
+                       /* Copy via structure assignment */
+                       *kir = *kip;
+                       /* If a name is wanted and one exists, copy it out */
+                       if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
+                               strlcpy(getname, kip->ki_name, MAXPATHLEN);
+                       break;
+               }
+       }
+       KAUTH_IDENTITY_UNLOCK();
+       return((kip == NULL) ? ENOENT : 0);
+}
+
+/*
+ * kauth_identity_find_nam
+ *
+ * Description:        Search for an entry by name
+ *
+ * Parameters: name                            Pointer to name to find
+ *             valid                           KI_VALID_PWNAM or KI_VALID_GRNAM
+ *             kir                             Pointer to return area
+ *
+ * Returns:    0                               Found
+ *             ENOENT                          Not found
+ *
+ * Implicit returns:
+ *             *klr                            Modified, if found
+ */
+static int
+kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir)
+{
+       struct kauth_identity *kip;
+
+       KAUTH_IDENTITY_LOCK();
+       TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
+               if ((kip->ki_valid & valid) && !strcmp(name, kip->ki_name)) {
                        kauth_identity_lru(kip);
                        /* Copy via structure assignment */
                        *kir = *kip;
@@ -1281,6 +1750,7 @@ kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
  *
  * Parameters: ntsid                           Pointer to NTSID to find
  *             kir                             Pointer to return area
+ *             getname                         Name buffer, if ki_name wanted
  *
  * Returns:    0                               Found
  *             ENOENT                          Not found
@@ -1292,7 +1762,7 @@ kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
  *             may elect to call out to userland to revalidate.
  */
 static int
-kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
+kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname)
 {
        struct kauth_identity *kip;
 
@@ -1302,12 +1772,16 @@ kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
                        kauth_identity_lru(kip);
                        /* Copy via structure assignment */
                        *kir = *kip;
+                       /* If a name is wanted and one exists, copy it out */
+                       if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
+                               strlcpy(getname, kip->ki_name, MAXPATHLEN);
                        break;
                }
        }
        KAUTH_IDENTITY_UNLOCK();
        return((kip == NULL) ? ENOENT : 0);
 }
+#endif /* CONFIG_EXT_RESOLVER */
 
 
 /*
@@ -1324,7 +1798,7 @@ guid_t kauth_null_guid;
  * Parameters: guid1                           Pointer to first GUID
  *             guid2                           Pointer to second GUID
  *
- * Returns:    0                               If GUIDs are inequal
+ * Returns:    0                               If GUIDs are unequal
  *             !0                              If GUIDs are equal
  */
 int
@@ -1341,7 +1815,7 @@ kauth_guid_equal(guid_t *guid1, guid_t *guid2)
  *
  * Parameters: guid                            Pointer to GUID to check
  *
- * Returns:    KAUTH_WKG_NOT                   Not a wel known GUID
+ * Returns:    KAUTH_WKG_NOT                   Not a well known GUID
  *             KAUTH_WKG_EVERYBODY             "Everybody"
  *             KAUTH_WKG_NOBODY                "Nobody"
  *             KAUTH_WKG_OWNER                 "Other"
@@ -1351,7 +1825,7 @@ int
 kauth_wellknown_guid(guid_t *guid)
 {
        static char     fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
-       int             code;
+       uint32_t                code;
        /*
         * All WKGs begin with the same 12 bytes.
         */
@@ -1359,7 +1833,7 @@ kauth_wellknown_guid(guid_t *guid)
                /*
                 * The final 4 bytes are our code (in network byte order).
                 */
-               code = OSSwapHostToBigInt32(*(u_int32_t *)&guid->g_guid[12]);
+               code = OSSwapHostToBigInt32(*(uint32_t *)&guid->g_guid[12]);
                switch(code) {
                case 0x0000000c:
                        return(KAUTH_WKG_EVERYBODY);
@@ -1380,10 +1854,10 @@ kauth_wellknown_guid(guid_t *guid)
  *
  * Description:        Determine the equality of two NTSIDs (NT Security Identifiers) 
  *
- * Paramters:  sid1                            Pointer to first NTSID
+ * Parameters: sid1                            Pointer to first NTSID
  *             sid2                            Pointer to second NTSID
  *
- * Returns:    0                               If GUIDs are inequal
+ * Returns:    0                               If GUIDs are unequal
  *             !0                              If GUIDs are equal
  */
 int
@@ -1411,7 +1885,6 @@ kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
  * be done using it.
  */
 
-static int     kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
 
 
 /*
@@ -1445,16 +1918,17 @@ kauth_cred_change_egid(kauth_cred_t cred, gid_t new_egid)
 #if radar_4600026
        int     is_member;
 #endif /* radar_4600026 */
-       gid_t   old_egid = cred->cr_groups[0];
+       gid_t   old_egid = kauth_cred_getgid(cred);
+       posix_cred_t pcred = posix_cred_get(cred);
 
        /* Ignoring the first entry, scan for a match for the new egid */
-       for (i = 1; i < cred->cr_ngroups; i++) {
+       for (i = 1; i < pcred->cr_ngroups; i++) {
                /*
                 * If we find a match, swap them so we don't lose overall
                 * group information
                 */
-               if (cred->cr_groups[i] == new_egid) {
-                       cred->cr_groups[i] = old_egid;
+               if (pcred->cr_groups[i] == new_egid) {
+                       pcred->cr_groups[i] = old_egid;
                        DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
                        displaced = 0;
                        break;
@@ -1480,7 +1954,7 @@ conservative approach (i.e. less likely to cause things to break).
         *
         * NB:  This is typically a cold code path.
         */
-       if (displaced && !(cred->cr_flags & CRF_NOMEMBERD) &&
+       if (displaced && !(pcred->cr_flags & CRF_NOMEMBERD) &&
            kauth_cred_ismember_gid(cred, new_egid, &is_member) == 0 &&
            is_member) {
                displaced = 0;
@@ -1489,7 +1963,7 @@ conservative approach (i.e. less likely to cause things to break).
 #endif /* radar_4600026 */
 
        /* set the new EGID into the old spot */
-       cred->cr_groups[0] = new_egid;
+       pcred->cr_groups[0] = new_egid;
 
        return (displaced);
 }
@@ -1508,7 +1982,41 @@ uid_t
 kauth_cred_getuid(kauth_cred_t cred)
 {
        NULLCRED_CHECK(cred);
-       return(cred->cr_uid);
+       return(posix_cred_get(cred)->cr_uid);
+}
+
+
+/*
+ * kauth_cred_getruid
+ *
+ * Description:        Fetch RUID from credential
+ *
+ * Parameters: cred                            Credential to examine
+ *
+ * Returns:    (uid_t)                         RUID associated with credential
+ */
+uid_t
+kauth_cred_getruid(kauth_cred_t cred)
+{
+       NULLCRED_CHECK(cred);
+       return(posix_cred_get(cred)->cr_ruid);
+}
+
+
+/*
+ * kauth_cred_getsvuid
+ *
+ * Description:        Fetch SVUID from credential
+ *
+ * Parameters: cred                            Credential to examine
+ *
+ * Returns:    (uid_t)                         SVUID associated with credential
+ */
+uid_t
+kauth_cred_getsvuid(kauth_cred_t cred)
+{
+       NULLCRED_CHECK(cred);
+       return(posix_cred_get(cred)->cr_svuid);
 }
 
 
@@ -1521,11 +2029,196 @@ kauth_cred_getuid(kauth_cred_t cred)
  *
  * Returns:    (gid_t)                         GID associated with credential
  */
-uid_t
+gid_t
 kauth_cred_getgid(kauth_cred_t cred)
 {
        NULLCRED_CHECK(cred);
-       return(cred->cr_gid);
+       return(posix_cred_get(cred)->cr_gid);
+}
+
+
+/*
+ * kauth_cred_getrgid
+ *
+ * Description:        Fetch RGID from credential
+ *
+ * Parameters: cred                            Credential to examine
+ *
+ * Returns:    (gid_t)                         RGID associated with credential
+ */
+gid_t
+kauth_cred_getrgid(kauth_cred_t cred)
+{
+       NULLCRED_CHECK(cred);
+       return(posix_cred_get(cred)->cr_rgid);
+}
+
+
+/*
+ * kauth_cred_getsvgid
+ *
+ * Description:        Fetch SVGID from credential
+ *
+ * Parameters: cred                            Credential to examine
+ *
+ * Returns:    (gid_t)                         SVGID associated with credential
+ */
+gid_t
+kauth_cred_getsvgid(kauth_cred_t cred)
+{
+       NULLCRED_CHECK(cred);
+       return(posix_cred_get(cred)->cr_svgid);
+}
+
+
+static int     kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
+
+#if CONFIG_EXT_RESOLVER == 0
+/*
+ * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
+ */
+static __inline int
+kauth_cred_cache_lookup(__unused int from, __unused int to,
+       __unused void *src, __unused void *dst)
+{
+       return (EWOULDBLOCK);
+
+}
+#endif
+
+#if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
+/*
+ * Structure to hold supplemental groups. Used for impedance matching with 
+ * kauth_cred_cache_lookup below.
+ */
+struct supgroups {
+       int *count;
+       gid_t *groups;
+};
+
+/*
+ * kauth_cred_uid2groups
+ *
+ * Description:        Fetch supplemental GROUPS from UID
+ *
+ * Parameters: uid                             UID to examine
+ *             groups                          pointer to an array of gid_ts
+ *             gcount                          pointer to the number of groups wanted/returned
+ *
+ * Returns:    0                               Success
+ *     kauth_cred_cache_lookup:EINVAL
+ *
+ * Implicit returns:
+ *             *groups                         Modified, if successful
+ *             *gcount                         Modified, if successful
+ *
+ */
+static int
+kauth_cred_uid2groups(uid_t *uid, gid_t *groups, int *gcount)
+{
+       int rv;
+
+       struct supgroups supgroups;
+       supgroups.count = gcount;
+       supgroups.groups = groups;
+
+       rv = kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GROUPS, uid, &supgroups);
+
+       return (rv);
+}
+#endif
+
+/*
+ * kauth_cred_guid2pwnam
+ *
+ * Description:        Fetch PWNAM from GUID
+ *
+ * Parameters: guidp                           Pointer to GUID to examine
+ *             pwnam                           Pointer to user@domain buffer
+ *
+ * Returns:    0                               Success
+ *     kauth_cred_cache_lookup:EINVAL
+ *
+ * Implicit returns:
+ *             *pwnam                          Modified, if successful
+ *
+ * Notes:      pwnam is assumed to point to a buffer of MAXPATHLEN in size
+ */
+int
+kauth_cred_guid2pwnam(guid_t *guidp, char *pwnam)
+{
+       return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_PWNAM, guidp, pwnam));
+}
+
+
+/*
+ * kauth_cred_guid2grnam
+ *
+ * Description:        Fetch GRNAM from GUID
+ *
+ * Parameters: guidp                           Pointer to GUID to examine
+ *             grnam                           Pointer to group@domain buffer
+ *
+ * Returns:    0                               Success
+ *     kauth_cred_cache_lookup:EINVAL
+ *
+ * Implicit returns:
+ *             *grnam                          Modified, if successful
+ *
+ * Notes:      grnam is assumed to point to a buffer of MAXPATHLEN in size
+ */
+int
+kauth_cred_guid2grnam(guid_t *guidp, char *grnam)
+{
+       return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GRNAM, guidp, grnam));
+}
+
+
+/*
+ * kauth_cred_pwnam2guid
+ *
+ * Description:        Fetch PWNAM from GUID
+ *
+ * Parameters: pwnam                           String containing user@domain
+ *             guidp                           Pointer to buffer for GUID
+ *
+ * Returns:    0                               Success
+ *     kauth_cred_cache_lookup:EINVAL
+ *
+ * Implicit returns:
+ *             *guidp                          Modified, if successful
+ *
+ * Notes:      pwnam should not point to a request larger than MAXPATHLEN
+ *             bytes in size, including the NUL termination of the string.
+ */
+int
+kauth_cred_pwnam2guid(char *pwnam, guid_t *guidp)
+{
+       return(kauth_cred_cache_lookup(KI_VALID_PWNAM, KI_VALID_GUID, pwnam, guidp));
+}
+
+
+/*
+ * kauth_cred_grnam2guid
+ *
+ * Description:        Fetch GRNAM from GUID
+ *
+ * Parameters: grnam                           String containing group@domain
+ *             guidp                           Pointer to buffer for GUID
+ *
+ * Returns:    0                               Success
+ *     kauth_cred_cache_lookup:EINVAL
+ *
+ * Implicit returns:
+ *             *guidp                          Modified, if successful
+ *
+ * Notes:      grnam should not point to a request larger than MAXPATHLEN
+ *             bytes in size, including the NUL termination of the string.
+ */
+int
+kauth_cred_grnam2guid(char *grnam, guid_t *guidp)
+{
+       return(kauth_cred_cache_lookup(KI_VALID_GRNAM, KI_VALID_GUID, grnam, guidp));
 }
 
 
@@ -1800,33 +2493,47 @@ kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
  * Returns:    0                               Success
  *             EINVAL                          Unknown source identity type
  */
+#if CONFIG_EXT_RESOLVER
 static int
 kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
 {
        struct kauth_identity ki;
        struct kauth_identity_extlookup el;
        int error;
+       uint64_t extend_data = 0ULL;
        int (* expired)(struct kauth_identity *kip);
+       char *namebuf = NULL;
 
        KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
        
        /*
         * Look for an existing cache entry for this association.
         * If the entry has not expired, return the cached information.
+        * We do not cache user@domain translations here; they use too
+        * much memory to hold onto forever, and can not be updated
+        * atomically.
         */
+       if (to == KI_VALID_PWNAM || to == KI_VALID_GRNAM) {
+               namebuf = dst;
+       }
        ki.ki_valid = 0;
        switch(from) {
        case KI_VALID_UID:
-               error = kauth_identity_find_uid(*(uid_t *)src, &ki);
+               error = kauth_identity_find_uid(*(uid_t *)src, &ki, namebuf);
                break;
        case KI_VALID_GID:
-               error = kauth_identity_find_gid(*(gid_t *)src, &ki);
+               error = kauth_identity_find_gid(*(gid_t *)src, &ki, namebuf);
                break;
        case KI_VALID_GUID:
-               error = kauth_identity_find_guid((guid_t *)src, &ki);
+               error = kauth_identity_find_guid((guid_t *)src, &ki, namebuf);
                break;
        case KI_VALID_NTSID:
-               error = kauth_identity_find_ntsid((ntsid_t *)src, &ki);
+               error = kauth_identity_find_ntsid((ntsid_t *)src, &ki, namebuf);
+               break;
+       case KI_VALID_PWNAM:
+       case KI_VALID_GRNAM:
+               /* Names are unique in their 'from' space */
+               error = kauth_identity_find_nam((char *)src, from, &ki);
                break;
        default:
                return(EINVAL);
@@ -1840,10 +2547,19 @@ kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
                        return(error);
                }
        } else {
-               /* do we have a translation? */
-               if (ki.ki_valid & to) {
-                       /* found a valid cached entry, check expiry */
-                       switch(to) {
+               /* found a valid cached entry, check expiry */
+               switch(to) {
+               case KI_VALID_GUID:
+                       expired = kauth_identity_guid_expired;
+                       break;
+               case KI_VALID_NTSID:
+                       expired = kauth_identity_ntsid_expired;
+                       break;
+               case KI_VALID_GROUPS:
+                       expired = kauth_identity_groups_expired;
+                       break;
+               default:
+                       switch(from) {
                        case KI_VALID_GUID:
                                expired = kauth_identity_guid_expired;
                                break;
@@ -1851,44 +2567,77 @@ kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
                                expired = kauth_identity_ntsid_expired;
                                break;
                        default:
+                               expired = NULL;
+                       }
+               }
+
+               /*
+                * If no expiry function, or not expired, we have found
+                * a hit.
+                */
+               if (expired) {
+                       if (!expired(&ki)) {
+                               KAUTH_DEBUG("CACHE - entry valid, unexpired");
+                               expired = NULL; /* must clear it is used as a flag */
+                       } else {
+                               /*
+                                * We leave ki_valid set here; it contains a
+                                * translation but the TTL has expired.  If we can't
+                                * get a result from the resolver, we will use it as
+                                * a better-than nothing alternative.
+                                */
+                               
+                               KAUTH_DEBUG("CACHE - expired entry found");
+                       }
+               } else {
+                       KAUTH_DEBUG("CACHE - no expiry function");
+               }
+               
+               if (!expired) {
+                       /* do we have a translation? */
+                       if (ki.ki_valid & to) {
+                               KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki.ki_valid);
+                               DTRACE_PROC4(kauth__identity__cache__hit, int, from, int, to, void *, src, void *, dst);
+                               goto found;
+                       } else {
+                               /*
+                                * GUIDs and NTSIDs map to either a UID or a GID, but not both. 
+                                * If we went looking for a translation from GUID or NTSID and 
+                                * found a translation that wasn't for our desired type, then 
+                                * don't bother calling the resolver. We know that this 
+                                * GUID/NTSID can't translate to our desired type.
+                                */
                                switch(from) {
                                case KI_VALID_GUID:
-                                       expired = kauth_identity_guid_expired;
-                                       break;
                                case KI_VALID_NTSID:
-                                       expired = kauth_identity_ntsid_expired;
+                                       switch(to) {
+                                       case KI_VALID_GID:
+                                               if ((ki.ki_valid & KI_VALID_UID)) {
+                                                       KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_GID);
+                                                       return (ENOENT);
+                                               }
+                                               break;
+                                       case KI_VALID_UID:
+                                               if ((ki.ki_valid & KI_VALID_GID)) {
+                                                       KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_UID);
+                                                       return (ENOENT);
+                                               }
+                                               break;
+                                       }
                                        break;
-                               default:
-                                       expired = NULL;
                                }
                        }
-                       KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki.ki_valid);
-                       /*
-                        * If no expiry function, or not expired, we have found
-                        * a hit.
-                        */
-                       if (!expired) {
-                               KAUTH_DEBUG("CACHE - no expiry function");
-                               goto found;
-                       }
-                       if (!expired(&ki)) {
-                               KAUTH_DEBUG("CACHE - entry valid, unexpired");
-                               goto found;
-                       }
-                       /*
-                        * We leave ki_valid set here; it contains a
-                        * translation but the TTL has expired.  If we can't
-                        * get a result from the resolver, we will use it as
-                        * a better-than nothing alternative.
-                        */
-                       KAUTH_DEBUG("CACHE - expired entry found");
                }
        }
 
        /*
         * We failed to find a cache entry; call the resolver.
         *
-        * Note:        We ask for as much data as we can get.
+        * Note:        We ask for as much non-extended data as we can get,
+        *              and only provide (or ask for) extended information if
+        *              we have a 'from' (or 'to') which requires it.  This
+        *              way we don't pay for the extra transfer overhead for
+        *              data we don't need.
         */
        bzero(&el, sizeof(el));
        el.el_info_pid = current_proc()->p_pid;
@@ -1911,6 +2660,16 @@ kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
                el.el_usid = *(ntsid_t *)src;
                el.el_gsid = *(ntsid_t *)src;
                break;
+       case KI_VALID_PWNAM:
+               /* extra overhead */
+               el.el_flags = KAUTH_EXTLOOKUP_VALID_PWNAM;
+               extend_data = CAST_USER_ADDR_T(src);
+               break;
+       case KI_VALID_GRNAM:
+               /* extra overhead */
+               el.el_flags = KAUTH_EXTLOOKUP_VALID_GRNAM;
+               extend_data = CAST_USER_ADDR_T(src);
+               break;
        default:
                return(EINVAL);
        }
@@ -1926,25 +2685,80 @@ kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
        el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
            KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
            KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
+       if (to == KI_VALID_PWNAM) {
+               /* extra overhead */
+               el.el_flags |= KAUTH_EXTLOOKUP_WANT_PWNAM;
+               extend_data = CAST_USER_ADDR_T(dst);
+       }
+       if (to == KI_VALID_GRNAM) {
+               /* extra overhead */
+               el.el_flags |= KAUTH_EXTLOOKUP_WANT_GRNAM;
+               extend_data = CAST_USER_ADDR_T(dst);
+       }
+       if (to == KI_VALID_GROUPS) {
+               /* Expensive and only useful for an NFS client not using kerberos */
+               el.el_flags |= KAUTH_EXTLOOKUP_WANT_SUPGRPS;
+               if (ki.ki_valid & KI_VALID_GROUPS) {
+                       /*
+                        * Copy the current supplemental groups for the resolver. 
+                        * The resolver should check these groups first and if
+                        * the user (uid) is still a member it should endeavor to 
+                        * keep them in the list. Otherwise NFS clients could get
+                        * changing access to server file system objects on each
+                        * expiration.
+                        */
+                       el.el_sup_grp_cnt = ki.ki_supgrpcnt;
+
+                       memcpy(el.el_sup_groups, ki.ki_supgrps, sizeof (el.el_sup_groups[0]) * ki.ki_supgrpcnt);
+                       /* Let the resolver know these were the previous valid groups */
+                       el.el_flags |= KAUTH_EXTLOOKUP_VALID_SUPGRPS;
+                       KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
+               } else
+                       KAUTH_DEBUG("GROUPS: no valid groups to send");
+       }
+
+       /* Call resolver */
        KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
-       error = kauth_resolver_submit(&el);
+
+       DTRACE_PROC3(kauth__id__resolver__submitted, int, from, int, to, uintptr_t, src);
+       
+       error = kauth_resolver_submit(&el, extend_data);
+
+       DTRACE_PROC2(kauth__id__resolver__returned, int, error, struct kauth_identity_extlookup *, &el)
+       
        KAUTH_DEBUG("CACHE - resolver returned %d", error);
-       /* was the lookup successful? */
+
+       /* was the external lookup successful? */
        if (error == 0) {
                /*
-                * Save the results from the lookup - may have other
-                * information even if we didn't get a guid.
+                * Save the results from the lookup - we may have other
+                * information, even if we didn't get a guid or the
+                * extended data.
+                *
+                * If we came from a name, we know the extend_data is valid.
+                */
+               if (from == KI_VALID_PWNAM)
+                       el.el_flags |= KAUTH_EXTLOOKUP_VALID_PWNAM;
+               else if (from == KI_VALID_GRNAM)
+                       el.el_flags |= KAUTH_EXTLOOKUP_VALID_GRNAM;
+
+               kauth_identity_updatecache(&el, &ki, extend_data);
+
+               /*
+                * Check to see if we have a valid cache entry
+                * originating from the result.
                 */
-               kauth_identity_updatecache(&el, &ki);
+               if (!(ki.ki_valid & to)) {
+                       error = ENOENT;
+               }
        }
-       /*
-        * Check to see if we have a valid result.
-        */
-       if (!error && !(ki.ki_valid & to))
-               error = ENOENT;
        if (error)
                return(error);
 found:
+       /*
+        * Copy from the appropriate struct kauth_identity cache entry
+        * structure into the destination buffer area.
+        */
        switch(to) {
        case KI_VALID_UID:
                *(uid_t *)dst = ki.ki_uid;
@@ -1958,6 +2772,22 @@ found:
        case KI_VALID_NTSID:
                *(ntsid_t *)dst = ki.ki_ntsid;
                break;
+       case KI_VALID_GROUPS: {
+                       struct supgroups *gp = (struct supgroups *)dst;
+                       u_int32_t limit = ki.ki_supgrpcnt;
+                       
+                       if (gp->count) {
+                               limit = MIN(ki.ki_supgrpcnt, *gp->count);
+                               *gp->count = limit;
+                       }
+                       
+                       memcpy(gp->groups, ki.ki_supgrps, sizeof(gid_t) * limit);
+               }
+               break;
+       case KI_VALID_PWNAM:
+       case KI_VALID_GRNAM:
+               /* handled in kauth_resolver_complete() */
+               break;
        default:
                return(EINVAL);
        }
@@ -1972,28 +2802,6 @@ found:
  * XXX the linked-list implementation here needs to be optimized.
  */
 
-struct kauth_group_membership {
-       TAILQ_ENTRY(kauth_group_membership) gm_link;
-       uid_t   gm_uid;         /* the identity whose membership we're recording */
-       gid_t   gm_gid;         /* group of which they are a member */
-       time_t  gm_expiry;      /* TTL for the membership */
-       int     gm_flags;
-#define KAUTH_GROUP_ISMEMBER   (1<<0)
-};
-
-TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
-#define KAUTH_GROUPS_CACHEMAX          100     /* XXX sizing? */
-static int kauth_groups_count;
-
-static lck_mtx_t *kauth_groups_mtx;
-#define KAUTH_GROUPS_LOCK()    lck_mtx_lock(kauth_groups_mtx);
-#define KAUTH_GROUPS_UNLOCK()  lck_mtx_unlock(kauth_groups_mtx);
-
-static int     kauth_groups_expired(struct kauth_group_membership *gm);
-static void    kauth_groups_lru(struct kauth_group_membership *gm);
-static void    kauth_groups_updatecache(struct kauth_identity_extlookup *el);
-
-
 /*
  * kauth_groups_init
  *
@@ -2003,7 +2811,7 @@ static void       kauth_groups_updatecache(struct kauth_identity_extlookup *el);
  *
  * Returns:    (void)
  *
- * Notes:      Intialize the groups cache for use; the group cache is used
+ * Notes:      Initialize the groups cache for use; the group cache is used
  *             to avoid unnecessary calls out to user space.
  *
  *             This function is called from kauth_init() in the file
@@ -2033,7 +2841,14 @@ kauth_groups_expired(struct kauth_group_membership *gm)
 {
        struct timeval tv;
 
+       /*
+        * Expiration time of 0 means this entry is persistent.
+        */
+       if (gm->gm_expiry == 0)
+               return (0);
+               
        microuptime(&tv);
+       
        return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
 }
 
@@ -2105,7 +2920,7 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
                        } else {
                                gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
                        }
-                       gm->gm_expiry = el->el_member_valid + tv.tv_sec;
+                       gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
                        kauth_groups_lru(gm);
                        break;
                }
@@ -2126,7 +2941,7 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
                } else {
                        gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
                }
-               gm->gm_expiry = el->el_member_valid + tv.tv_sec;
+               gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
        }               
 
        /*
@@ -2137,7 +2952,7 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
         */
        KAUTH_GROUPS_LOCK();
        TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
-       if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
+       if (++kauth_groups_count > kauth_groups_cachemax) {
                gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
                TAILQ_REMOVE(&kauth_groups, gm, gm_link);
                kauth_groups_count--;
@@ -2151,6 +2966,25 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
                FREE(gm, M_KAUTH);
 }
 
+/*
+ * Trim older entries from the group membership cache.
+ *
+ * Must be called with the group cache lock held.
+ */
+static void
+kauth_groups_trimcache(int new_size) {
+        struct kauth_group_membership *gm;
+
+       lck_mtx_assert(kauth_groups_mtx, LCK_MTX_ASSERT_OWNED);
+        
+       while (kauth_groups_count > new_size) {
+               gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
+               TAILQ_REMOVE(&kauth_groups, gm, gm_link);
+               kauth_groups_count--;
+               FREE(gm, M_KAUTH);
+       }
+}
+#endif /* CONFIG_EXT_RESOLVER */
 
 /*
  * Group membership KPI
@@ -2169,7 +3003,7 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
  *                                             result of the call
  *
  * Returns:    0                               Success
- *             ENOENT                          Could not proform lookup
+ *             ENOENT                          Could not perform lookup
  *     kauth_resolver_submit:EWOULDBLOCK
  *     kauth_resolver_submit:EINTR
  *     kauth_resolver_submit:ENOMEM
@@ -2184,15 +3018,14 @@ kauth_groups_updatecache(struct kauth_identity_extlookup *el)
  * Notes:      This function guarantees not to modify resultp when returning
  *             an error.
  *
- *             This function effectively checkes the EGID as well, since the
+ *             This function effectively checks the EGID as well, since the
  *             EGID is cr_groups[0] as an implementation detail.
  */
 int
 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
 {
-       struct kauth_group_membership *gm;
-       struct kauth_identity_extlookup el;
-       int i, error;
+       posix_cred_t pcred = posix_cred_get(cred);
+       int i;
 
        /*
         * Check the per-credential list of override groups.
@@ -2200,8 +3033,8 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
         * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
         * the cache should be used for that case.
         */
-       for (i = 0; i < cred->cr_ngroups; i++) {
-               if (gid == cred->cr_groups[i]) {
+       for (i = 0; i < pcred->cr_ngroups; i++) {
+               if (gid == pcred->cr_groups[i]) {
                        *resultp = 1;
                        return(0);
                }
@@ -2211,12 +3044,16 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
         * If we don't have a UID for group membership checks, the in-cred list
         * was authoritative and we can stop here.
         */
-       if (cred->cr_gmuid == KAUTH_UID_NONE) {
+       if (pcred->cr_gmuid == KAUTH_UID_NONE) {
                *resultp = 0;
                return(0);
        }
                
-       
+#if CONFIG_EXT_RESOLVER
+       struct kauth_group_membership *gm;
+       struct kauth_identity_extlookup el;
+       int error;
+
        /*
         * If the resolver hasn't checked in yet, we are early in the boot
         * phase and the local group list is complete and authoritative.
@@ -2225,7 +3062,7 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
                *resultp = 0;
                return(0);
        }
-       
+
        /* TODO: */
        /* XXX check supplementary groups */
        /* XXX check whiteout groups */
@@ -2236,7 +3073,7 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
         */
        KAUTH_GROUPS_LOCK();
        TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
-               if ((gm->gm_uid == cred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
+               if ((gm->gm_uid == pcred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
                        kauth_groups_lru(gm);
                        break;
                }
@@ -2248,17 +3085,25 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
        KAUTH_GROUPS_UNLOCK();
 
        /* if we did, we can return now */
-       if (gm != NULL)
+       if (gm != NULL) {
+               DTRACE_PROC2(kauth__group__cache__hit, int, pcred->cr_gmuid, int, gid);
                return(0);
-       
+       }
+
        /* nothing in the cache, need to go to userland */
        bzero(&el, sizeof(el));
        el.el_info_pid = current_proc()->p_pid;
        el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
-       el.el_uid = cred->cr_gmuid;
+       el.el_uid = pcred->cr_gmuid;
        el.el_gid = gid;
        el.el_member_valid = 0;         /* XXX set by resolver? */
-       error = kauth_resolver_submit(&el);
+
+       DTRACE_PROC2(kauth__group__resolver__submitted, int, el.el_uid, int, el.el_gid);
+       
+       error = kauth_resolver_submit(&el, 0ULL);
+       
+       DTRACE_PROC2(kauth__group__resolver__returned, int, error, int, el.el_flags);
+       
        if (error != 0)
                return(error);
        /* save the results from the lookup */
@@ -2271,9 +3116,12 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
        }
 
        return(ENOENT);
+#else
+       *resultp = 0;
+       return(0);
+#endif
 }
 
-
 /*
  * kauth_cred_ismember_guid
  *
@@ -2301,15 +3149,11 @@ kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
  *                                     0       Is not member
  */
 int
-kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
+kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp)
 {
-       struct kauth_identity ki;
-       gid_t gid;
-       int error, wkg;
+       int error = 0;
 
-       error = 0;
-       wkg = kauth_wellknown_guid(guidp);
-       switch(wkg) {
+       switch (kauth_wellknown_guid(guidp)) {
        case KAUTH_WKG_NOBODY:
                *resultp = 0;
                break;
@@ -2317,6 +3161,10 @@ kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
                *resultp = 1;
                break;
        default:
+#if CONFIG_EXT_RESOLVER
+       {
+               struct kauth_identity ki;
+               gid_t gid;
 #if 6603280
                /*
                 * Grovel the identity cache looking for this GUID.
@@ -2332,7 +3180,7 @@ kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
                 * this is expected to be a common case.
                 */
                ki.ki_valid = 0;
-               if ((error = kauth_identity_find_guid(guidp, &ki)) == 0 &&
+               if ((error = kauth_identity_find_guid(guidp, &ki, NULL)) == 0 &&
                    !kauth_identity_guid_expired(&ki)) {
                        if (ki.ki_valid & KI_VALID_GID) {
                                /* It's a group after all... */
@@ -2365,6 +3213,11 @@ kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
                        error = kauth_cred_ismember_gid(cred, gid, resultp);
                }
        }
+#else  /* CONFIG_EXT_RESOLVER */
+               error = ENOENT;
+#endif /* CONFIG_EXT_RESOLVER */
+               break;
+       }
        return(error);
 }
 
@@ -2395,38 +3248,40 @@ kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
 {
        int i, err, res = 1;
        gid_t gid;
+       posix_cred_t pcred1 = posix_cred_get(cred1);
+       posix_cred_t pcred2 = posix_cred_get(cred2);
 
        /* First, check the local list of groups */
-       for (i = 0; i < cred1->cr_ngroups; i++) {
-               gid = cred1->cr_groups[i];
+       for (i = 0; i < pcred1->cr_ngroups; i++) {
+               gid = pcred1->cr_groups[i];
                if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) {
                        return err;
                }
 
-               if (!res && gid != cred2->cr_rgid && gid != cred2->cr_svgid) {
+               if (!res && gid != pcred2->cr_rgid && gid != pcred2->cr_svgid) {
                        *resultp = 0;
                        return 0;
                }
        }
 
        /* Check real gid */
-       if ((err = kauth_cred_ismember_gid(cred2, cred1->cr_rgid, &res)) != 0) {
+       if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_rgid, &res)) != 0) {
                return err;
        }
 
-       if (!res && cred1->cr_rgid != cred2->cr_rgid &&
-                       cred1->cr_rgid != cred2->cr_svgid) {
+       if (!res && pcred1->cr_rgid != pcred2->cr_rgid &&
+                       pcred1->cr_rgid != pcred2->cr_svgid) {
                *resultp = 0;
                return 0;
        }
 
        /* Finally, check saved gid */
-       if ((err = kauth_cred_ismember_gid(cred2, cred1->cr_svgid, &res)) != 0){
+       if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_svgid, &res)) != 0){
                return err;
        }
 
-       if (!res && cred1->cr_svgid != cred2->cr_rgid &&
-                       cred1->cr_svgid != cred2->cr_svgid) {
+       if (!res && pcred1->cr_svgid != pcred2->cr_rgid &&
+                       pcred1->cr_svgid != pcred2->cr_svgid) {
                *resultp = 0;
                return 0;
        }
@@ -2453,7 +3308,7 @@ kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
 int
 kauth_cred_issuser(kauth_cred_t cred)
 {
-       return(cred->cr_uid == 0);
+       return(kauth_cred_getuid(cred) == 0);
 }
 
 
@@ -2536,7 +3391,7 @@ kauth_cred_init(void)
 uid_t
 kauth_getuid(void)
 {
-       return(kauth_cred_get()->cr_uid);
+       return(kauth_cred_getuid(kauth_cred_get()));
 }
 
 
@@ -2553,7 +3408,7 @@ kauth_getuid(void)
 uid_t
 kauth_getruid(void)
 {
-       return(kauth_cred_get()->cr_ruid);
+       return(kauth_cred_getruid(kauth_cred_get()));
 }
 
 
@@ -2570,7 +3425,7 @@ kauth_getruid(void)
 gid_t
 kauth_getgid(void)
 {
-       return(kauth_cred_get()->cr_groups[0]);
+       return(kauth_cred_getgid(kauth_cred_get()));
 }
 
 
@@ -2587,7 +3442,7 @@ kauth_getgid(void)
 gid_t
 kauth_getrgid(void)
 {
-       return(kauth_cred_get()->cr_rgid);
+       return(kauth_cred_getrgid(kauth_cred_get()));
 }
 
 
@@ -2823,13 +3678,12 @@ kauth_cred_alloc(void)
        
        MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK);
        if (newcred != 0) {
+               posix_cred_t newpcred = posix_cred_get(newcred);
                bzero(newcred, sizeof(*newcred));
                newcred->cr_ref = 1;
-               newcred->cr_audit.as_aia_p = &audit_default_aia;
-               /* XXX the following will go away with cr_au */
-               newcred->cr_au.ai_auid = AU_DEFAUDITID;
+               newcred->cr_audit.as_aia_p = audit_default_aia_p;
                /* must do this, or cred has same group membership as uid 0 */
-               newcred->cr_gmuid = KAUTH_UID_NONE;
+               newpcred->cr_gmuid = KAUTH_UID_NONE;
 #if CRED_DIAGNOSTIC
        } else {
                panic("kauth_cred_alloc: couldn't allocate credential");
@@ -2878,12 +3732,13 @@ kauth_cred_t
 kauth_cred_create(kauth_cred_t cred)
 {
        kauth_cred_t    found_cred, new_cred = NULL;
+       posix_cred_t    pcred = posix_cred_get(cred);
        int is_member = 0;
 
        KAUTH_CRED_HASH_LOCK_ASSERT();
 
-       if (cred->cr_flags & CRF_NOMEMBERD) {
-               cred->cr_gmuid = KAUTH_UID_NONE;
+       if (pcred->cr_flags & CRF_NOMEMBERD) {
+               pcred->cr_gmuid = KAUTH_UID_NONE;
        } else {
                /*
                 * If the template credential is not opting out of external
@@ -2902,7 +3757,7 @@ kauth_cred_create(kauth_cred_t cred)
                         * the answer, so long as it's something the external
                         * resolver could have vended.
                         */
-                       cred->cr_gmuid = cred->cr_uid;
+                       pcred->cr_gmuid = pcred->cr_uid;
                } else {
                        /*
                         * It's not something the external resolver could
@@ -2913,13 +3768,13 @@ kauth_cred_create(kauth_cred_t cred)
                         * cost.  Since most credentials are used multiple
                         * times, we still get some performance win from this.
                         */
-                       cred->cr_gmuid = KAUTH_UID_NONE;
-                       cred->cr_flags |= CRF_NOMEMBERD;
+                       pcred->cr_gmuid = KAUTH_UID_NONE;
+                       pcred->cr_flags |= CRF_NOMEMBERD;
                }
        }
 
        /* Caller *must* specify at least the egid in cr_groups[0] */
-       if (cred->cr_ngroups < 1)
+       if (pcred->cr_ngroups < 1)
                return(NULL);
        
        for (;;) {
@@ -2943,22 +3798,20 @@ kauth_cred_create(kauth_cred_t cred)
                new_cred = kauth_cred_alloc();
                if (new_cred != NULL) {
                        int             err;
-                       new_cred->cr_uid = cred->cr_uid;
-                       new_cred->cr_ruid = cred->cr_ruid;
-                       new_cred->cr_svuid = cred->cr_svuid;
-                       new_cred->cr_rgid = cred->cr_rgid;
-                       new_cred->cr_svgid = cred->cr_svgid;
-                       new_cred->cr_gmuid = cred->cr_gmuid;
-                       new_cred->cr_ngroups = cred->cr_ngroups;        
-                       bcopy(&cred->cr_groups[0], &new_cred->cr_groups[0], sizeof(new_cred->cr_groups));
+                       posix_cred_t    new_pcred = posix_cred_get(new_cred);
+                       new_pcred->cr_uid = pcred->cr_uid;
+                       new_pcred->cr_ruid = pcred->cr_ruid;
+                       new_pcred->cr_svuid = pcred->cr_svuid;
+                       new_pcred->cr_rgid = pcred->cr_rgid;
+                       new_pcred->cr_svgid = pcred->cr_svgid;
+                       new_pcred->cr_gmuid = pcred->cr_gmuid;
+                       new_pcred->cr_ngroups = pcred->cr_ngroups;      
+                       bcopy(&pcred->cr_groups[0], &new_pcred->cr_groups[0], sizeof(new_pcred->cr_groups));
 #if CONFIG_AUDIT
                        bcopy(&cred->cr_audit, &new_cred->cr_audit, 
                            sizeof(new_cred->cr_audit));
-                       /* XXX the following bcopy() will go away with cr_au */
-                       bcopy(&cred->cr_au, &new_cred->cr_au,
-                           sizeof(new_cred->cr_au));
 #endif
-                       new_cred->cr_flags = cred->cr_flags;
+                       new_pcred->cr_flags = pcred->cr_flags;
                        
                        KAUTH_CRED_HASH_LOCK();
                        err = kauth_cred_add(new_cred);
@@ -3017,6 +3870,8 @@ kauth_cred_t
 kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
 {
        struct ucred temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred = posix_cred_get(cred);
 
        NULLCRED_CHECK(cred);
 
@@ -3024,10 +3879,10 @@ kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid
         * We don't need to do anything if the UIDs we are changing are
         * already the same as the UIDs passed in
         */
-       if ((euid == KAUTH_UID_NONE || cred->cr_uid == euid) &&
-           (ruid == KAUTH_UID_NONE || cred->cr_ruid == ruid) &&
-           (svuid == KAUTH_UID_NONE || cred->cr_svuid == svuid) &&
-           (cred->cr_gmuid == gmuid)) {
+       if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) &&
+           (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) &&
+           (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) &&
+           (pcred->cr_gmuid == gmuid)) {
                /* no change needed */
                return(cred);
        }
@@ -3038,13 +3893,13 @@ kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid
         */
        bcopy(cred, &temp_cred, sizeof(temp_cred));
        if (euid != KAUTH_UID_NONE) {
-               temp_cred.cr_uid = euid;
+               temp_pcred->cr_uid = euid;
        }
        if (ruid != KAUTH_UID_NONE) {
-               temp_cred.cr_ruid = ruid;
+               temp_pcred->cr_ruid = ruid;
        }
        if (svuid != KAUTH_UID_NONE) {
-               temp_cred.cr_svuid = svuid;
+               temp_pcred->cr_svuid = svuid;
        }
 
        /*
@@ -3052,8 +3907,8 @@ kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid
         * opt out of participation in external group resolution, unless we
         * unless we explicitly opt back in later.
         */
-       if ((temp_cred.cr_gmuid = gmuid) == KAUTH_UID_NONE) {
-               temp_cred.cr_flags |= CRF_NOMEMBERD;
+       if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) {
+               temp_pcred->cr_flags |= CRF_NOMEMBERD;
        }
 
        return(kauth_cred_update(cred, &temp_cred, TRUE));
@@ -3090,6 +3945,8 @@ kauth_cred_t
 kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
 {
        struct ucred    temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred = posix_cred_get(cred);
 
        NULLCRED_CHECK(cred);
        DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
@@ -3098,9 +3955,9 @@ kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
         * We don't need to do anything if the given GID are already the 
         * same as the GIDs in the credential.
         */
-       if (cred->cr_groups[0] == egid &&
-           cred->cr_rgid == rgid &&
-           cred->cr_svgid == svgid) {
+       if (pcred->cr_groups[0] == egid &&
+           pcred->cr_rgid == rgid &&
+           pcred->cr_svgid == svgid) {
                /* no change needed */
                return(cred);
        }
@@ -3114,17 +3971,17 @@ kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
                /* displacing a supplementary group opts us out of memberd */
                if (kauth_cred_change_egid(&temp_cred, egid)) {
                        DEBUG_CRED_CHANGE("displaced!\n");
-                       temp_cred.cr_flags |= CRF_NOMEMBERD;
-                       temp_cred.cr_gmuid = KAUTH_UID_NONE;
+                       temp_pcred->cr_flags |= CRF_NOMEMBERD;
+                       temp_pcred->cr_gmuid = KAUTH_UID_NONE;
                } else {
                        DEBUG_CRED_CHANGE("not displaced\n");
                }
        }
        if (rgid != KAUTH_GID_NONE) {
-               temp_cred.cr_rgid = rgid;
+               temp_pcred->cr_rgid = rgid;
        }
        if (svgid != KAUTH_GID_NONE) {
-               temp_cred.cr_svgid = svgid;
+               temp_pcred->cr_svgid = svgid;
        }
 
        return(kauth_cred_update(cred, &temp_cred, TRUE));
@@ -3147,7 +4004,7 @@ kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
  * Parameters: cred                            The original credential
  *             groups                          Pointer to gid_t array which
  *                                             contains the new group list
- *             groupcount                      The cound of valid groups which
+ *             groupcount                      The count of valid groups which
  *                                             are contained in 'groups'
  *             gmuid                           KAUTH_UID_NONE -or- the new
  *                                             group membership UID
@@ -3170,7 +4027,7 @@ kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
  *             that is returned to them, if it is not intended to be a
  *             persistent reference.
  *
- * XXX:                Changes are determined in ordinal order - if the caller pasess
+ * XXX:                Changes are determined in ordinal order - if the caller passes
  *             in the same groups list that is already present in the
  *             credential, but the members are in a different order, even if
  *             the EGID is not modified (i.e. cr_groups[0] is the same), it
@@ -3185,16 +4042,20 @@ kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmu
 {
        int             i;
        struct ucred temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred;
 
        NULLCRED_CHECK(cred);
 
+       pcred = posix_cred_get(cred);
+
        /*
         * We don't need to do anything if the given list of groups does not
         * change.
         */
-       if ((cred->cr_gmuid == gmuid) && (cred->cr_ngroups == groupcount)) {
+       if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) {
                for (i = 0; i < groupcount; i++) {
-                       if (cred->cr_groups[i] != groups[i])
+                       if (pcred->cr_groups[i] != groups[i])
                                break;
                }
                if (i == groupcount) {
@@ -3211,17 +4072,71 @@ kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmu
         * using initgroups().  This is required for POSIX conformance.
         */
        bcopy(cred, &temp_cred, sizeof(temp_cred));
-       temp_cred.cr_ngroups = groupcount;
-       bcopy(groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
-       temp_cred.cr_gmuid = gmuid;
+       temp_pcred->cr_ngroups = groupcount;
+       bcopy(groups, temp_pcred->cr_groups, sizeof(temp_pcred->cr_groups));
+       temp_pcred->cr_gmuid = gmuid;
        if (gmuid == KAUTH_UID_NONE)
-               temp_cred.cr_flags |= CRF_NOMEMBERD;
+               temp_pcred->cr_flags |= CRF_NOMEMBERD;
        else
-               temp_cred.cr_flags &= ~CRF_NOMEMBERD;
+               temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
 
        return(kauth_cred_update(cred, &temp_cred, TRUE));
 }
 
+/*
+ * Notes:      The return value exists to account for the possibility of a
+ *             kauth_cred_t without a POSIX label.  This will be the case in
+ *             the future (see posix_cred_get() below, for more details).
+ */
+#if CONFIG_EXT_RESOLVER
+int kauth_external_supplementary_groups_supported = 1;
+
+SYSCTL_INT(_kern, OID_AUTO, ds_supgroups_supported, CTLFLAG_RW | CTLFLAG_LOCKED, &kauth_external_supplementary_groups_supported, 0, "");
+#endif
+
+int
+kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, int *countp)
+{
+       int limit = NGROUPS;
+       posix_cred_t pcred;
+       
+       pcred = posix_cred_get(cred);
+
+#if CONFIG_EXT_RESOLVER  
+       /*
+        * If we've not opted out of using the resolver, then convert the cred to a list
+        * of supplemental groups. We do this only if there has been a resolver to talk to,
+        * since we may be too early in boot, or in an environment that isn't using DS.
+        */
+       if (kauth_identitysvc_has_registered && kauth_external_supplementary_groups_supported && (pcred->cr_flags & CRF_NOMEMBERD) == 0) {              
+               uid_t uid = kauth_cred_getuid(cred);
+               int err;
+               
+               err = kauth_cred_uid2groups(&uid, grouplist, countp);
+               if (!err)
+                       return 0;
+
+               /* On error just fall through */
+               KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err);
+       }
+#endif /* CONFIG_EXT_RESOLVER */
+
+       /*
+        * If they just want a copy of the groups list, they may not care
+        * about the actual count.  If they specify an input count, however,
+        * treat it as an indicator of the buffer size available in grouplist,
+        * and limit the returned list to that size.
+        */
+       if (countp) {
+               limit = MIN(*countp, pcred->cr_ngroups);
+               *countp = limit;
+       }
+
+       memcpy(grouplist, pcred->cr_groups, sizeof(gid_t) * limit);
+
+       return 0;
+}
+
 
 /*
  * kauth_cred_setuidgid
@@ -3262,15 +4177,19 @@ kauth_cred_t
 kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
 {
        struct ucred temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred;
 
        NULLCRED_CHECK(cred);
 
+       pcred = posix_cred_get(cred);
+
        /*
         * We don't need to do anything if the effective, real and saved
         * user IDs are already the same as the user ID passed into us.
         */
-       if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid &&
-               cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
+       if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid &&
+               pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) {
                /* no change needed */
                return(cred);
        }
@@ -3280,26 +4199,26 @@ kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
         * with the new values.
         */
        bzero(&temp_cred, sizeof(temp_cred));
-       temp_cred.cr_uid = uid;
-       temp_cred.cr_ruid = uid;
-       temp_cred.cr_svuid = uid;
-       temp_cred.cr_flags = cred->cr_flags;
+       temp_pcred->cr_uid = uid;
+       temp_pcred->cr_ruid = uid;
+       temp_pcred->cr_svuid = uid;
+       temp_pcred->cr_flags = pcred->cr_flags;
        /* inherit the opt-out of memberd */
-       if (cred->cr_flags & CRF_NOMEMBERD) {
-               temp_cred.cr_gmuid = KAUTH_UID_NONE;
-               temp_cred.cr_flags |= CRF_NOMEMBERD;
+       if (pcred->cr_flags & CRF_NOMEMBERD) {
+               temp_pcred->cr_gmuid = KAUTH_UID_NONE;
+               temp_pcred->cr_flags |= CRF_NOMEMBERD;
        } else {
-               temp_cred.cr_gmuid = uid;
-               temp_cred.cr_flags &= ~CRF_NOMEMBERD;
+               temp_pcred->cr_gmuid = uid;
+               temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
        }
-       temp_cred.cr_ngroups = 1;
+       temp_pcred->cr_ngroups = 1;
        /* displacing a supplementary group opts us out of memberd */
        if (kauth_cred_change_egid(&temp_cred, gid)) {
-               temp_cred.cr_gmuid = KAUTH_UID_NONE;
-               temp_cred.cr_flags |= CRF_NOMEMBERD;
+               temp_pcred->cr_gmuid = KAUTH_UID_NONE;
+               temp_pcred->cr_flags |= CRF_NOMEMBERD;
        }
-       temp_cred.cr_rgid = gid;
-       temp_cred.cr_svgid = gid;
+       temp_pcred->cr_rgid = gid;
+       temp_pcred->cr_svgid = gid;
 #if CONFIG_MACF
        temp_cred.cr_label = cred->cr_label;
 #endif
@@ -3336,8 +4255,13 @@ kauth_cred_t
 kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
 {
        struct ucred temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred;
 
        NULLCRED_CHECK(cred);
+
+       pcred = posix_cred_get(cred);
+
        DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
 
        /*
@@ -3345,7 +4269,7 @@ kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
         * uids are already the same as the uid provided.  This check is
         * likely insufficient.
         */
-       if (cred->cr_svuid == uid && cred->cr_svgid == gid) {
+       if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) {
                /* no change needed */
                return(cred);
        }
@@ -3355,8 +4279,8 @@ kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
         * with new values.
         */
        bcopy(cred, &temp_cred, sizeof(temp_cred));
-       temp_cred.cr_svuid = uid;
-       temp_cred.cr_svgid = gid;
+       temp_pcred->cr_svuid = uid;
+       temp_pcred->cr_svgid = gid;
 
        return(kauth_cred_update(cred, &temp_cred, TRUE));
 }
@@ -3402,18 +4326,6 @@ kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
 
        bcopy(cred, &temp_cred, sizeof(temp_cred));
        bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
-       /* XXX the following will go away with cr_au */
-       temp_cred.cr_au.ai_auid = auditinfo_p->as_aia_p->ai_auid;
-       temp_cred.cr_au.ai_mask.am_success = 
-               auditinfo_p->as_mask.am_success;
-       temp_cred.cr_au.ai_mask.am_failure = 
-               auditinfo_p->as_mask.am_failure;
-       temp_cred.cr_au.ai_termid.port = 
-               auditinfo_p->as_aia_p->ai_termid.at_port;
-       temp_cred.cr_au.ai_termid.machine = 
-               auditinfo_p->as_aia_p->ai_termid.at_addr[0];
-       temp_cred.cr_au.ai_asid = auditinfo_p->as_aia_p->ai_asid;
-       /* XXX */
 
        return(kauth_cred_update(cred, &temp_cred, FALSE));
 }
@@ -3489,11 +4401,12 @@ kauth_cred_label_update(kauth_cred_t cred, struct label *label)
  *             that is returned to them, if it is not intended to be a
  *             persistent reference.
  */
+
 static
 kauth_cred_t
 kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
-       struct vnode *vp, struct label *scriptl, struct label *execl,
-       int *disjointp)
+       struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
+       struct label *execl, unsigned int *csflags, void *macextensions, int *disjointp, int *labelupdateerror)
 {
        kauth_cred_t newcred;
        struct ucred temp_cred;
@@ -3502,8 +4415,9 @@ kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
 
        mac_cred_label_init(&temp_cred);
        mac_cred_label_associate(cred, &temp_cred);
-       *disjointp = mac_cred_label_update_execve(ctx, &temp_cred, 
-                                                    vp, scriptl, execl);
+       mac_cred_label_update_execve(ctx, &temp_cred, 
+                                                 vp, offset, scriptvp, scriptl, execl, csflags,
+                                                 macextensions, disjointp, labelupdateerror);
 
        newcred = kauth_cred_update(cred, &temp_cred, TRUE);
        mac_cred_label_destroy(&temp_cred);
@@ -3560,6 +4474,9 @@ int kauth_proc_label_update(struct proc *p, struct label *label)
                                continue;
                        }
                        p->p_ucred = my_new_cred;
+                       /* update cred on proc */
+                       PROC_UPDATE_CREDS_ONPROC(p);
+
                        mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
                        proc_unlock(p);
                }
@@ -3584,6 +4501,8 @@ int kauth_proc_label_update(struct proc *p, struct label *label)
  *             vp                      The vnode being exec'ed
  *             scriptl                 The script MAC label
  *             execl                   The executable MAC label
+ *             lupdateerror    The error place holder for MAC label authority 
+ *                                             to update about possible termination
  *
  * Returns:    0                       Label update did not make credential
  *                                     disjoint
@@ -3594,13 +4513,13 @@ int kauth_proc_label_update(struct proc *p, struct label *label)
  *             result of this call.  The caller should not assume the process
  *             reference to the old credential still exists.
  */
-int
+void
 kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
-       struct vnode *vp, struct label *scriptl, struct label *execl)
+       struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
+       struct label *execl, unsigned int *csflags, void *macextensions, int *disjoint, int *update_return)
 {
        kauth_cred_t my_cred, my_new_cred;
-       int disjoint = 0;
-
        my_cred = kauth_cred_proc_ref(p);
 
        DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
@@ -3615,7 +4534,7 @@ kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
                 * passed in.  The subsequent compare is safe, because it is
                 * a pointer compare rather than a contents compare.
                 */
-               my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, scriptl, execl, &disjoint);
+               my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, offset, scriptvp, scriptl, execl, csflags, macextensions, disjoint, update_return);
                if (my_cred != my_new_cred) {
 
                        DEBUG_CRED_CHANGE("kauth_proc_label_update_execve_unlocked 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);
@@ -3635,6 +4554,8 @@ kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
                                continue;
                        }
                        p->p_ucred = my_new_cred;
+                       /* update cred on proc */
+                       PROC_UPDATE_CREDS_ONPROC(p);
                        mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
                        proc_unlock(p);
                }
@@ -3642,8 +4563,6 @@ kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
        }
        /* Drop old proc reference or our extra reference */
        kauth_cred_unref(&my_cred);
-       
-       return (disjoint);
 }
 
 #if 1
@@ -3757,7 +4676,8 @@ kauth_cred_ref(kauth_cred_t cred)
  * Parameters: credp                           Pointer to address containing
  *                                             credential to be freed
  *
- * Returns:    (void)
+ * Returns:    TRUE if the credential must be destroyed by the caller.
+ *             FALSE otherwise.
  *
  * Implicit returns:
  *             *credp                          Set to NOCRED
@@ -3768,18 +4688,19 @@ kauth_cred_ref(kauth_cred_t cred)
  *             scoped to this compilation unit.
  *
  *             This function destroys the contents of the pointer passed by
- *             the caller to prevent the caller accidently attempting to
+ *             the caller to prevent the caller accidentally attempting to
  *             release a given reference twice in error.
  *
  *             The last reference is considered to be released when a release
  *             of a credential of a reference count of 2 occurs; this is an
- *             intended effect, to take into accout the reference held by
+ *             intended effect, to take into account the reference held by
  *             the credential hash, which is released at the same time.
  */
-static void
+static boolean_t
 kauth_cred_unref_hashlocked(kauth_cred_t *credp)
 {
        int             old_value;
+       boolean_t       destroy_it = FALSE;
 
        KAUTH_CRED_HASH_LOCK_ASSERT();
        NULLCRED_CHECK(*credp);
@@ -3805,9 +4726,14 @@ kauth_cred_unref_hashlocked(kauth_cred_t *credp)
         */
        if (old_value < 3) {
                /* The last absolute reference is our credential hash table */
-               kauth_cred_remove(*credp);
+               destroy_it = kauth_cred_remove(*credp);
+       }
+
+       if (destroy_it == FALSE) {
+               *credp = NOCRED;
        }
-       *credp = NOCRED;
+
+       return (destroy_it);
 }
 
 
@@ -3832,9 +4758,23 @@ kauth_cred_unref_hashlocked(kauth_cred_t *credp)
 void
 kauth_cred_unref(kauth_cred_t *credp)
 {
+       boolean_t destroy_it;
+
        KAUTH_CRED_HASH_LOCK();
-       kauth_cred_unref_hashlocked(credp);
+       destroy_it = kauth_cred_unref_hashlocked(credp);
        KAUTH_CRED_HASH_UNLOCK();
+
+       if (destroy_it == TRUE) {
+               assert(*credp != NOCRED);
+#if CONFIG_MACF
+               mac_cred_label_destroy(*credp);
+#endif
+               AUDIT_SESSION_UNREF(*credp);
+
+               (*credp)->cr_ref = 0;
+               FREE_ZONE(*credp, sizeof(*(*credp)), M_CRED);
+               *credp = NOCRED;
+       }
 }
 
 
@@ -3889,11 +4829,11 @@ kauth_cred_rele(kauth_cred_t cred)
  *             referencing them, prior to making them visible in an externally
  *             visible pointer (e.g. by adding them to the credential hash
  *             cache) is the only legal time in which an existing credential
- *             can be safely iinitialized or modified directly.
+ *             can be safely initialized or modified directly.
  *
  *             After initialization, the caller is expected to call the
  *             function kauth_cred_add() to add the credential to the hash
- *             cache, after which time it's frozen and becomes publically
+ *             cache, after which time it's frozen and becomes publicly
  *             visible.
  *
  *             The release protocol depends on kauth_hash_add() being called
@@ -3944,17 +4884,19 @@ kauth_cred_dup(kauth_cred_t cred)
  *             result, the caller is responsible for dropping BOTH the
  *             additional reference on the passed cred (if any), and the
  *             credential returned by this function.  The drop should be
- *             via the satnadr kauth_cred_unref() KPI.
+ *             via the kauth_cred_unref() KPI.
  */
 kauth_cred_t
 kauth_cred_copy_real(kauth_cred_t cred)
 {
        kauth_cred_t newcred = NULL, found_cred;
        struct ucred temp_cred;
+       posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
+       posix_cred_t pcred = posix_cred_get(cred);
 
        /* if the credential is already 'real', just take a reference */
-       if ((cred->cr_ruid == cred->cr_uid) &&
-           (cred->cr_rgid == cred->cr_gid)) {
+       if ((pcred->cr_ruid == pcred->cr_uid) &&
+           (pcred->cr_rgid == pcred->cr_gid)) {
                kauth_cred_ref(cred);
                return(cred);
        }
@@ -3964,18 +4906,18 @@ kauth_cred_copy_real(kauth_cred_t cred)
         * with the new values.
         */
        bcopy(cred, &temp_cred, sizeof(temp_cred));
-       temp_cred.cr_uid = cred->cr_ruid;
+       temp_pcred->cr_uid = pcred->cr_ruid;
        /* displacing a supplementary group opts us out of memberd */
-       if (kauth_cred_change_egid(&temp_cred, cred->cr_rgid)) {
-               temp_cred.cr_flags |= CRF_NOMEMBERD;
-               temp_cred.cr_gmuid = KAUTH_UID_NONE;
+       if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) {
+               temp_pcred->cr_flags |= CRF_NOMEMBERD;
+               temp_pcred->cr_gmuid = KAUTH_UID_NONE;
        }
        /*
         * If the cred is not opted out, make sure we are using the r/euid
         * for group checks
         */
-       if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
-               temp_cred.cr_gmuid = cred->cr_ruid;
+       if (temp_pcred->cr_gmuid != KAUTH_UID_NONE)
+               temp_pcred->cr_gmuid = pcred->cr_ruid;
 
        for (;;) {
                int             err;
@@ -4063,9 +5005,6 @@ kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
        if (retain_auditinfo) {
                bcopy(&old_cred->cr_audit, &model_cred->cr_audit, 
                    sizeof(model_cred->cr_audit));
-               /* XXX following bcopy will go away with cr_au */
-               bcopy(&old_cred->cr_au, &model_cred->cr_au,
-                   sizeof(model_cred->cr_au));
        }
        
        for (;;) {
@@ -4079,17 +5018,31 @@ kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
                        return(old_cred); 
                }
                if (found_cred != NULL) {
+                       boolean_t destroy_it;
+
                        DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
                        /*
                         * Found a match so we bump reference count on new
                         * one and decrement reference count on the old one.
                         */
                        kauth_cred_ref(found_cred);
-                       kauth_cred_unref_hashlocked(&old_cred);
+                       destroy_it = kauth_cred_unref_hashlocked(&old_cred);
                        KAUTH_CRED_HASH_UNLOCK();
+                       if (destroy_it == TRUE) {
+                               assert(old_cred != NOCRED);
+#if CONFIG_MACF
+                               mac_cred_label_destroy(old_cred);
+#endif
+                               AUDIT_SESSION_UNREF(old_cred);
+
+                               old_cred->cr_ref = 0;
+                               FREE_ZONE(old_cred, sizeof(*old_cred), M_CRED);
+                               old_cred = NOCRED;
+
+                       }
                        return(found_cred);
                }
-       
+
                /*
                 * Must allocate a new credential using the model.  also
                 * adds the new credential to the credential hash table.
@@ -4171,7 +5124,7 @@ kauth_cred_add(kauth_cred_t new_cred)
  * Parameters: cred                            Credential to remove from cred
  *                                             hash cache
  *
- * Returns:    (void)
+ * Returns:    TRUE if the cred was found & removed from the hash; FALSE if not.
  *
  * Locks:      Caller is expected to hold KAUTH_CRED_HASH_LOCK
  *
@@ -4180,7 +5133,7 @@ kauth_cred_add(kauth_cred_t new_cred)
  *             following code occurs with the hash lock held; in theory, this
  *             protects us from the 2->1 reference that gets us here.
  */
-static void
+static boolean_t
 kauth_cred_remove(kauth_cred_t cred)
 {
        u_long                  hash_key;
@@ -4193,30 +5146,23 @@ kauth_cred_remove(kauth_cred_t cred)
        if (cred->cr_ref < 1)
                panic("cred reference underflow");
        if (cred->cr_ref > 1)
-               return;         /* someone else got a ref */
+               return (FALSE);         /* someone else got a ref */
                
        /* Find cred in the credential hash table */
        TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
                if (found_cred == cred) {
                        /* found a match, remove it from the hash table */
                        TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
-#if CONFIG_MACF
-                       mac_cred_label_destroy(cred);
-#endif
-                       AUDIT_SESSION_UNREF(cred);
-
-                       cred->cr_ref = 0;
-                       FREE_ZONE(cred, sizeof(*cred), M_CRED);
 #if KAUTH_CRED_HASH_DEBUG
                        kauth_cred_count--;
 #endif
-                       return;
+                       return (TRUE);
                }
        }
 
        /* Did not find a match... this should not happen! XXX Make panic? */
        printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
-       return;
+       return (FALSE);
 }
 
 
@@ -4230,7 +5176,7 @@ kauth_cred_remove(kauth_cred_t cred)
  *                                             hash cache
  *
  * Returns:    NULL                            Not found
- *             !NULL                           Matching cedential already in
+ *             !NULL                           Matching credential already in
  *                                             cred hash cache
  *
  * Locks:      Caller is expected to hold KAUTH_CRED_HASH_LOCK
@@ -4240,6 +5186,7 @@ kauth_cred_find(kauth_cred_t cred)
 {
        u_long                  hash_key;
        kauth_cred_t    found_cred;
+       posix_cred_t pcred = posix_cred_get(cred);
 
        KAUTH_CRED_HASH_LOCK_ASSERT();
 
@@ -4258,24 +5205,22 @@ kauth_cred_find(kauth_cred_t cred)
        /* Find cred in the credential hash table */
        TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
                boolean_t match;
+               posix_cred_t found_pcred = posix_cred_get(found_cred);
 
                /*
                 * don't worry about the label unless the flags in
                 * either credential tell us to.
                 */
-               if ((found_cred->cr_flags & CRF_MAC_ENFORCE) != 0 ||
-                   (cred->cr_flags & CRF_MAC_ENFORCE) != 0) {
-                       /* include the label pointer in the compare */
-                       match = (bcmp(&found_cred->cr_uid, &cred->cr_uid,
-                                (sizeof(struct ucred) -
-                                 offsetof(struct ucred, cr_uid))) == 0);
-               } else {
-                       /* flags have to match, but skip the label in bcmp */
-                       match = (found_cred->cr_flags == cred->cr_flags &&
-                                bcmp(&found_cred->cr_uid, &cred->cr_uid,
-                                     (offsetof(struct ucred, cr_label) -
-                                      offsetof(struct ucred, cr_uid))) == 0);
+               match = (bcmp(found_pcred, pcred, sizeof (*pcred)) == 0) ? TRUE : FALSE;
+               match = match && ((bcmp(&found_cred->cr_audit, &cred->cr_audit,
+                       sizeof(cred->cr_audit)) == 0) ? TRUE : FALSE);
+#if CONFIG_MACF
+               if (((found_pcred->cr_flags & CRF_MAC_ENFORCE) != 0) ||
+                   ((pcred->cr_flags & CRF_MAC_ENFORCE) != 0)) {
+                       match = match && mac_cred_label_compare(found_cred->cr_label,
+                               cred->cr_label);
                }
+#endif
                if (match) {
                        /* found a match */
                        return(found_cred);
@@ -4326,24 +5271,35 @@ kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
  *             not including the ref count or the TAILQ, which are mutable;
  *             everything else isn't.
  *
- *             We also avoid the label (if the flag is not set saying the
- *             label is actually enforced).
- *
  * Parameters: cred                            Credential for which hash is
  *                                             desired
  *
  * Returns:    (u_long)                        Returned hash key
+ *
+ * Notes:      When actually moving the POSIX credential into a real label,
+ *             remember to update this hash computation.
  */
 static u_long
 kauth_cred_get_hashkey(kauth_cred_t cred)
 {
+#if CONFIG_MACF
+       posix_cred_t pcred = posix_cred_get(cred);
+#endif
        u_long  hash_key = 0;
-       
-       hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid, 
-                       ((cred->cr_flags & CRF_MAC_ENFORCE) ? 
-                           sizeof(struct ucred) : offsetof(struct ucred, cr_label)) -
-                           offsetof(struct ucred, cr_uid),
-                       hash_key);
+
+       hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix, 
+                                                          sizeof (struct posix_cred),
+                                                          hash_key);
+       hash_key = kauth_cred_hash((uint8_t *)&cred->cr_audit, 
+                                                          sizeof(struct au_session),
+                                                          hash_key);
+#if CONFIG_MACF
+       if (pcred->cr_flags & CRF_MAC_ENFORCE) {
+               hash_key = kauth_cred_hash((uint8_t *)cred->cr_label, 
+                                                                  sizeof (struct label),
+                                                                  hash_key);
+       }
+#endif
        return(hash_key);
 }
 
@@ -4691,3 +5647,226 @@ sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *ar
 }
 
 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
+
+
+/*
+ **********************************************************************
+ * The following routines will be moved to a policy_posix.c module at
+ * some future point.
+ **********************************************************************
+ */
+
+/*
+ * posix_cred_create
+ *
+ * Description:        Helper function to create a kauth_cred_t credential that is
+ *             initally labelled with a specific POSIX credential label
+ *
+ * Parameters: pcred                   The posix_cred_t to use as the initial
+ *                                     label value
+ *
+ * Returns:    (kauth_cred_t)          The credential that was found in the
+ *                                     hash or creates
+ *             NULL                    kauth_cred_add() failed, or there was
+ *                                     no egid specified, or we failed to
+ *                                     attach a label to the new credential
+ *
+ * Notes:      This function currently wraps kauth_cred_create(), and is the
+ *             only consumer of that ill-fated function, apart from bsd_init().
+ *             It exists solely to support the NFS server code creation of
+ *             credentials based on the over-the-wire RPC calls containing
+ *             traditional POSIX credential information being tunneled to
+ *             the server host from the client machine.
+ *
+ *             In the future, we hope this function goes away.
+ *
+ *             In the short term, it creates a temporary credential, puts
+ *             the POSIX information from NFS into it, and then calls
+ *             kauth_cred_create(), as an internal implementation detail.
+ *
+ *             If we have to keep it around in the medium term, it will
+ *             create a new kauth_cred_t, then label it with a POSIX label
+ *             corresponding to the contents of the kauth_cred_t.  If the
+ *             policy_posix MACF module is not loaded, it will instead
+ *             substitute a posix_cred_t which GRANTS all access (effectively
+ *             a "root" credential) in order to not prevent NFS from working
+ *             in the case that we are not supporting POSIX credentials.
+ */
+kauth_cred_t
+posix_cred_create(posix_cred_t pcred)
+{
+       struct ucred temp_cred;
+
+       bzero(&temp_cred, sizeof(temp_cred));
+       temp_cred.cr_posix = *pcred;
+
+       return kauth_cred_create(&temp_cred);
+}
+
+
+/*
+ * posix_cred_get
+ *
+ * Description:        Given a kauth_cred_t, return the POSIX credential label, if
+ *             any, which is associated with it.
+ *
+ * Parameters: cred                    The credential to obtain the label from
+ *
+ * Returns:    posix_cred_t            The POSIX credential label
+ *
+ * Notes:      In the event that the policy_posix MACF module IS NOT loaded,
+ *             this function will return a pointer to a posix_cred_t which
+ *             GRANTS all access (effectively, a "root" credential).  This is
+ *             necessary to support legacy code which insists on tightly
+ *             integrating POSIX credentials into its APIs, including, but
+ *             not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
+ *             NFSv3, signals, dtrace, and a large number of kauth routines
+ *             used to implement POSIX permissions related system calls.
+ *
+ *             In the event that the policy_posix MACF module IS loaded, and
+ *             there is no POSIX label on the kauth_cred_t credential, this
+ *             function will return a pointer to a posix_cred_t which DENIES
+ *             all access (effectively, a "deny rights granted by POSIX"
+ *             credential).  This is necessary to support the concept of a
+ *             transiently loaded POSIX policy, or kauth_cred_t credentials
+ *             which can not be used in conjunctions with POSIX permissions
+ *             checks.
+ *
+ *             This function currently returns the address of the cr_posix
+ *             field of the supplied kauth_cred_t credential, and as such
+ *             currently can not fail.  In the future, this will not be the
+ *             case.
+ */
+posix_cred_t
+posix_cred_get(kauth_cred_t cred)
+{
+       return(&cred->cr_posix);
+}
+
+
+/*
+ * posix_cred_label
+ *
+ * Description:        Label a kauth_cred_t with a POSIX credential label
+ *
+ * Parameters: cred                    The credential to label
+ *             pcred                   The POSIX credential t label it with
+ *
+ * Returns:    (void)
+ *
+ * Notes:      This function is currently void in order to permit it to fit
+ *             in with the current MACF framework label methods which allow
+ *             labeling to fail silently.  This is like acceptable for
+ *             mandatory access controls, but not for POSIX, since those
+ *             access controls are advisory.  We will need to consider a
+ *             return value in a future version of the MACF API.
+ *
+ *             This operation currently cannot fail, as currently the POSIX
+ *             credential is a subfield of the kauth_cred_t (ucred), which
+ *             MUST be valid.  In the future, this will not be the case.
+ */
+void
+posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
+{
+       cred->cr_posix = *pcred;        /* structure assign for now */
+}
+
+
+/*
+ * posix_cred_access
+ *
+ * Description:        Perform a POSIX access check for a protected object
+ *
+ * Parameters: cred                    The credential to check
+ *             object_uid              The POSIX UID of the protected object
+ *             object_gid              The POSIX GID of the protected object
+ *             object_mode             The POSIX mode of the protected object
+ *             mode_req                The requested POSIX access rights
+ *
+ * Returns     0                       Access is granted
+ *             EACCES                  Access is denied
+ *
+ * Notes:      This code optimizes the case where the world and group rights
+ *             would both grant the requested rights to avoid making a group
+ *             membership query.  This is a big performance win in the case
+ *             where this is true.
+ */
+int
+posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
+{
+       int is_member;
+       mode_t mode_owner = (object_mode & S_IRWXU);
+       mode_t mode_group = (object_mode & S_IRWXG) << 3;
+       mode_t mode_world = (object_mode & S_IRWXO) << 6;
+
+       /*
+        * Check first for owner rights
+        */
+       if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req)
+               return (0);
+
+       /*
+        * Combined group and world rights check, if we don't have owner rights
+        *
+        * OPTIMIZED: If group and world rights would grant the same bits, and
+        * they set of requested bits is in both, then we can simply check the
+        * world rights, avoiding a group membership check, which is expensive.
+        */
+       if ((mode_req & mode_group & mode_world) == mode_req) {
+               return (0);
+       } else {
+               /*
+                * NON-OPTIMIZED: requires group membership check.
+                */
+               if ((mode_req & mode_group) != mode_req) {
+                       /*
+                        * exclusion group : treat errors as "is a member"
+                        *
+                        * NON-OPTIMIZED: +group would deny; must check group
+                        */
+                       if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
+                               /*
+                                * DENY: +group denies
+                                */
+                               return (EACCES);
+                       } else {
+                               if ((mode_req & mode_world) != mode_req) {
+                                       /*
+                                        * DENY: both -group & world would deny
+                                        */
+                                       return (EACCES);
+                               } else {
+                                       /*
+                                        * ALLOW: allowed by -group and +world
+                                        */
+                                       return (0);
+                               }
+                       }
+               } else {
+                       /*
+                        * inclusion group; treat errors as "not a member"
+                        *
+                        * NON-OPTIMIZED: +group allows, world denies; must
+                        * check group
+                        */
+                       if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
+                               /*
+                                * ALLOW: allowed by +group
+                                */
+                               return (0);
+                       } else {
+                               if ((mode_req & mode_world) != mode_req) {
+                                       /*
+                                        * DENY: both -group & world would deny
+                                        */
+                                       return (EACCES);
+                               } else {
+                                       /*
+                                        * ALLOW: allowed by -group and +world
+                                        */
+                                       return (0);
+                               }
+                       }
+               }
+       }
+}