]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_authorization.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / kern / kern_authorization.c
index 98ecef6fa333c895c7578dea11a8f700a6ce005d..574d4b1988bc9cd29465dd7ee934e156df5775a5 100644 (file)
@@ -1,31 +1,29 @@
 /*
 /*
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
  *
  *
- * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code 
- * as defined in and that are subject to the Apple Public Source License 
- * Version 2.0 (the 'License'). You may not use this file except in 
- * compliance with the License.  The rights granted to you under the 
- * License may not be used to create, or enable the creation or 
- * redistribution of, unlawful or unlicensed copies of an Apple operating 
- * system, or to circumvent, violate, or enable the circumvention or 
- * violation of, any terms of an Apple operating system software license 
- * agreement.
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
  *
- * Please obtain a copy of the License at 
- * http://www.opensource.apple.com/apsl/ and read it before using this 
- * file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  *
  *
- * The Original Code and all software distributed under the License are 
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
- * Please see the License for the specific language governing rights and 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
  * limitations under the License.
  *
  * limitations under the License.
  *
- * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 /*
  */
 
 /*
@@ -33,7 +31,7 @@
  */
 
 #include <sys/appleapiopts.h>
  */
 
 #include <sys/appleapiopts.h>
-#include <sys/param.h> /* XXX trim includes */
+#include <sys/param.h>  /* XXX trim includes */
 #include <sys/acct.h>
 #include <sys/systm.h>
 #include <sys/ucred.h>
 #include <sys/acct.h>
 #include <sys/systm.h>
 #include <sys/ucred.h>
@@ -45,7 +43,7 @@
 #include <sys/kauth.h>
 #include <sys/stat.h>
 
 #include <sys/kauth.h>
 #include <sys/stat.h>
 
-#include <bsm/audit_kernel.h>
+#include <security/audit/audit.h>
 
 #include <sys/mount.h>
 #include <sys/sysproto.h>
 
 #include <sys/mount.h>
 #include <sys/sysproto.h>
@@ -61,8 +59,8 @@
 
 lck_grp_t *kauth_lck_grp;
 static lck_mtx_t *kauth_scope_mtx;
 
 lck_grp_t *kauth_lck_grp;
 static lck_mtx_t *kauth_scope_mtx;
-#define KAUTH_SCOPELOCK()      lck_mtx_lock(kauth_scope_mtx);
-#define KAUTH_SCOPEUNLOCK()    lck_mtx_unlock(kauth_scope_mtx);
+#define KAUTH_SCOPELOCK()       lck_mtx_lock(kauth_scope_mtx);
+#define KAUTH_SCOPEUNLOCK()     lck_mtx_unlock(kauth_scope_mtx);
 
 /*
  * We support listeners for scopes that have not been registered yet.
 
 /*
  * We support listeners for scopes that have not been registered yet.
@@ -71,72 +69,72 @@ static lck_mtx_t *kauth_scope_mtx;
  * remove it from kauth_dangling_listeners and add it to the active scope.
  */
 struct kauth_listener {
  * remove it from kauth_dangling_listeners and add it to the active scope.
  */
 struct kauth_listener {
-       TAILQ_ENTRY(kauth_listener)     kl_link;
-       const char *                            kl_identifier;
-       kauth_scope_callback_t          kl_callback;
-       void *                                          kl_idata;
+       TAILQ_ENTRY(kauth_listener)     kl_link;
+       const char *                            kl_identifier;
+       kauth_scope_callback_t          kl_callback;
+       void *                                          kl_idata;
 };
 
 /* XXX - kauth_todo - there is a race if a scope listener is removed while we
  * we are in the kauth_authorize_action code path.  We intentionally do not take
 };
 
 /* XXX - kauth_todo - there is a race if a scope listener is removed while we
  * we are in the kauth_authorize_action code path.  We intentionally do not take
- * a scope lock in order to get the best possible performance.  we will fix this 
- * post Tiger. 
- * Until the race is fixed our kext clients are responsible for all active 
+ * a scope lock in order to get the best possible performance.  we will fix this
+ * post Tiger.
+ * Until the race is fixed our kext clients are responsible for all active
  * requests that may be in their callback code or on the way to their callback
  * code before they free kauth_listener.kl_callback or kauth_listener.kl_idata.
  * requests that may be in their callback code or on the way to their callback
  * code before they free kauth_listener.kl_callback or kauth_listener.kl_idata.
- * We keep copies of these in our kauth_local_listener in an attempt to limit 
- * our expose to unlisten race. 
+ * We keep copies of these in our kauth_local_listener in an attempt to limit
+ * our expose to unlisten race.
  */
 struct kauth_local_listener {
  */
 struct kauth_local_listener {
-       kauth_listener_t                        kll_listenerp;
-       kauth_scope_callback_t          kll_callback;
-       void *                                          kll_idata;
+       kauth_listener_t                        kll_listenerp;
+       kauth_scope_callback_t          kll_callback;
+       void *                                          kll_idata;
 };
 typedef struct kauth_local_listener *kauth_local_listener_t;
 
 };
 typedef struct kauth_local_listener *kauth_local_listener_t;
 
-static TAILQ_HEAD(,kauth_listener) kauth_dangling_listeners;
+static TAILQ_HEAD(, kauth_listener) kauth_dangling_listeners;
 
 
-/* 
+/*
  * Scope listeners need to be reworked to be dynamic.
  * Scope listeners need to be reworked to be dynamic.
- * We intentionally used a static table to avoid locking issues with linked 
+ * We intentionally used a static table to avoid locking issues with linked
  * lists.  The listeners may be called quite often.
  * XXX - kauth_todo
  */
 #define KAUTH_SCOPE_MAX_LISTENERS  15
 
 struct kauth_scope {
  * lists.  The listeners may be called quite often.
  * XXX - kauth_todo
  */
 #define KAUTH_SCOPE_MAX_LISTENERS  15
 
 struct kauth_scope {
-       TAILQ_ENTRY(kauth_scope)        ks_link;
+       TAILQ_ENTRY(kauth_scope)        ks_link;
        volatile struct kauth_local_listener  ks_listeners[KAUTH_SCOPE_MAX_LISTENERS];
        volatile struct kauth_local_listener  ks_listeners[KAUTH_SCOPE_MAX_LISTENERS];
-       const char *                            ks_identifier;
-       kauth_scope_callback_t          ks_callback;
-       void *                                          ks_idata;
-       u_int                                           ks_flags;
+       const char *                            ks_identifier;
+       kauth_scope_callback_t          ks_callback;
+       void *                                          ks_idata;
+       u_int                                           ks_flags;
 };
 
 /* values for kauth_scope.ks_flags */
 };
 
 /* values for kauth_scope.ks_flags */
-#define KS_F_HAS_LISTENERS             (1 << 0)
+#define KS_F_HAS_LISTENERS              (1 << 0)
 
 
-static TAILQ_HEAD(,kauth_scope)        kauth_scopes;
+static TAILQ_HEAD(, kauth_scope) kauth_scopes;
 
 static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp);
 
 static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp);
-static void    kauth_scope_init(void);
+static void     kauth_scope_init(void);
 static kauth_scope_t kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata);
 static kauth_listener_t kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata);
 #if 0
 static kauth_scope_t kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata);
 static kauth_listener_t kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata);
 #if 0
-static int     kauth_scope_valid(kauth_scope_t scope);
+static int      kauth_scope_valid(kauth_scope_t scope);
 #endif
 
 #endif
 
-kauth_scope_t  kauth_scope_process;
-static int     kauth_authorize_process_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
+kauth_scope_t   kauth_scope_process;
+static int      kauth_authorize_process_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
     uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3);
     uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3);
-kauth_scope_t  kauth_scope_generic;
-static int     kauth_authorize_generic_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
+kauth_scope_t   kauth_scope_generic;
+static int      kauth_authorize_generic_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
     uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
     uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
-kauth_scope_t  kauth_scope_fileop;
+kauth_scope_t   kauth_scope_fileop;
 
 
-extern int             cansignal(struct proc *, kauth_cred_t, struct proc *, int);
-extern char *  get_pathbuff(void);
-extern void            release_pathbuff(char *path);
+extern int              cansignal(struct proc *, kauth_cred_t, struct proc *, int);
+extern char *   get_pathbuff(void);
+extern void             release_pathbuff(char *path);
 
 /*
  * Initialization.
 
 /*
  * Initialization.
@@ -144,7 +142,7 @@ extern void         release_pathbuff(char *path);
 void
 kauth_init(void)
 {
 void
 kauth_init(void)
 {
-       lck_grp_attr_t  *grp_attributes;
+       lck_grp_attr_t  *grp_attributes;
 
        TAILQ_INIT(&kauth_scopes);
        TAILQ_INIT(&kauth_dangling_listeners);
 
        TAILQ_INIT(&kauth_scopes);
        TAILQ_INIT(&kauth_dangling_listeners);
@@ -156,11 +154,14 @@ kauth_init(void)
 
        /* bring up kauth subsystem components */
        kauth_cred_init();
 
        /* bring up kauth subsystem components */
        kauth_cred_init();
+#if CONFIG_EXT_RESOLVER
        kauth_identity_init();
        kauth_groups_init();
        kauth_identity_init();
        kauth_groups_init();
+#endif
        kauth_scope_init();
        kauth_scope_init();
+#if CONFIG_EXT_RESOLVER
        kauth_resolver_init();
        kauth_resolver_init();
-
+#endif
        /* can't alloc locks after this */
        lck_grp_free(kauth_lck_grp);
        kauth_lck_grp = NULL;
        /* can't alloc locks after this */
        lck_grp_free(kauth_lck_grp);
        kauth_lck_grp = NULL;
@@ -182,20 +183,20 @@ kauth_scope_init(void)
 static kauth_scope_t
 kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
 static kauth_scope_t
 kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
-       kauth_scope_t   sp;
+       kauth_scope_t   sp;
 
        /*
         * Allocate and populate the scope structure.
         */
 
        /*
         * Allocate and populate the scope structure.
         */
-       MALLOC(sp, kauth_scope_t, sizeof(*sp), M_KAUTH, M_WAITOK);
-       if (sp == NULL)
-               return(NULL);
-       bzero(&sp->ks_listeners, sizeof(sp->ks_listeners));
+       MALLOC(sp, kauth_scope_t, sizeof(*sp), M_KAUTH, M_WAITOK | M_ZERO);
+       if (sp == NULL) {
+               return NULL;
+       }
        sp->ks_flags = 0;
        sp->ks_identifier = identifier;
        sp->ks_idata = idata;
        sp->ks_callback = callback;
        sp->ks_flags = 0;
        sp->ks_identifier = identifier;
        sp->ks_idata = idata;
        sp->ks_callback = callback;
-       return(sp);
+       return sp;
 }
 
 static kauth_listener_t
 }
 
 static kauth_listener_t
@@ -207,22 +208,24 @@ kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, vo
         * Allocate and populate the listener structure.
         */
        MALLOC(lsp, kauth_listener_t, sizeof(*lsp), M_KAUTH, M_WAITOK);
         * Allocate and populate the listener structure.
         */
        MALLOC(lsp, kauth_listener_t, sizeof(*lsp), M_KAUTH, M_WAITOK);
-       if (lsp == NULL)
-               return(NULL);
+       if (lsp == NULL) {
+               return NULL;
+       }
        lsp->kl_identifier = identifier;
        lsp->kl_idata = idata;
        lsp->kl_callback = callback;
        lsp->kl_identifier = identifier;
        lsp->kl_idata = idata;
        lsp->kl_callback = callback;
-       return(lsp);
+       return lsp;
 }
 
 kauth_scope_t
 kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
 }
 
 kauth_scope_t
 kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
-       kauth_scope_t           sp, tsp;
-       kauth_listener_t        klp;
+       kauth_scope_t           sp, tsp;
+       kauth_listener_t        klp;
 
 
-       if ((sp = kauth_alloc_scope(identifier, callback, idata)) == NULL)
-               return(NULL);
+       if ((sp = kauth_alloc_scope(identifier, callback, idata)) == NULL) {
+               return NULL;
+       }
 
        /*
         * Lock the list and insert.
 
        /*
         * Lock the list and insert.
@@ -230,10 +233,11 @@ kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, vo
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(tsp, &kauth_scopes, ks_link) {
                /* duplicate! */
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(tsp, &kauth_scopes, ks_link) {
                /* duplicate! */
-               if (strcmp(tsp->ks_identifier, identifier) == 0) {
+               if (strncmp(tsp->ks_identifier, identifier,
+                   strlen(tsp->ks_identifier) + 1) == 0) {
                        KAUTH_SCOPEUNLOCK();
                        FREE(sp, M_KAUTH);
                        KAUTH_SCOPEUNLOCK();
                        FREE(sp, M_KAUTH);
-                       return(NULL);
+                       return NULL;
                }
        }
        TAILQ_INSERT_TAIL(&kauth_scopes, sp, ks_link);
                }
        }
        TAILQ_INSERT_TAIL(&kauth_scopes, sp, ks_link);
@@ -246,14 +250,14 @@ kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, vo
         */
 restart:
        TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
         */
 restart:
        TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
-               if (strcmp(klp->kl_identifier, sp->ks_identifier) == 0) {
+               if (strncmp(klp->kl_identifier, sp->ks_identifier,
+                   strlen(klp->kl_identifier) + 1) == 0) {
                        /* found a match on the dangling listener list.  add it to the
                         * the active scope.
                         */
                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
                                TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
                        /* found a match on the dangling listener list.  add it to the
                         * the active scope.
                         */
                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
                                TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
-                       }
-                       else {
+                       } else {
 #if 0
                                printf("%s - failed to add listener to scope \"%s\" \n", __FUNCTION__, sp->ks_identifier);
 #endif
 #if 0
                                printf("%s - failed to add listener to scope \"%s\" \n", __FUNCTION__, sp->ks_identifier);
 #endif
@@ -264,7 +268,7 @@ restart:
        }
 
        KAUTH_SCOPEUNLOCK();
        }
 
        KAUTH_SCOPEUNLOCK();
-       return(sp);
+       return sp;
 }
 
 
 }
 
 
@@ -272,18 +276,18 @@ restart:
 void
 kauth_deregister_scope(kauth_scope_t scope)
 {
 void
 kauth_deregister_scope(kauth_scope_t scope)
 {
-       int             i;
+       int             i;
 
        KAUTH_SCOPELOCK();
 
        TAILQ_REMOVE(&kauth_scopes, scope, ks_link);
 
        KAUTH_SCOPELOCK();
 
        TAILQ_REMOVE(&kauth_scopes, scope, ks_link);
-       
+
        /* relocate listeners back to the waiting list */
        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
                if (scope->ks_listeners[i].kll_listenerp != NULL) {
                        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, scope->ks_listeners[i].kll_listenerp, kl_link);
                        scope->ks_listeners[i].kll_listenerp = NULL;
        /* relocate listeners back to the waiting list */
        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
                if (scope->ks_listeners[i].kll_listenerp != NULL) {
                        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, scope->ks_listeners[i].kll_listenerp, kl_link);
                        scope->ks_listeners[i].kll_listenerp = NULL;
-                       /* 
+                       /*
                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
                         * kll_idata here.  they are part of our scope unlisten race hack
                         */
                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
                         * kll_idata here.  they are part of our scope unlisten race hack
                         */
@@ -291,7 +295,7 @@ kauth_deregister_scope(kauth_scope_t scope)
        }
        KAUTH_SCOPEUNLOCK();
        FREE(scope, M_KAUTH);
        }
        KAUTH_SCOPEUNLOCK();
        FREE(scope, M_KAUTH);
-       
+
        return;
 }
 
        return;
 }
 
@@ -299,44 +303,46 @@ kauth_listener_t
 kauth_listen_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
        kauth_listener_t klp;
 kauth_listen_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 {
        kauth_listener_t klp;
-       kauth_scope_t   sp;
+       kauth_scope_t   sp;
 
 
-       if ((klp = kauth_alloc_listener(identifier, callback, idata)) == NULL)
-               return(NULL);
+       if ((klp = kauth_alloc_listener(identifier, callback, idata)) == NULL) {
+               return NULL;
+       }
 
        /*
         * Lock the scope list and check to see whether this scope already exists.
         */
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
 
        /*
         * Lock the scope list and check to see whether this scope already exists.
         */
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
-               if (strcmp(sp->ks_identifier, identifier) == 0) {
+               if (strncmp(sp->ks_identifier, identifier,
+                   strlen(sp->ks_identifier) + 1) == 0) {
                        /* scope exists, add it to scope listener table */
                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
                                KAUTH_SCOPEUNLOCK();
                        /* scope exists, add it to scope listener table */
                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
                                KAUTH_SCOPEUNLOCK();
-                               return(klp);
+                               return klp;
                        }
                        /* table already full */
                        KAUTH_SCOPEUNLOCK();
                        FREE(klp, M_KAUTH);
                        }
                        /* table already full */
                        KAUTH_SCOPEUNLOCK();
                        FREE(klp, M_KAUTH);
-                       return(NULL);
+                       return NULL;
                }
        }
                }
        }
-       
+
        /* scope doesn't exist, put on waiting list. */
        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, klp, kl_link);
 
        KAUTH_SCOPEUNLOCK();
 
        /* scope doesn't exist, put on waiting list. */
        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, klp, kl_link);
 
        KAUTH_SCOPEUNLOCK();
 
-       return(klp);
+       return klp;
 }
 
 void
 kauth_unlisten_scope(kauth_listener_t listener)
 {
 }
 
 void
 kauth_unlisten_scope(kauth_listener_t listener)
 {
-       kauth_scope_t           sp;
-       kauth_listener_t        klp;
-       int                                     i, listener_count, do_free;
-       
+       kauth_scope_t           sp;
+       kauth_listener_t        klp;
+       int                                     i, listener_count, do_free;
+
        KAUTH_SCOPELOCK();
 
        /* search the active scope for this listener */
        KAUTH_SCOPELOCK();
 
        /* search the active scope for this listener */
@@ -348,12 +354,11 @@ kauth_unlisten_scope(kauth_listener_t listener)
                                if (sp->ks_listeners[i].kll_listenerp == listener) {
                                        sp->ks_listeners[i].kll_listenerp = NULL;
                                        do_free = 1;
                                if (sp->ks_listeners[i].kll_listenerp == listener) {
                                        sp->ks_listeners[i].kll_listenerp = NULL;
                                        do_free = 1;
-                                       /* 
+                                       /*
                                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
                                         * kll_idata here.  they are part of our scope unlisten race hack
                                         */
                                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
                                         * kll_idata here.  they are part of our scope unlisten race hack
                                         */
-                               }
-                               else if (sp->ks_listeners[i].kll_listenerp != NULL) {
+                               } else if (sp->ks_listeners[i].kll_listenerp != NULL) {
                                        listener_count++;
                                }
                        }
                                        listener_count++;
                                }
                        }
@@ -384,6 +389,12 @@ kauth_unlisten_scope(kauth_listener_t listener)
 
 /*
  * Authorization requests.
 
 /*
  * Authorization requests.
+ *
+ * Returns:    0                       Success
+ *             EPERM                   Operation not permitted
+ *
+ * Imputed:    *arg3, modified         Callback return - depends on callback
+ *                                     modification of *arg3, if any
  */
 int
 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_action_t action,
  */
 int
 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_action_t action,
@@ -392,36 +403,39 @@ kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_actio
        int result, ret, i;
 
        /* ask the scope */
        int result, ret, i;
 
        /* ask the scope */
-       if (scope->ks_callback != NULL)
+       if (scope->ks_callback != NULL) {
                result = scope->ks_callback(credential, scope->ks_idata, action, arg0, arg1, arg2, arg3);
                result = scope->ks_callback(credential, scope->ks_idata, action, arg0, arg1, arg2, arg3);
-       else
+       } else {
                result = KAUTH_RESULT_DEFER;
                result = KAUTH_RESULT_DEFER;
+       }
 
        /* check with listeners */
        if ((scope->ks_flags & KS_F_HAS_LISTENERS) != 0) {
                for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
 
        /* check with listeners */
        if ((scope->ks_flags & KS_F_HAS_LISTENERS) != 0) {
                for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
-                       /* XXX - kauth_todo - there is a race here if listener is removed - we will fix this post Tiger. 
+                       /* XXX - kauth_todo - there is a race here if listener is removed - we will fix this post Tiger.
                         * Until the race is fixed our kext clients are responsible for all active requests that may
                         * be in their callbacks or on the way to their callbacks before they free kl_callback or kl_idata.
                         * Until the race is fixed our kext clients are responsible for all active requests that may
                         * be in their callbacks or on the way to their callbacks before they free kl_callback or kl_idata.
-                        * We keep copies of these in our kauth_local_listener in an attempt to limit our expose to 
-                        * unlisten race. 
+                        * We keep copies of these in our kauth_local_listener in an attempt to limit our expose to
+                        * unlisten race.
                         */
                         */
-                       if (scope->ks_listeners[i].kll_listenerp == NULL || 
-                               scope->ks_listeners[i].kll_callback == NULL) 
+                       if (scope->ks_listeners[i].kll_listenerp == NULL ||
+                           scope->ks_listeners[i].kll_callback == NULL) {
                                continue;
                                continue;
+                       }
 
                        ret = scope->ks_listeners[i].kll_callback(
 
                        ret = scope->ks_listeners[i].kll_callback(
-                                       credential, scope->ks_listeners[i].kll_idata, 
-                                       action, arg0, arg1, arg2, arg3);
+                               credential, scope->ks_listeners[i].kll_idata,
+                               action, arg0, arg1, arg2, arg3);
                        if ((ret == KAUTH_RESULT_DENY) ||
                        if ((ret == KAUTH_RESULT_DENY) ||
-                               (result == KAUTH_RESULT_DEFER))
+                           (result == KAUTH_RESULT_DEFER)) {
                                result = ret;
                                result = ret;
+                       }
                }
        }
 
        /* we need an explicit allow, or the auth fails */
                }
        }
 
        /* we need an explicit allow, or the auth fails */
-       /* XXX need a mechanism for auth failure to be signalled vs. denial */
-       return(result == KAUTH_RESULT_ALLOW ? 0 : EPERM);
+       /* XXX need a mechanism for auth failure to be signalled vs. denial */
+       return result == KAUTH_RESULT_ALLOW ? 0 : EPERM;
 }
 
 /*
 }
 
 /*
@@ -429,10 +443,9 @@ kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_actio
  */
 int
 kauth_authorize_allow(__unused kauth_cred_t credential, __unused void *idata, __unused kauth_action_t action,
  */
 int
 kauth_authorize_allow(__unused kauth_cred_t credential, __unused void *idata, __unused kauth_action_t action,
-     __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
+    __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 {
 {
-
-       return(KAUTH_RESULT_ALLOW);
+       return KAUTH_RESULT_ALLOW;
 }
 
 #if 0
 }
 
 #if 0
@@ -442,15 +455,16 @@ kauth_authorize_allow(__unused kauth_cred_t credential, __unused void *idata, __
 static int
 kauth_scope_valid(kauth_scope_t scope)
 {
 static int
 kauth_scope_valid(kauth_scope_t scope)
 {
-       kauth_scope_t   sp;
+       kauth_scope_t   sp;
 
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
 
        KAUTH_SCOPELOCK();
        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
-               if (sp == scope)
+               if (sp == scope) {
                        break;
                        break;
+               }
        }
        KAUTH_SCOPEUNLOCK();
        }
        KAUTH_SCOPEUNLOCK();
-       return((sp == NULL) ? 0 : 1);
+       return (sp == NULL) ? 0 : 1;
 }
 #endif
 
 }
 #endif
 
@@ -461,35 +475,37 @@ kauth_scope_valid(kauth_scope_t scope)
 int
 kauth_authorize_process(kauth_cred_t credential, kauth_action_t action, struct proc *process, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
 {
 int
 kauth_authorize_process(kauth_cred_t credential, kauth_action_t action, struct proc *process, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
 {
-       return(kauth_authorize_action(kauth_scope_process, credential, action, (uintptr_t)process, arg1, arg2, arg3));
+       return kauth_authorize_action(kauth_scope_process, credential, action, (uintptr_t)process, arg1, arg2, arg3);
 }
 
 static int
 kauth_authorize_process_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
     uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 {
 }
 
 static int
 kauth_authorize_process_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
     uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 {
-       switch(action) {
+       switch (action) {
        case KAUTH_PROCESS_CANSIGNAL:
                panic("KAUTH_PROCESS_CANSIGNAL not implemented");
                /* XXX credential wrong here */
                /* arg0 - process to signal
                 * arg1 - signal to send the process
                 */
        case KAUTH_PROCESS_CANSIGNAL:
                panic("KAUTH_PROCESS_CANSIGNAL not implemented");
                /* XXX credential wrong here */
                /* arg0 - process to signal
                 * arg1 - signal to send the process
                 */
-               if (cansignal(current_proc(), credential, (struct proc *)arg0, (int)arg1))
-                       return(KAUTH_RESULT_ALLOW);
+               if (cansignal(current_proc(), credential, (struct proc *)arg0, (int)arg1)) {
+                       return KAUTH_RESULT_ALLOW;
+               }
                break;
        case KAUTH_PROCESS_CANTRACE:
                break;
        case KAUTH_PROCESS_CANTRACE:
-               /* current_proc() - process that will do the tracing 
-                * arg0 - process to be traced 
-                * arg1 - pointer to int - reason (errno) for denial 
+               /* current_proc() - process that will do the tracing
+                * arg0 - process to be traced
+                * arg1 - pointer to int - reason (errno) for denial
                 */
                 */
-               if (cantrace(current_proc(), credential, (proc_t)arg0, (int *)arg1))
-                       return(KAUTH_RESULT_ALLOW);
+               if (cantrace(current_proc(), credential, (proc_t)arg0, (int *)arg1)) {
+                       return KAUTH_RESULT_ALLOW;
+               }
                break;
        }
 
        /* no explicit result, so defer to others in the chain */
                break;
        }
 
        /* no explicit result, so defer to others in the chain */
-       return(KAUTH_RESULT_DEFER);
+       return KAUTH_RESULT_DEFER;
 }
 
 /*
 }
 
 /*
@@ -503,6 +519,10 @@ kauth_authorize_process_callback(kauth_cred_t credential, __unused void *idata,
  *             arg0 is pointer to vnode (vnode *) for file to be closed.
  *             arg1 is pointer to path (char *) of file to be closed.
  *             arg2 is close flags.
  *             arg0 is pointer to vnode (vnode *) for file to be closed.
  *             arg1 is pointer to path (char *) of file to be closed.
  *             arg2 is close flags.
+ * arguments passed to KAUTH_FILEOP_WILL_RENAME listeners
+ *             arg0 is pointer to vnode (vnode *) of the file being renamed
+ *             arg1 is pointer to the "from" path (char *)
+ *             arg2 is pointer to the "to" path (char *)
  * arguments passed to KAUTH_FILEOP_RENAME listeners
  *             arg0 is pointer to "from" path (char *).
  *             arg1 is pointer to "to" path (char *).
  * arguments passed to KAUTH_FILEOP_RENAME listeners
  *             arg0 is pointer to "from" path (char *).
  *             arg1 is pointer to "to" path (char *).
@@ -522,46 +542,56 @@ kauth_authorize_fileop_has_listeners(void)
         * otherwize return 0
         */
        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) != 0) {
         * otherwize return 0
         */
        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) != 0) {
-               return(1);
+               return 1;
        }
        }
-       return (0);
+       return 0;
 }
 
 int
 kauth_authorize_fileop(kauth_cred_t credential, kauth_action_t action, uintptr_t arg0, uintptr_t arg1)
 {
 }
 
 int
 kauth_authorize_fileop(kauth_cred_t credential, kauth_action_t action, uintptr_t arg0, uintptr_t arg1)
 {
-       char            *namep = NULL;
-       int                     name_len;
-       uintptr_t       arg2 = 0;
-       
-       /* we do not have a primary handler for the fileop scope so bail out if 
+       char            *namep = NULL;
+       int                     name_len;
+       uintptr_t       arg2 = 0;
+
+       /* we do not have a primary handler for the fileop scope so bail out if
         * there are no listeners.
         */
        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) == 0) {
         * there are no listeners.
         */
        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) == 0) {
-               return(0);
+               return 0;
        }
 
        }
 
-       if (action == KAUTH_FILEOP_OPEN || action == KAUTH_FILEOP_CLOSE || action == KAUTH_FILEOP_EXEC) {
+       if (action == KAUTH_FILEOP_OPEN ||
+           action == KAUTH_FILEOP_CLOSE ||
+           action == KAUTH_FILEOP_EXEC ||
+           action == KAUTH_FILEOP_WILL_RENAME) {
                /* get path to the given vnode as a convenience to our listeners.
                 */
                namep = get_pathbuff();
                name_len = MAXPATHLEN;
                if (vn_getpath((vnode_t)arg0, namep, &name_len) != 0) {
                        release_pathbuff(namep);
                /* get path to the given vnode as a convenience to our listeners.
                 */
                namep = get_pathbuff();
                name_len = MAXPATHLEN;
                if (vn_getpath((vnode_t)arg0, namep, &name_len) != 0) {
                        release_pathbuff(namep);
-                       return(0);
+                       return 0;
                }
                }
-               if (action == KAUTH_FILEOP_CLOSE) {
-                       arg2 = arg1;  /* close has some flags that come in via arg1 */
+               if (action == KAUTH_FILEOP_CLOSE ||
+                   action == KAUTH_FILEOP_WILL_RENAME) {
+                       /*
+                        * - Close has some flags that come in via arg1.
+                        * - Will-rename wants to pass the vnode and
+                        *   both paths to the listeners ("to" path
+                        *   starts in arg1, moves to arg2).
+                        */
+                       arg2 = arg1;
                }
                arg1 = (uintptr_t)namep;
                }
                arg1 = (uintptr_t)namep;
-       }       
+       }
        kauth_authorize_action(kauth_scope_fileop, credential, action, arg0, arg1, arg2, 0);
        kauth_authorize_action(kauth_scope_fileop, credential, action, arg0, arg1, arg2, 0);
-       
+
        if (namep != NULL) {
                release_pathbuff(namep);
        }
        if (namep != NULL) {
                release_pathbuff(namep);
        }
-       
-       return(0);
+
+       return 0;
 }
 
 /*
 }
 
 /*
@@ -571,27 +601,26 @@ kauth_authorize_fileop(kauth_cred_t credential, kauth_action_t action, uintptr_t
 int
 kauth_authorize_generic(kauth_cred_t credential, kauth_action_t action)
 {
 int
 kauth_authorize_generic(kauth_cred_t credential, kauth_action_t action)
 {
-       if (credential == NULL)
+       if (credential == NULL) {
                panic("auth against NULL credential");
                panic("auth against NULL credential");
+       }
 
 
-       return(kauth_authorize_action(kauth_scope_generic, credential, action, 0, 0, 0, 0));
-               
+       return kauth_authorize_action(kauth_scope_generic, credential, action, 0, 0, 0, 0);
 }
 
 static int
 kauth_authorize_generic_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
 }
 
 static int
 kauth_authorize_generic_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
-     __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
+    __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 {
 {
-       switch(action) {
+       switch (action) {
        case KAUTH_GENERIC_ISSUSER:
                /* XXX == 0 ? */
        case KAUTH_GENERIC_ISSUSER:
                /* XXX == 0 ? */
-               return((kauth_cred_getuid(credential) == 0) ?
-                   KAUTH_RESULT_ALLOW : KAUTH_RESULT_DENY);
-               break;
+               return (kauth_cred_getuid(credential) == 0) ?
+                      KAUTH_RESULT_ALLOW : KAUTH_RESULT_DENY;
        }
 
        /* no explicit result, so defer to others in the chain */
        }
 
        /* no explicit result, so defer to others in the chain */
-       return(KAUTH_RESULT_DEFER);
+       return KAUTH_RESULT_DEFER;
 }
 
 /*
 }
 
 /*
@@ -606,7 +635,7 @@ kauth_authorize_generic_callback(kauth_cred_t credential, __unused void *idata,
 int
 kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
 {
 int
 kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
 {
-       int applies, error, i;
+       int applies, error, i, gotguid;
        kauth_ace_t ace;
        guid_t guid;
        uint32_t rights;
        kauth_ace_t ace;
        guid_t guid;
        uint32_t rights;
@@ -615,71 +644,85 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
        /* always allowed to do nothing */
        if (eval->ae_requested == 0) {
                eval->ae_result = KAUTH_RESULT_ALLOW;
        /* always allowed to do nothing */
        if (eval->ae_requested == 0) {
                eval->ae_result = KAUTH_RESULT_ALLOW;
-               return(0);
+               return 0;
        }
 
        eval->ae_residual = eval->ae_requested;
        }
 
        eval->ae_residual = eval->ae_requested;
+       eval->ae_found_deny = FALSE;
 
        /*
         * Get our guid for comparison purposes.
         */
        if ((error = kauth_cred_getguid(cred, &guid)) != 0) {
 
        /*
         * Get our guid for comparison purposes.
         */
        if ((error = kauth_cred_getguid(cred, &guid)) != 0) {
-               eval->ae_result = KAUTH_RESULT_DENY;
-               KAUTH_DEBUG("    ACL - can't get credential GUID (%d), ACL denied", error);
-               return(error);
+               KAUTH_DEBUG("    ACL - can't get credential GUID (%d)", error);
+               error = 0;
+               gotguid = 0;
+       } else {
+               gotguid = 1;
        }
 
        KAUTH_DEBUG("    ACL - %d entries, initial residual %x", eval->ae_count, eval->ae_residual);
        for (i = 0, ace = eval->ae_acl; i < eval->ae_count; i++, ace++) {
        }
 
        KAUTH_DEBUG("    ACL - %d entries, initial residual %x", eval->ae_count, eval->ae_residual);
        for (i = 0, ace = eval->ae_acl; i < eval->ae_count; i++, ace++) {
-
                /*
                 * Skip inherit-only entries.
                 */
                /*
                 * Skip inherit-only entries.
                 */
-               if (ace->ace_flags & KAUTH_ACE_ONLY_INHERIT)
+               if (ace->ace_flags & KAUTH_ACE_ONLY_INHERIT) {
                        continue;
                        continue;
+               }
 
                /*
                 * Expand generic rights, if appropriate.
                 */
                rights = ace->ace_rights;
 
                /*
                 * Expand generic rights, if appropriate.
                 */
                rights = ace->ace_rights;
-               if (rights & KAUTH_ACE_GENERIC_ALL)
+               if (rights & KAUTH_ACE_GENERIC_ALL) {
                        rights |= eval->ae_exp_gall;
                        rights |= eval->ae_exp_gall;
-               if (rights & KAUTH_ACE_GENERIC_READ)
+               }
+               if (rights & KAUTH_ACE_GENERIC_READ) {
                        rights |= eval->ae_exp_gread;
                        rights |= eval->ae_exp_gread;
-               if (rights & KAUTH_ACE_GENERIC_WRITE)
+               }
+               if (rights & KAUTH_ACE_GENERIC_WRITE) {
                        rights |= eval->ae_exp_gwrite;
                        rights |= eval->ae_exp_gwrite;
-               if (rights & KAUTH_ACE_GENERIC_EXECUTE)
+               }
+               if (rights & KAUTH_ACE_GENERIC_EXECUTE) {
                        rights |= eval->ae_exp_gexec;
                        rights |= eval->ae_exp_gexec;
+               }
 
                /*
                 * Determine whether this entry applies to the current request.  This
                 * saves us checking the GUID if the entry has nothing to do with what
                 * we're currently doing.
                 */
 
                /*
                 * Determine whether this entry applies to the current request.  This
                 * saves us checking the GUID if the entry has nothing to do with what
                 * we're currently doing.
                 */
-               switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
+               switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
                case KAUTH_ACE_PERMIT:
                case KAUTH_ACE_PERMIT:
-                       if (!(eval->ae_residual & rights))
+                       if (!(eval->ae_residual & rights)) {
                                continue;
                                continue;
+                       }
                        break;
                case KAUTH_ACE_DENY:
                        break;
                case KAUTH_ACE_DENY:
-                       if (!(eval->ae_requested & rights))
+                       if (!(eval->ae_requested & rights)) {
                                continue;
                                continue;
+                       }
+                       eval->ae_found_deny = TRUE;
                        break;
                default:
                        /* we don't recognise this ACE, skip it */
                        continue;
                }
                        break;
                default:
                        /* we don't recognise this ACE, skip it */
                        continue;
                }
-               
+
                /*
                 * Verify whether this entry applies to the credential.
                 */
                wkguid = kauth_wellknown_guid(&ace->ace_applicable);
                /*
                 * Verify whether this entry applies to the credential.
                 */
                wkguid = kauth_wellknown_guid(&ace->ace_applicable);
-               switch(wkguid) {
+               switch (wkguid) {
                case KAUTH_WKG_OWNER:
                        applies = eval->ae_options & KAUTH_AEVAL_IS_OWNER;
                        break;
                case KAUTH_WKG_GROUP:
                case KAUTH_WKG_OWNER:
                        applies = eval->ae_options & KAUTH_AEVAL_IS_OWNER;
                        break;
                case KAUTH_WKG_GROUP:
-                       applies = eval->ae_options & KAUTH_AEVAL_IN_GROUP;
+                       if (!gotguid || (eval->ae_options & KAUTH_AEVAL_IN_GROUP_UNKNOWN)) {
+                               applies = ((ace->ace_flags & KAUTH_ACE_KINDMASK) == KAUTH_ACE_DENY);
+                       } else {
+                               applies = eval->ae_options & KAUTH_AEVAL_IN_GROUP;
+                       }
                        break;
                /* we short-circuit these here rather than wasting time calling the group membership code */
                case KAUTH_WKG_EVERYBODY:
                        break;
                /* we short-circuit these here rather than wasting time calling the group membership code */
                case KAUTH_WKG_EVERYBODY:
@@ -691,12 +734,12 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
 
                default:
                        /* check to see whether it's exactly us, or a group we are a member of */
 
                default:
                        /* check to see whether it's exactly us, or a group we are a member of */
-                       applies = kauth_guid_equal(&guid, &ace->ace_applicable);
+                       applies = !gotguid ? 0 : kauth_guid_equal(&guid, &ace->ace_applicable);
                        KAUTH_DEBUG("    ACL - ACE applicable " K_UUID_FMT " caller " K_UUID_FMT " %smatched",
                            K_UUID_ARG(ace->ace_applicable), K_UUID_ARG(guid), applies ? "" : "not ");
                        KAUTH_DEBUG("    ACL - ACE applicable " K_UUID_FMT " caller " K_UUID_FMT " %smatched",
                            K_UUID_ARG(ace->ace_applicable), K_UUID_ARG(guid), applies ? "" : "not ");
-               
+
                        if (!applies) {
                        if (!applies) {
-                               error = kauth_cred_ismember_guid(cred, &ace->ace_applicable, &applies);
+                               error = !gotguid ? ENOENT : kauth_cred_ismember_guid(cred, &ace->ace_applicable, &applies);
                                /*
                                 * If we can't resolve group membership, we have to limit misbehaviour.
                                 * If the ACE is an 'allow' ACE, assume the cred is not a member (avoid
                                /*
                                 * If we can't resolve group membership, we have to limit misbehaviour.
                                 * If the ACE is an 'allow' ACE, assume the cred is not a member (avoid
@@ -705,7 +748,7 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
                                 */
                                if (error != 0) {
                                        KAUTH_DEBUG("    ACL[%d] - can't get membership, making pessimistic assumption", i);
                                 */
                                if (error != 0) {
                                        KAUTH_DEBUG("    ACL[%d] - can't get membership, making pessimistic assumption", i);
-                                       switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
+                                       switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
                                        case KAUTH_ACE_PERMIT:
                                                applies = 0;
                                                break;
                                        case KAUTH_ACE_PERMIT:
                                                applies = 0;
                                                break;
@@ -720,13 +763,14 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
                                KAUTH_DEBUG("    ACL - entry matches caller");
                        }
                }
                                KAUTH_DEBUG("    ACL - entry matches caller");
                        }
                }
-               if (!applies)
+               if (!applies) {
                        continue;
                        continue;
+               }
 
                /*
                 * Apply ACE to outstanding rights.
                 */
 
                /*
                 * Apply ACE to outstanding rights.
                 */
-               switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
+               switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
                case KAUTH_ACE_PERMIT:
                        /* satisfy any rights that this ACE grants */
                        eval->ae_residual = eval->ae_residual & ~rights;
                case KAUTH_ACE_PERMIT:
                        /* satisfy any rights that this ACE grants */
                        eval->ae_residual = eval->ae_residual & ~rights;
@@ -734,7 +778,7 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
                        /* all rights satisfied? */
                        if (eval->ae_residual == 0) {
                                eval->ae_result = KAUTH_RESULT_ALLOW;
                        /* all rights satisfied? */
                        if (eval->ae_residual == 0) {
                                eval->ae_result = KAUTH_RESULT_ALLOW;
-                               return(0);
+                               return 0;
                        }
                        break;
                case KAUTH_ACE_DENY:
                        }
                        break;
                case KAUTH_ACE_DENY:
@@ -742,7 +786,7 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
                        if (eval->ae_requested & rights) {
                                KAUTH_DEBUG("    ACL[%d] - denying based on %x", i, rights);
                                eval->ae_result = KAUTH_RESULT_DENY;
                        if (eval->ae_requested & rights) {
                                KAUTH_DEBUG("    ACL[%d] - denying based on %x", i, rights);
                                eval->ae_result = KAUTH_RESULT_DENY;
-                               return(0);
+                               return 0;
                        }
                        break;
                default:
                        }
                        break;
                default:
@@ -752,7 +796,7 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
        }
        /* if not permitted, defer to other modes of authorisation */
        eval->ae_result = KAUTH_RESULT_DEFER;
        }
        /* if not permitted, defer to other modes of authorisation */
        eval->ae_result = KAUTH_RESULT_DEFER;
-       return(0);
+       return 0;
 }
 
 /*
 }
 
 /*
@@ -767,50 +811,74 @@ kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
 int
 kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int isdir, vfs_context_t ctx)
 {
 int
 kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int isdir, vfs_context_t ctx)
 {
-       int     entries, error, index;
+       int     entries, error, index;
        unsigned int i;
        struct vnode_attr dva;
        kauth_acl_t inherit, result;
 
        /*
        unsigned int i;
        struct vnode_attr dva;
        kauth_acl_t inherit, result;
 
        /*
-        * Fetch the ACL from the directory.  This should never fail.  Note that we don't
-        * manage inheritance when the remote server is doing authorization; we just
-        * want to compose the umask-ACL and any initial ACL.
+        * Fetch the ACL from the directory.  This should never fail.
+        * Note that we don't manage inheritance when the remote server is
+        * doing authorization, since this means server enforcement of
+        * inheritance semantics; we just want to compose the initial
+        * ACL and any inherited ACE entries from the container object.
+        *
+        * XXX TODO: <rdar://3634665> wants a "umask ACL" from the process.
         */
        inherit = NULL;
         */
        inherit = NULL;
-       if ((dvp != NULL) && !vfs_authopaque(vnode_mount(dvp))) {
+       /*
+        * If there is no initial ACL, or there is, and the initial ACLs
+        * flags do not request "no inheritance", then we inherit.  This allows
+        * initial object creation via open_extended() and mkdir_extended()
+        * to reject inheritance for themselves and for inferior nodes by
+        * specifying a non-NULL inital ACL which has the KAUTH_ACL_NO_INHERIT
+        * flag set in the flags field.
+        */
+       if ((initial == NULL || !(initial->acl_flags & KAUTH_ACL_NO_INHERIT)) &&
+           (dvp != NULL) && !vfs_authopaque(vnode_mount(dvp))) {
                VATTR_INIT(&dva);
                VATTR_WANTED(&dva, va_acl);
                if ((error = vnode_getattr(dvp, &dva, ctx)) != 0) {
                        KAUTH_DEBUG("    ERROR - could not get parent directory ACL for inheritance");
                VATTR_INIT(&dva);
                VATTR_WANTED(&dva, va_acl);
                if ((error = vnode_getattr(dvp, &dva, ctx)) != 0) {
                        KAUTH_DEBUG("    ERROR - could not get parent directory ACL for inheritance");
-                       return(error);
+                       return error;
                }
                }
-               if (VATTR_IS_SUPPORTED(&dva, va_acl))
+               if (VATTR_IS_SUPPORTED(&dva, va_acl)) {
                        inherit = dva.va_acl;
                        inherit = dva.va_acl;
+               }
        }
 
        /*
        }
 
        /*
-        * Compute the number of entries in the result ACL by scanning the input lists.
+        * Compute the number of entries in the result ACL by scanning the
+        * input lists.
         */
        entries = 0;
        if (inherit != NULL) {
                for (i = 0; i < inherit->acl_entrycount; i++) {
         */
        entries = 0;
        if (inherit != NULL) {
                for (i = 0; i < inherit->acl_entrycount; i++) {
-                       if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT))
+                       if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
                                entries++;
                                entries++;
+                       }
                }
        }
 
        if (initial == NULL) {
                }
        }
 
        if (initial == NULL) {
-               /* XXX 3634665 TODO: fetch umask ACL from the process, set in initial */
+               /*
+                * XXX 3634665 TODO: if the initial ACL is not specfied by
+                * XXX the caller, fetch the umask ACL from the process,
+                * and use it in place of "initial".
+                */
        }
 
        if (initial != NULL) {
        }
 
        if (initial != NULL) {
-               entries += initial->acl_entrycount;
+               if (initial->acl_entrycount != KAUTH_FILESEC_NOACL) {
+                       entries += initial->acl_entrycount;
+               } else {
+                       initial = NULL;
+               }
        }
 
        /*
         * If there is no initial ACL, and no inheritable entries, the
        }
 
        /*
         * If there is no initial ACL, and no inheritable entries, the
-        * object should have no ACL at all.
+        * object should be created with no ACL at all.
         * Note that this differs from the case where the initial ACL
         * is empty, in which case the object must also have an empty ACL.
         */
         * Note that this differs from the case where the initial ACL
         * is empty, in which case the object must also have an empty ACL.
         */
@@ -819,37 +887,54 @@ kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int is
                error = 0;
                goto out;
        }
                error = 0;
                goto out;
        }
-       
+
        /*
         * Allocate the result buffer.
         */
        if ((result = kauth_acl_alloc(entries)) == NULL) {
        /*
         * Allocate the result buffer.
         */
        if ((result = kauth_acl_alloc(entries)) == NULL) {
-               KAUTH_DEBUG("    ERROR - could not allocate %d-entry result buffer for inherited ACL");
+               KAUTH_DEBUG("    ERROR - could not allocate %d-entry result buffer for inherited ACL", entries);
                error = ENOMEM;
                goto out;
        }
 
        /*
         * Composition is simply:
                error = ENOMEM;
                goto out;
        }
 
        /*
         * Composition is simply:
-        *  - initial
-        *  - inherited
+        *  - initial direct ACEs
+        *  - inherited ACEs from new parent
         */
        index = 0;
        if (initial != NULL) {
         */
        index = 0;
        if (initial != NULL) {
-               for (i = 0; i < initial->acl_entrycount; i++)
-                       result->acl_ace[index++] = initial->acl_ace[i];
-               KAUTH_DEBUG("    INHERIT - applied %d initial entries", index);
+               for (i = 0; i < initial->acl_entrycount; i++) {
+                       if (!(initial->acl_ace[i].ace_flags & KAUTH_ACE_INHERITED)) {
+                               result->acl_ace[index++] = initial->acl_ace[i];
+                       }
+               }
+               KAUTH_DEBUG("    INHERIT - applied %d of %d initial entries", index, initial->acl_entrycount);
        }
        if (inherit != NULL) {
                for (i = 0; i < inherit->acl_entrycount; i++) {
        }
        if (inherit != NULL) {
                for (i = 0; i < inherit->acl_entrycount; i++) {
-                       /* inherit onto this object? */
+                       /*
+                        * Inherit onto this object?  We inherit only if
+                        * the target object is a container object and the
+                        * KAUTH_ACE_DIRECTORY_INHERIT bit is set, OR if
+                        * if the target object is not a container, and
+                        * the KAUTH_ACE_FILE_INHERIT bit is set.
+                        */
                        if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
                                result->acl_ace[index] = inherit->acl_ace[i];
                                result->acl_ace[index].ace_flags |= KAUTH_ACE_INHERITED;
                        if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
                                result->acl_ace[index] = inherit->acl_ace[i];
                                result->acl_ace[index].ace_flags |= KAUTH_ACE_INHERITED;
-                               /* don't re-inherit? */
-                               if (result->acl_ace[index].ace_flags & KAUTH_ACE_LIMIT_INHERIT)
+                               result->acl_ace[index].ace_flags &= ~KAUTH_ACE_ONLY_INHERIT;
+                               /*
+                                * We do not re-inherit inheritance flags
+                                * if the ACE from the container has a
+                                * KAUTH_ACE_LIMIT_INHERIT, OR if the new
+                                * object is not itself a container (since
+                                * inheritance is always container-based).
+                                */
+                               if ((result->acl_ace[index].ace_flags & KAUTH_ACE_LIMIT_INHERIT) || !isdir) {
                                        result->acl_ace[index].ace_flags &=
                                        result->acl_ace[index].ace_flags &=
-                                           ~(KAUTH_ACE_DIRECTORY_INHERIT | KAUTH_ACE_FILE_INHERIT | KAUTH_ACE_LIMIT_INHERIT);
+                                           ~(KAUTH_ACE_INHERIT_CONTROL_FLAGS);
+                               }
                                index++;
                        }
                }
                                index++;
                        }
                }
@@ -859,9 +944,10 @@ kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int is
        KAUTH_DEBUG("    INHERIT - product ACL has %d entries", index);
        error = 0;
 out:
        KAUTH_DEBUG("    INHERIT - product ACL has %d entries", index);
        error = 0;
 out:
-       if (inherit != NULL)
+       if (inherit != NULL) {
                kauth_acl_free(inherit);
                kauth_acl_free(inherit);
-       return(error);
+       }
+       return error;
 }
 
 /*
 }
 
 /*
@@ -895,12 +981,11 @@ out:
 int
 kauth_copyinfilesec(user_addr_t xsecurity, kauth_filesec_t *xsecdestpp)
 {
 int
 kauth_copyinfilesec(user_addr_t xsecurity, kauth_filesec_t *xsecdestpp)
 {
-       user_addr_t uaddr, known_bound;
        int error;
        kauth_filesec_t fsec;
        u_int32_t count;
        size_t copysize;
        int error;
        kauth_filesec_t fsec;
        u_int32_t count;
        size_t copysize;
-       
+
        error = 0;
        fsec = NULL;
 
        error = 0;
        fsec = NULL;
 
@@ -912,20 +997,30 @@ kauth_copyinfilesec(user_addr_t xsecurity, kauth_filesec_t *xsecdestpp)
         *
         * The upper bound must be less than KAUTH_ACL_MAX_ENTRIES.  The
         * value here is fairly arbitrary.  It's ok to have a zero count.
         *
         * The upper bound must be less than KAUTH_ACL_MAX_ENTRIES.  The
         * value here is fairly arbitrary.  It's ok to have a zero count.
+        *
+        * Because we're just using these values to make a guess about the
+        * number of entries, the actual address doesn't matter, only their
+        * relative offsets into the page.  We take advantage of this to
+        * avoid an overflow in the rounding step (this is a user-provided
+        * parameter, so caution pays off).
         */
         */
-       known_bound = xsecurity + sizeof(struct kauth_filesec);
-       uaddr = mach_vm_round_page(known_bound);
-       count = (uaddr - known_bound) / sizeof(struct kauth_ace);
-       if (count > 32)
+       {
+               user_addr_t known_bound = (xsecurity & PAGE_MASK) + KAUTH_FILESEC_SIZE(0);
+               user_addr_t uaddr = mach_vm_round_page(known_bound);
+               count = (uaddr - known_bound) / sizeof(struct kauth_ace);
+       }
+       if (count > 32) {
                count = 32;
                count = 32;
+       }
 restart:
        if ((fsec = kauth_filesec_alloc(count)) == NULL) {
                error = ENOMEM;
                goto out;
        }
        copysize = KAUTH_FILESEC_SIZE(count);
 restart:
        if ((fsec = kauth_filesec_alloc(count)) == NULL) {
                error = ENOMEM;
                goto out;
        }
        copysize = KAUTH_FILESEC_SIZE(count);
-       if ((error = copyin(xsecurity, (caddr_t)fsec, copysize)) != 0)
+       if ((error = copyin(xsecurity, (caddr_t)fsec, copysize)) != 0) {
                goto out;
                goto out;
+       }
 
        /* validate the filesec header */
        if (fsec->fsec_magic != KAUTH_FILESEC_MAGIC) {
 
        /* validate the filesec header */
        if (fsec->fsec_magic != KAUTH_FILESEC_MAGIC) {
@@ -947,15 +1042,17 @@ restart:
                kauth_filesec_free(fsec);
                goto restart;
        }
                kauth_filesec_free(fsec);
                goto restart;
        }
-       
+
 out:
        if (error) {
 out:
        if (error) {
-               if (fsec)
+               if (fsec) {
                        kauth_filesec_free(fsec);
                        kauth_filesec_free(fsec);
+               }
        } else {
                *xsecdestpp = fsec;
        } else {
                *xsecdestpp = fsec;
+               AUDIT_ARG(opaque, fsec, copysize);
        }
        }
-       return(error);
+       return error;
 }
 
 /*
 }
 
 /*
@@ -980,11 +1077,12 @@ out:
 kauth_filesec_t
 kauth_filesec_alloc(int count)
 {
 kauth_filesec_t
 kauth_filesec_alloc(int count)
 {
-       kauth_filesec_t fsp;
-       
+       kauth_filesec_t fsp;
+
        /* if the caller hasn't given us a valid size hint, assume the worst */
        /* if the caller hasn't given us a valid size hint, assume the worst */
-       if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES))
-               return(NULL);
+       if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES)) {
+               return NULL;
+       }
 
        MALLOC(fsp, kauth_filesec_t, KAUTH_FILESEC_SIZE(count), M_KAUTH, M_WAITOK);
        if (fsp != NULL) {
 
        MALLOC(fsp, kauth_filesec_t, KAUTH_FILESEC_SIZE(count), M_KAUTH, M_WAITOK);
        if (fsp != NULL) {
@@ -994,8 +1092,8 @@ kauth_filesec_alloc(int count)
                fsp->fsec_entrycount = KAUTH_FILESEC_NOACL;
                fsp->fsec_flags = 0;
        }
                fsp->fsec_entrycount = KAUTH_FILESEC_NOACL;
                fsp->fsec_flags = 0;
        }
-       return(fsp);
-}      
+       return fsp;
+}
 
 /*
  * Free a kauth_filesec_t that was previous allocated, either by a direct
 
 /*
  * Free a kauth_filesec_t that was previous allocated, either by a direct
@@ -1013,16 +1111,18 @@ void
 kauth_filesec_free(kauth_filesec_t fsp)
 {
 #ifdef KAUTH_DEBUG_ENABLE
 kauth_filesec_free(kauth_filesec_t fsp)
 {
 #ifdef KAUTH_DEBUG_ENABLE
-       if (fsp == KAUTH_FILESEC_NONE)
+       if (fsp == KAUTH_FILESEC_NONE) {
                panic("freeing KAUTH_FILESEC_NONE");
                panic("freeing KAUTH_FILESEC_NONE");
-       if (fsp == KAUTH_FILESEC_WANTED)
+       }
+       if (fsp == KAUTH_FILESEC_WANTED) {
                panic("freeing KAUTH_FILESEC_WANTED");
                panic("freeing KAUTH_FILESEC_WANTED");
+       }
 #endif
        FREE(fsp, M_KAUTH);
 }
 
 /*
 #endif
        FREE(fsp, M_KAUTH);
 }
 
 /*
- * Set the endianness of a filesec and an ACL; if 'acl' is NULL, use the 
+ * Set the endianness of a filesec and an ACL; if 'acl' is NULL, use the
  * ACL interior to 'fsec' instead.  If the endianness doesn't change, then
  * this function will have no effect.
  *
  * ACL interior to 'fsec' instead.  If the endianness doesn't change, then
  * this function will have no effect.
  *
@@ -1040,17 +1140,19 @@ kauth_filesec_free(kauth_filesec_t fsp)
 void
 kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
 {
 void
 kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
 {
-       uint32_t        compare_magic = KAUTH_FILESEC_MAGIC;
-       uint32_t        invert_magic = ntohl(KAUTH_FILESEC_MAGIC);
-       uint32_t        compare_acl_entrycount;
-       uint32_t        i;
+       uint32_t        compare_magic = KAUTH_FILESEC_MAGIC;
+       uint32_t        invert_magic = ntohl(KAUTH_FILESEC_MAGIC);
+       uint32_t        compare_acl_entrycount;
+       uint32_t        i;
 
 
-       if (compare_magic == invert_magic)
+       if (compare_magic == invert_magic) {
                return;
                return;
+       }
 
        /* If no ACL, use ACL interior to 'fsec' instead */
 
        /* If no ACL, use ACL interior to 'fsec' instead */
-       if (acl == NULL)
+       if (acl == NULL) {
                acl = &fsec->fsec_acl;
                acl = &fsec->fsec_acl;
+       }
 
        compare_acl_entrycount = acl->acl_entrycount;
 
 
        compare_acl_entrycount = acl->acl_entrycount;
 
@@ -1059,21 +1161,23 @@ kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
         * are valid.  The following switch and tests effectively reject
         * conversions on invalid magic numbers as a desirable side effect.
         */
         * are valid.  The following switch and tests effectively reject
         * conversions on invalid magic numbers as a desirable side effect.
         */
-       switch(kendian) {
-       case KAUTH_ENDIAN_HOST:         /* not in host, convert to host */
-               if (fsec->fsec_magic != invert_magic)
+       switch (kendian) {
+       case KAUTH_ENDIAN_HOST:         /* not in host, convert to host */
+               if (fsec->fsec_magic != invert_magic) {
                        return;
                        return;
+               }
                /* acl_entrycount is byteswapped */
                compare_acl_entrycount = ntohl(acl->acl_entrycount);
                break;
                /* acl_entrycount is byteswapped */
                compare_acl_entrycount = ntohl(acl->acl_entrycount);
                break;
-       case KAUTH_ENDIAN_DISK:         /* not in disk, convert to disk */
-               if (fsec->fsec_magic != compare_magic)
+       case KAUTH_ENDIAN_DISK:         /* not in disk, convert to disk */
+               if (fsec->fsec_magic != compare_magic) {
                        return;
                        return;
+               }
                break;
                break;
-       default:                        /* bad argument */
+       default:                        /* bad argument */
                return;
        }
                return;
        }
-       
+
        /* We are go for conversion */
        fsec->fsec_magic = ntohl(fsec->fsec_magic);
        acl->acl_entrycount = ntohl(acl->acl_entrycount);
        /* We are go for conversion */
        fsec->fsec_magic = ntohl(fsec->fsec_magic);
        acl->acl_entrycount = ntohl(acl->acl_entrycount);
@@ -1086,7 +1190,7 @@ kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
                        acl->acl_ace[i].ace_rights = ntohl(acl->acl_ace[i].ace_rights);
                }
        }
                        acl->acl_ace[i].ace_rights = ntohl(acl->acl_ace[i].ace_rights);
                }
        }
- }
+}
 
 
 /*
 
 
 /*
@@ -1095,19 +1199,20 @@ kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
 kauth_acl_t
 kauth_acl_alloc(int count)
 {
 kauth_acl_t
 kauth_acl_alloc(int count)
 {
-       kauth_acl_t     aclp;
-       
+       kauth_acl_t     aclp;
+
        /* if the caller hasn't given us a valid size hint, assume the worst */
        /* if the caller hasn't given us a valid size hint, assume the worst */
-       if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES))
-               return(NULL);
+       if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES)) {
+               return NULL;
+       }
 
        MALLOC(aclp, kauth_acl_t, KAUTH_ACL_SIZE(count), M_KAUTH, M_WAITOK);
        if (aclp != NULL) {
                aclp->acl_entrycount = 0;
                aclp->acl_flags = 0;
        }
 
        MALLOC(aclp, kauth_acl_t, KAUTH_ACL_SIZE(count), M_KAUTH, M_WAITOK);
        if (aclp != NULL) {
                aclp->acl_entrycount = 0;
                aclp->acl_flags = 0;
        }
-       return(aclp);
-}      
+       return aclp;
+}
 
 void
 kauth_acl_free(kauth_acl_t aclp)
 
 void
 kauth_acl_free(kauth_acl_t aclp)
@@ -1119,9 +1224,10 @@ kauth_acl_free(kauth_acl_t aclp)
 /*
  * WARNING - caller must hold KAUTH_SCOPELOCK
  */
 /*
  * WARNING - caller must hold KAUTH_SCOPELOCK
  */
-static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp)
+static int
+kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp)
 {
 {
-       int             i;
+       int             i;
 
        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
                if (sp->ks_listeners[i].kll_listenerp == NULL) {
 
        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
                if (sp->ks_listeners[i].kll_listenerp == NULL) {
@@ -1129,8 +1235,8 @@ static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp)
                        sp->ks_listeners[i].kll_idata = klp->kl_idata;
                        sp->ks_listeners[i].kll_listenerp = klp;
                        sp->ks_flags |= KS_F_HAS_LISTENERS;
                        sp->ks_listeners[i].kll_idata = klp->kl_idata;
                        sp->ks_listeners[i].kll_listenerp = klp;
                        sp->ks_flags |= KS_F_HAS_LISTENERS;
-                       return(0);
+                       return 0;
                }
        }
                }
        }
-       return(ENOSPC);
+       return ENOSPC;
 }
 }