2 * Copyright (c) 2004-2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
36 * Kernel Authorization framework: Management of process/thread credentials
37 * and identity information.
40 #include <sys/param.h> /* XXX trim includes */
42 #include <sys/systm.h>
43 #include <sys/ucred.h>
44 #include <sys/proc_internal.h>
46 #include <sys/timeb.h>
47 #include <sys/times.h>
48 #include <sys/malloc.h>
49 #include <sys/kauth.h>
50 #include <sys/kernel.h>
53 #include <security/audit/audit.h>
55 #include <sys/mount.h>
56 #include <sys/stat.h> /* For manifest constants in posix_cred_access */
57 #include <sys/sysproto.h>
58 #include <mach/message.h>
59 #include <mach/host_security.h>
61 #include <machine/atomic.h>
63 #include <kern/task.h>
64 #include <kern/locks.h>
68 #define MACH_ASSERT 1 /* XXX so bogus */
69 #include <kern/assert.h>
72 #include <security/mac.h>
73 #include <security/mac_framework.h>
74 #include <security/_label.h>
78 #include <IOKit/IOBSD.h>
80 void mach_kauth_cred_uthread_update( void );
82 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
84 /* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */
98 # define K_UUID_FMT "%08x:%08x:%08x:%08x"
99 # define K_UUID_ARG(_u) &_u.g_guid_asint[0],&_u.g_guid_asint[1],&_u.g_guid_asint[2],&_u.g_guid_asint[3]
100 # define KAUTH_DEBUG(fmt, args...) do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0)
104 * Credential debugging; we can track entry into a function that might
105 * change a credential, and we can track actual credential changes that
108 * Note: Does *NOT* currently include per-thread credential changes
111 #define DEBUG_CRED_ENTER printf
112 #define DEBUG_CRED_CHANGE printf
113 #else /* !DEBUG_CRED */
114 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
115 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
116 #endif /* !DEBUG_CRED */
118 #if CONFIG_EXT_RESOLVER
120 * Interface to external identity resolver.
122 * The architecture of the interface is simple; the external resolver calls
123 * in to get work, then calls back with completed work. It also calls us
124 * to let us know that it's (re)started, so that we can resubmit work if it
128 static LCK_MTX_DECLARE(kauth_resolver_mtx
, &kauth_lck_grp
);
129 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(&kauth_resolver_mtx);
130 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(&kauth_resolver_mtx);
132 static volatile pid_t kauth_resolver_identity
;
133 static int kauth_identitysvc_has_registered
;
134 static int kauth_resolver_registered
;
135 static uint32_t kauth_resolver_sequence
= 31337;
136 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
138 struct kauth_resolver_work
{
139 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
140 struct kauth_identity_extlookup kr_work
;
145 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
146 #define KAUTH_REQUEST_SUBMITTED (1<<1)
147 #define KAUTH_REQUEST_DONE (1<<2)
151 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
=
152 TAILQ_HEAD_INITIALIZER(kauth_resolver_unsubmitted
);
153 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
=
154 TAILQ_HEAD_INITIALIZER(kauth_resolver_submitted
);
155 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
=
156 TAILQ_HEAD_INITIALIZER(kauth_resolver_done
);
158 /* Number of resolver timeouts between logged complaints */
159 #define KAUTH_COMPLAINT_INTERVAL 1000
160 int kauth_resolver_timeout_cnt
= 0;
162 #if DEVELOPMENT || DEBUG
163 /* Internal builds get different (less ambiguous) breadcrumbs. */
164 #define KAUTH_RESOLVER_FAILED_ERRCODE EOWNERDEAD
166 /* But non-Internal builds get errors that are allowed by standards. */
167 #define KAUTH_RESOLVER_FAILED_ERRCODE EIO
168 #endif /* DEVELOPMENT || DEBUG */
170 int kauth_resolver_failed_cnt
= 0;
171 #define RESOLVER_FAILED_MESSAGE(fmt, args...) \
173 if (!(kauth_resolver_failed_cnt++ % 100)) { \
174 printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##args); \
178 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
179 static int kauth_resolver_complete(user_addr_t message
);
180 static int kauth_resolver_getwork(user_addr_t message
);
181 static int kauth_resolver_getwork2(user_addr_t message
);
182 static __attribute__((noinline
)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
183 struct kauth_resolver_work
*);
185 #define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
187 struct kauth_identity
{
188 TAILQ_ENTRY(kauth_identity
) ki_link
;
192 uint32_t ki_supgrpcnt
;
193 gid_t ki_supgrps
[NGROUPS
];
196 const char *ki_name
; /* string name from string cache */
198 * Expiry times are the earliest time at which we will disregard the
199 * cached state and go to userland. Before then if the valid bit is
200 * set, we will return the cached value. If it's not set, we will
201 * not go to userland to resolve, just assume that there is no answer
204 time_t ki_groups_expiry
;
205 time_t ki_guid_expiry
;
206 time_t ki_ntsid_expiry
;
209 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
=
210 TAILQ_HEAD_INITIALIZER(kauth_identities
);
211 static LCK_MTX_DECLARE(kauth_identity_mtx
, &kauth_lck_grp
);
212 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(&kauth_identity_mtx);
213 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(&kauth_identity_mtx);
214 #define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
215 static int kauth_identity_cachemax
= KAUTH_IDENTITY_CACHEMAX_DEFAULT
;
216 static int kauth_identity_count
;
218 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
219 ntsid_t
*ntsidp
, time_t ntsid_expiry
, size_t supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
220 const char *name
, int nametype
);
221 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
222 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
223 static void kauth_identity_trimcache(int newsize
);
224 static void kauth_identity_lru(struct kauth_identity
*kip
);
225 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
226 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
227 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
228 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
229 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
230 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
231 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
233 struct kauth_group_membership
{
234 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
235 uid_t gm_uid
; /* the identity whose membership we're recording */
236 gid_t gm_gid
; /* group of which they are a member */
237 time_t gm_expiry
; /* TTL for the membership, or 0 for persistent entries */
239 #define KAUTH_GROUP_ISMEMBER (1<<0)
242 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
=
243 TAILQ_HEAD_INITIALIZER(kauth_groups
);
244 static LCK_MTX_DECLARE(kauth_groups_mtx
, &kauth_lck_grp
);
245 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(&kauth_groups_mtx);
246 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(&kauth_groups_mtx);
247 #define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
248 static int kauth_groups_cachemax
= KAUTH_GROUPS_CACHEMAX_DEFAULT
;
249 static int kauth_groups_count
;
251 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
252 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
253 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
254 static void kauth_groups_trimcache(int newsize
);
256 #endif /* CONFIG_EXT_RESOLVER */
258 #define KAUTH_CRED_TABLE_SIZE 128
260 ZONE_DECLARE(ucred_zone
, "cred", sizeof(struct ucred
), ZC_ZFREE_CLEARMEM
);
262 LIST_HEAD(kauth_cred_entry_head
, ucred
);
263 static struct kauth_cred_entry_head
264 kauth_cred_table_anchor
[KAUTH_CRED_TABLE_SIZE
];
266 static struct kauth_cred_entry_head
*kauth_cred_get_bucket(kauth_cred_t cred
);
267 static kauth_cred_t
kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
);
268 static void kauth_cred_remove_locked(kauth_cred_t cred
);
269 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
270 static kauth_cred_t
kauth_cred_find_and_ref(kauth_cred_t cred
,
271 struct kauth_cred_entry_head
*bucket
);
272 static bool kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
);
274 #if CONFIG_EXT_RESOLVER
277 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
279 * Description: Waits for the user space daemon to respond to the request
280 * we made. Function declared non inline to be visible in
281 * stackshots and spindumps as well as debugging.
283 * Parameters: workp Work queue entry.
285 * Returns: 0 on Success.
286 * EIO if Resolver is dead.
287 * EINTR thread interrupted in msleep
288 * EWOULDBLOCK thread timed out in msleep
289 * ERESTART returned by msleep.
292 static __attribute__((noinline
)) int
293 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
294 struct kauth_resolver_work
*workp
)
299 /* we could compute a better timeout here */
300 ts
.tv_sec
= kauth_resolver_timeout
;
302 error
= msleep(workp
, &kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
303 /* request has been completed? */
304 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
)) {
307 /* woken because the resolver has died? */
308 if (kauth_resolver_identity
== 0) {
309 RESOLVER_FAILED_MESSAGE("kauth external resolver died while while waiting for work to complete");
310 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
323 * kauth_resolver_identity_reset
325 * Description: Reset the identity of the external resolver in certain
326 * controlled circumstances.
333 kauth_resolver_identity_reset(void)
335 KAUTH_RESOLVER_LOCK();
336 if (kauth_resolver_identity
!= 0) {
337 printf("kauth external resolver %d failed to de-register.\n",
338 kauth_resolver_identity
);
339 kauth_resolver_identity
= 0;
340 kauth_resolver_registered
= 0;
342 KAUTH_RESOLVER_UNLOCK();
346 * kauth_resolver_submit
348 * Description: Submit an external credential identity resolution request to
349 * the user space daemon.
351 * Parameters: lkp A pointer to an external
353 * extend_data extended data for kr_extend
356 * EWOULDBLOCK No resolver registered
357 * EINTR Operation interrupted (e.g. by
359 * ENOMEM Could not allocate work item
360 * copyinstr:EFAULT Bad message from user space
361 * workp->kr_result:??? An error from the user space
362 * daemon (includes ENOENT!)
367 * Notes: Allocate a work queue entry, submit the work and wait for
368 * the operation to either complete or time out. Outstanding
369 * operations may also be cancelled.
371 * Submission is by means of placing the item on a work queue
372 * which is serviced by an external resolver thread calling
373 * into the kernel. The caller then sleeps until timeout,
374 * cancellation, or an external resolver thread calls in with
375 * a result message to kauth_resolver_complete(). All of these
376 * events wake the caller back up.
378 * This code is called from either kauth_cred_ismember_gid()
379 * for a group membership request, or it is called from
380 * kauth_cred_cache_lookup() when we get a cache miss.
383 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
385 struct kauth_resolver_work
*workp
, *killp
;
387 int error
, shouldfree
;
389 /* no point actually blocking if the resolver isn't up yet */
390 if (kauth_resolver_identity
== 0) {
392 * We've already waited an initial <kauth_resolver_timeout>
393 * seconds with no result.
395 * Sleep on a stack address so no one wakes us before timeout;
396 * we sleep a half a second in case we are a high priority
397 * process, so that memberd doesn't starve while we are in a
398 * tight loop between user and kernel, eating all the CPU.
400 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/ 2);
401 if (kauth_resolver_identity
== 0) {
403 * if things haven't changed while we were asleep,
404 * tell the caller we couldn't get an authoritative
411 workp
= kheap_alloc(KM_KAUTH
, sizeof(struct kauth_resolver_work
),
417 workp
->kr_work
= *lkp
;
418 workp
->kr_extend
= extend_data
;
420 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
421 workp
->kr_result
= 0;
424 * We insert the request onto the unsubmitted queue, the call in from
425 * the resolver will it to the submitted thread when appropriate.
427 KAUTH_RESOLVER_LOCK();
428 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
429 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
432 * XXX We *MUST NOT* attempt to coalesce identical work items due to
433 * XXX the inability to ensure order of update of the request item
434 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
435 * XXX for each item repeat the update when they wake up.
437 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
440 * Wake up an external resolver thread to deal with the new work; one
441 * may not be available, and if not, then the request will be grabbed
442 * when a resolver thread comes back into the kernel to request new
445 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
446 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
448 /* if the request was processed, copy the result */
450 *lkp
= workp
->kr_work
;
453 if (error
== EWOULDBLOCK
) {
454 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
455 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
456 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
459 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
461 * If the request timed out and was never collected, the resolver
462 * is dead and probably not coming back anytime soon. In this
463 * case we revert to no-resolver behaviour, and punt all the other
464 * sleeping requests to clear the backlog.
466 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
469 * Make the current resolver non-authoritative, and mark it as
470 * no longer registered to prevent kauth_cred_ismember_gid()
471 * enqueueing more work until a new one is registered. This
472 * mitigates the damage a crashing resolver may inflict.
474 kauth_resolver_identity
= 0;
475 kauth_resolver_registered
= 0;
477 /* kill all the other requestes that are waiting as well */
478 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
480 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
482 /* Cause all waiting-for-work threads to return EIO */
483 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
488 * drop our reference on the work item, and note whether we should
491 if (--workp
->kr_refs
<= 0) {
492 /* work out which list we have to remove it from */
493 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
494 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
495 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
496 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
497 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
498 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
500 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
504 /* someone else still has a reference on this request */
508 /* collect request result */
510 error
= workp
->kr_result
;
512 KAUTH_RESOLVER_UNLOCK();
515 * If we dropped the last reference, free the request.
518 kheap_free(KM_KAUTH
, workp
, sizeof(struct kauth_resolver_work
));
521 KAUTH_DEBUG("RESOLVER - returning %d", error
);
529 * Description: System call interface for the external identity resolver.
531 * Parameters: uap->message Message from daemon to kernel
533 * Returns: 0 Successfully became resolver
534 * EPERM Not the resolver process
535 * kauth_authorize_generic:EPERM Not root user
536 * kauth_resolver_complete:EIO
537 * kauth_resolver_complete:EFAULT
538 * kauth_resolver_getwork:EINTR
539 * kauth_resolver_getwork:EFAULT
541 * Notes: This system call blocks until there is work enqueued, at
542 * which time the kernel wakes it up, and a message from the
543 * kernel is copied out to the identity resolution daemon, which
544 * proceed to attempt to resolve it. When the resolution has
545 * completed (successfully or not), the daemon called back into
546 * this system call to give the result to the kernel, and wait
547 * for the next request.
550 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
552 int opcode
= uap
->opcode
;
553 user_addr_t message
= uap
->message
;
554 struct kauth_resolver_work
*workp
;
555 struct kauth_cache_sizes sz_arg
= {};
559 if (!IOTaskHasEntitlement(current_task(), IDENTITYSVC_ENTITLEMENT
)) {
560 KAUTH_DEBUG("RESOLVER - pid %d not entitled to call identitysvc", current_proc()->p_pid
);
565 * New server registering itself.
567 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
568 new_id
= current_proc()->p_pid
;
569 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
570 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
573 KAUTH_RESOLVER_LOCK();
574 if (kauth_resolver_identity
!= new_id
) {
575 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
577 * We have a new server, so assume that all the old requests have been lost.
579 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
580 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
581 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
582 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
583 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
586 * Allow user space resolver to override the
587 * external resolution timeout
589 if (message
> 30 && message
< 10000) {
590 kauth_resolver_timeout
= (int)message
;
591 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
593 kauth_resolver_identity
= new_id
;
594 kauth_resolver_registered
= 1;
595 kauth_identitysvc_has_registered
= 1;
596 wakeup(&kauth_resolver_unsubmitted
);
598 KAUTH_RESOLVER_UNLOCK();
603 * Beyond this point, we must be the resolver process. We verify this
604 * by confirming the resolver credential and pid.
606 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
607 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
611 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
612 KAUTH_IDENTITY_LOCK();
613 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
614 KAUTH_IDENTITY_UNLOCK();
617 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
618 KAUTH_GROUPS_UNLOCK();
620 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof(sz_arg
))) != 0) {
625 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
626 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof(sz_arg
))) != 0) {
630 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
631 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
635 KAUTH_IDENTITY_LOCK();
636 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
637 kauth_identity_trimcache(kauth_identity_cachemax
);
638 KAUTH_IDENTITY_UNLOCK();
641 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
642 kauth_groups_trimcache(kauth_groups_cachemax
);
643 KAUTH_GROUPS_UNLOCK();
646 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
647 KAUTH_IDENTITY_LOCK();
648 kauth_identity_trimcache(0);
649 KAUTH_IDENTITY_UNLOCK();
652 kauth_groups_trimcache(0);
653 KAUTH_GROUPS_UNLOCK();
654 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
656 * Terminate outstanding requests; without an authoritative
657 * resolver, we are now back on our own authority.
659 struct kauth_resolver_work
*killp
;
661 KAUTH_RESOLVER_LOCK();
664 * Clear the identity, but also mark it as unregistered so
665 * there is no explicit future expectation of us getting a
666 * new resolver any time soon.
668 kauth_resolver_identity
= 0;
669 kauth_resolver_registered
= 0;
671 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
673 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
675 /* Cause all waiting-for-work threads to return EIO */
676 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
677 KAUTH_RESOLVER_UNLOCK();
681 * Got a result returning?
683 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
684 if ((error
= kauth_resolver_complete(message
)) != 0) {
690 * Caller wants to take more work?
692 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
693 if ((error
= kauth_resolver_getwork(message
)) != 0) {
703 * kauth_resolver_getwork_continue
705 * Description: Continuation for kauth_resolver_getwork
707 * Parameters: result Error code or 0 for the sleep
708 * that got us to this function
711 * EINTR Interrupted (e.g. by signal)
712 * kauth_resolver_getwork2:EFAULT
714 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
718 kauth_resolver_getwork_continue(int result
)
725 KAUTH_RESOLVER_UNLOCK();
730 * If we lost a race with another thread/memberd restarting, then we
731 * need to go back to sleep to look for more work. If it was memberd
732 * restarting, then the msleep0() will error out here, as our thread
733 * will already be "dead".
735 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
738 error
= msleep0(&kauth_resolver_unsubmitted
, &kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
740 * If this is a wakeup from another thread in the resolver
741 * deregistering it, error out the request-for-work thread
743 if (!kauth_resolver_identity
) {
744 RESOLVER_FAILED_MESSAGE("external resolver died");
745 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
747 KAUTH_RESOLVER_UNLOCK();
751 thread
= current_thread();
752 ut
= get_bsdthread_info(thread
);
753 message
= ut
->uu_save
.uus_kauth
.message
;
754 return kauth_resolver_getwork2(message
);
759 * kauth_resolver_getwork2
761 * Decription: Common utility function to copy out a identity resolver work
762 * item from the kernel to user space as part of the user space
763 * identity resolver requesting work.
765 * Parameters: message message to user space
768 * EFAULT Bad user space message address
770 * Notes: This common function exists to permit the use of continuations
771 * in the identity resolution process. This frees up the stack
772 * while we are waiting for the user space resolver to complete
773 * a request. This is specifically used so that our per thread
774 * cost can be small, and we will therefore be willing to run a
775 * larger number of threads in the user space identity resolver.
778 kauth_resolver_getwork2(user_addr_t message
)
780 struct kauth_resolver_work
*workp
;
784 * Note: We depend on the caller protecting us from a NULL work item
785 * queue, since we must have the kauth resolver lock on entry to this
788 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
791 * Copy out the external lookup structure for the request, not
792 * including the el_extend field, which contains the address of the
793 * external buffer provided by the external resolver into which we
794 * copy the extension request information.
797 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
798 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
802 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
803 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
804 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
805 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
810 * Handle extended requests here; if we have a request of a type where
811 * the kernel wants a translation of extended information, then we need
812 * to copy it out into the extended buffer, assuming the buffer is
813 * valid; we only attempt to get the buffer address if we have request
814 * data to copy into it.
818 * translate a user@domain string into a uid/gid/whatever
820 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
823 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
825 size_t actual
; /* not used */
827 * Use copyoutstr() to reduce the copy size; we let
828 * this catch a NULL uaddr because we shouldn't be
829 * asking in that case anyway.
831 error
= copyoutstr(CAST_DOWN(void *, workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
834 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
838 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
839 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
840 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
841 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
844 KAUTH_RESOLVER_UNLOCK();
850 * kauth_resolver_getwork
852 * Description: Get a work item from the enqueued requests from the kernel and
853 * give it to the user space daemon.
855 * Parameters: message message to user space
858 * EINTR Interrupted (e.g. by signal)
859 * kauth_resolver_getwork2:EFAULT
861 * Notes: This function blocks in a continuation if there are no work
862 * items available for processing at the time the user space
863 * identity resolution daemon makes a request for work. This
864 * permits a large number of threads to be used by the daemon,
865 * without using a lot of wired kernel memory when there are no
866 * actual request outstanding.
869 kauth_resolver_getwork(user_addr_t message
)
871 struct kauth_resolver_work
*workp
;
874 KAUTH_RESOLVER_LOCK();
876 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
877 thread_t thread
= current_thread();
878 struct uthread
*ut
= get_bsdthread_info(thread
);
880 ut
->uu_save
.uus_kauth
.message
= message
;
881 error
= msleep0(&kauth_resolver_unsubmitted
, &kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
882 KAUTH_RESOLVER_UNLOCK();
884 * If this is a wakeup from another thread in the resolver
885 * deregistering it, error out the request-for-work thread
887 if (!kauth_resolver_identity
) {
888 printf("external resolver died");
889 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
893 return kauth_resolver_getwork2(message
);
898 * kauth_resolver_complete
900 * Description: Return a result from userspace.
902 * Parameters: message message from user space
905 * EIO The resolver is dead
906 * copyin:EFAULT Bad message from user space
909 kauth_resolver_complete(user_addr_t message
)
911 struct kauth_identity_extlookup extl
;
912 struct kauth_resolver_work
*workp
;
913 struct kauth_resolver_work
*killp
;
914 int error
, result
, want_extend_data
;
917 * Copy in the mesage, including the extension field, since we are
918 * copying into a local variable.
920 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
921 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
925 KAUTH_RESOLVER_LOCK();
929 switch (extl
.el_result
) {
930 case KAUTH_EXTLOOKUP_INPROG
:
934 /* XXX this should go away once memberd is updated */
936 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
942 case KAUTH_EXTLOOKUP_SUCCESS
:
945 case KAUTH_EXTLOOKUP_FATAL
:
946 /* fatal error means the resolver is dead */
947 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
948 RESOLVER_FAILED_MESSAGE("resolver %d died, waiting for a new one", kauth_resolver_identity
);
950 * Terminate outstanding requests; without an authoritative
951 * resolver, we are now back on our own authority. Tag the
952 * resolver unregistered to prevent kauth_cred_ismember_gid()
953 * enqueueing more work until a new one is registered. This
954 * mitigates the damage a crashing resolver may inflict.
956 kauth_resolver_identity
= 0;
957 kauth_resolver_registered
= 0;
959 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
961 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
963 /* Cause all waiting-for-work threads to return EIO */
964 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
965 /* and return EIO to the caller */
966 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
969 case KAUTH_EXTLOOKUP_BADRQ
:
970 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
974 case KAUTH_EXTLOOKUP_FAILURE
:
975 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
976 RESOLVER_FAILED_MESSAGE("resolver reported transient failure for request %d", extl
.el_seqno
);
977 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
981 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
982 RESOLVER_FAILED_MESSAGE("resolver returned unexpected status %d", extl
.el_result
);
983 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
988 * In the case of a fatal error, we assume that the resolver will
989 * restart quickly and re-collect all of the outstanding requests.
990 * Thus, we don't complete the request which returned the fatal
993 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
994 /* scan our list for this request */
995 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
997 if (workp
->kr_seqno
== extl
.el_seqno
) {
999 * Do we want extend_data?
1001 want_extend_data
= (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_WANT_PWNAM
| KAUTH_EXTLOOKUP_WANT_GRNAM
));
1004 * Get the request of the submitted queue so
1005 * that it is not cleaned up out from under
1008 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1009 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1010 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1011 workp
->kr_result
= result
;
1013 /* Copy the result message to the work item. */
1014 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1017 * Check if we have a result in the extension
1018 * field; if we do, then we need to separately
1019 * copy the data from the message el_extend
1020 * into the request buffer that's in the work
1021 * item. We have to do it here because we do
1022 * not want to wake up the waiter until the
1023 * data is in their buffer, and because the
1024 * actual request response may be destroyed
1025 * by the time the requester wakes up, and they
1026 * do not have access to the user space buffer
1029 * It is safe to drop and reacquire the lock
1030 * here because we've already removed the item
1031 * from the submission queue, but have not yet
1032 * moved it to the completion queue. Note that
1033 * near simultaneous requests may result in
1034 * duplication of requests for items in this
1035 * window. This should not be a performance
1036 * issue and is easily detectable by comparing
1037 * time to live on last response vs. time of
1038 * next request in the resolver logs.
1040 * A malicious/faulty resolver could overwrite
1041 * part of a user's address space if they return
1042 * flags that mismatch the original request's flags.
1044 if (want_extend_data
&& (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
))) {
1045 size_t actual
; /* notused */
1047 KAUTH_RESOLVER_UNLOCK();
1048 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1049 KAUTH_DEBUG("RESOLVER - resolver got name :%*s: len = %d\n", (int)actual
,
1050 actual
? "null" : (char *)extl
.el_extend
, actual
);
1051 KAUTH_RESOLVER_LOCK();
1052 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1054 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1055 extl
.el_flags
, want_extend_data
);
1059 * Move the completed work item to the
1060 * completion queue and wake up requester(s)
1062 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1069 * Note that it's OK for us not to find anything; if the request has
1070 * timed out the work record will be gone.
1072 KAUTH_RESOLVER_UNLOCK();
1076 #endif /* CONFIG_EXT_RESOLVER */
1083 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1084 #define KI_VALID_GID (1<<1)
1085 #define KI_VALID_GUID (1<<2)
1086 #define KI_VALID_NTSID (1<<3)
1087 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1088 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1089 #define KI_VALID_GROUPS (1<<6)
1091 #if CONFIG_EXT_RESOLVER
1093 * kauth_identity_alloc
1095 * Description: Allocate and fill out a kauth_identity structure for
1096 * translation between {UID|GID}/GUID/NTSID
1100 * Returns: NULL Insufficient memory to satisfy
1101 * the request or bad parameters
1102 * !NULL A pointer to the allocated
1103 * structure, filled in
1105 * Notes: It is illegal to translate between UID and GID; any given UUID
1106 * or NTSID can only refer to an NTSID or UUID (respectively),
1107 * and *either* a UID *or* a GID, but not both.
1109 static struct kauth_identity
*
1110 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1111 ntsid_t
*ntsidp
, time_t ntsid_expiry
, size_t supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1112 const char *name
, int nametype
)
1114 struct kauth_identity
*kip
;
1116 /* get and fill in a new identity */
1117 kip
= kheap_alloc(KM_KAUTH
, sizeof(struct kauth_identity
),
1120 if (gid
!= KAUTH_GID_NONE
) {
1122 kip
->ki_valid
= KI_VALID_GID
;
1124 if (uid
!= KAUTH_UID_NONE
) {
1125 if (kip
->ki_valid
& KI_VALID_GID
) {
1126 panic("can't allocate kauth identity with both uid and gid");
1129 kip
->ki_valid
= KI_VALID_UID
;
1133 * A malicious/faulty resolver could return bad values
1135 assert(supgrpcnt
<= NGROUPS
);
1136 assert(supgrps
!= NULL
);
1138 if ((supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1141 if (kip
->ki_valid
& KI_VALID_GID
) {
1142 panic("can't allocate kauth identity with both gid and supplementary groups");
1144 kip
->ki_supgrpcnt
= (uint32_t)supgrpcnt
;
1145 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1146 kip
->ki_valid
|= KI_VALID_GROUPS
;
1148 kip
->ki_groups_expiry
= groups_expiry
;
1149 if (guidp
!= NULL
) {
1150 kip
->ki_guid
= *guidp
;
1151 kip
->ki_valid
|= KI_VALID_GUID
;
1153 kip
->ki_guid_expiry
= guid_expiry
;
1154 if (ntsidp
!= NULL
) {
1155 kip
->ki_ntsid
= *ntsidp
;
1156 kip
->ki_valid
|= KI_VALID_NTSID
;
1158 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1160 kip
->ki_name
= name
;
1161 kip
->ki_valid
|= nametype
;
1169 * kauth_identity_register_and_free
1171 * Description: Register an association between identity tokens. The passed
1172 * 'kip' is consumed by this function.
1174 * Parameters: kip Pointer to kauth_identity
1175 * structure to register
1179 * Notes: The memory pointer to by 'kip' is assumed to have been
1180 * previously allocated via kauth_identity_alloc().
1183 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1185 struct kauth_identity
*ip
;
1188 * We search the cache for the UID listed in the incoming association.
1189 * If we already have an entry, the new information is merged.
1192 KAUTH_IDENTITY_LOCK();
1193 if (kip
->ki_valid
& KI_VALID_UID
) {
1194 if (kip
->ki_valid
& KI_VALID_GID
) {
1195 panic("kauth_identity: can't insert record with both UID and GID as key");
1197 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1198 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
)) {
1201 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1202 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1203 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
)) {
1207 panic("kauth_identity: can't insert record without UID or GID as key");
1211 /* we already have an entry, merge/overwrite */
1212 if (kip
->ki_valid
& KI_VALID_GUID
) {
1213 ip
->ki_guid
= kip
->ki_guid
;
1214 ip
->ki_valid
|= KI_VALID_GUID
;
1216 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1217 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1218 ip
->ki_ntsid
= kip
->ki_ntsid
;
1219 ip
->ki_valid
|= KI_VALID_NTSID
;
1221 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1222 /* a valid ki_name field overwrites the previous name field */
1223 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1224 /* if there's an old one, discard it */
1225 const char *oname
= NULL
;
1226 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1227 oname
= ip
->ki_name
;
1229 ip
->ki_name
= kip
->ki_name
;
1230 kip
->ki_name
= oname
;
1232 /* and discard the incoming entry */
1236 * if we don't have any information on this identity, add it;
1237 * if it pushes us over our limit, discard the oldest one.
1239 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1240 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1241 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1242 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1243 kauth_identity_count
--;
1246 KAUTH_IDENTITY_UNLOCK();
1247 /* have to drop lock before freeing expired entry (it may be in use) */
1249 /* if the ki_name field is used, clear it first */
1250 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1251 vfs_removename(ip
->ki_name
);
1253 /* free the expired entry */
1254 kheap_free(KM_KAUTH
, ip
, sizeof(struct kauth_identity
));
1260 * kauth_identity_updatecache
1262 * Description: Given a lookup result, add any associations that we don't
1263 * currently have; replace ones which have changed.
1265 * Parameters: elp External lookup result from
1266 * user space daemon to kernel
1267 * rkip pointer to returned kauth
1269 * extend_data Extended data (can vary)
1274 * *rkip Modified (if non-NULL)
1276 * Notes: For extended information requests, this code relies on the fact
1277 * that elp->el_flags is never used as an rvalue, and is only
1278 * ever bit-tested for valid lookup information we are willing
1281 * XXX: We may have to do the same in the case that extended data was
1282 * passed out to user space to ensure that the request string
1283 * gets cached; we may also be able to use the rkip as an
1284 * input to avoid this. The jury is still out.
1286 * XXX: This codes performance could be improved for multiple valid
1287 * results by combining the loop iteration in a single loop.
1290 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1293 struct kauth_identity
*kip
;
1294 const char *speculative_name
= NULL
;
1299 * If there is extended data, and that data represents a name rather
1300 * than something else, speculatively create an entry for it in the
1301 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1302 * over the allocation later.
1304 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1305 const char *tmp
= CAST_DOWN(const char *, extend_data
);
1306 speculative_name
= vfs_addname(tmp
, (uint32_t)strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1309 /* user identity? */
1310 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1311 KAUTH_IDENTITY_LOCK();
1312 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1313 /* matching record */
1314 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1315 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1316 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1317 if (elp
->el_sup_grp_cnt
> NGROUPS
) {
1318 KAUTH_DEBUG("CACHE - invalid sup_grp_cnt provided (%d), truncating to %d",
1319 elp
->el_sup_grp_cnt
, NGROUPS
);
1320 elp
->el_sup_grp_cnt
= NGROUPS
;
1322 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1323 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1324 kip
->ki_valid
|= KI_VALID_GROUPS
;
1325 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1327 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1328 kip
->ki_guid
= elp
->el_uguid
;
1329 kip
->ki_valid
|= KI_VALID_GUID
;
1331 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1332 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1333 kip
->ki_ntsid
= elp
->el_usid
;
1334 kip
->ki_valid
|= KI_VALID_NTSID
;
1336 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1337 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1338 const char *oname
= kip
->ki_name
;
1339 kip
->ki_name
= speculative_name
;
1340 speculative_name
= NULL
;
1341 kip
->ki_valid
|= KI_VALID_PWNAM
;
1344 * free oname (if any) outside
1347 speculative_name
= oname
;
1350 kauth_identity_lru(kip
);
1354 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1358 KAUTH_IDENTITY_UNLOCK();
1359 /* not found in cache, add new record */
1361 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1362 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1363 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1364 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1365 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1366 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1367 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1368 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1369 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1375 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1376 speculative_name
= NULL
;
1378 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1379 kauth_identity_register_and_free(kip
);
1384 /* group identity? (ignore, if we already processed it as a user) */
1385 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1386 KAUTH_IDENTITY_LOCK();
1387 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1388 /* matching record */
1389 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1390 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1391 kip
->ki_guid
= elp
->el_gguid
;
1392 kip
->ki_valid
|= KI_VALID_GUID
;
1394 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1395 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1396 kip
->ki_ntsid
= elp
->el_gsid
;
1397 kip
->ki_valid
|= KI_VALID_NTSID
;
1399 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1400 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1401 const char *oname
= kip
->ki_name
;
1402 kip
->ki_name
= speculative_name
;
1403 speculative_name
= NULL
;
1404 kip
->ki_valid
|= KI_VALID_GRNAM
;
1407 * free oname (if any) outside
1410 speculative_name
= oname
;
1413 kauth_identity_lru(kip
);
1417 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1421 KAUTH_IDENTITY_UNLOCK();
1422 /* not found in cache, add new record */
1424 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1425 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1426 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1427 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1428 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1429 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1430 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1431 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1432 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1438 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1439 speculative_name
= NULL
;
1441 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1442 kauth_identity_register_and_free(kip
);
1447 /* If we have a name reference to drop, drop it here */
1448 if (speculative_name
!= NULL
) {
1449 vfs_removename(speculative_name
);
1455 * Trim older entries from the identity cache.
1457 * Must be called with the identity cache lock held.
1460 kauth_identity_trimcache(int newsize
)
1462 struct kauth_identity
*kip
;
1464 lck_mtx_assert(&kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1466 while (kauth_identity_count
> newsize
) {
1467 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1468 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1469 kauth_identity_count
--;
1470 kheap_free(KM_KAUTH
, kip
, sizeof(struct kauth_identity
));
1475 * kauth_identity_lru
1477 * Description: Promote the entry to the head of the LRU, assumes the cache
1480 * Parameters: kip kauth identity to move to the
1481 * head of the LRU list, if it's
1486 * Notes: This is called even if the entry has expired; typically an
1487 * expired entry that's been looked up is about to be revalidated,
1488 * and having it closer to the head of the LRU means finding it
1489 * quickly again when the revalidation comes through.
1492 kauth_identity_lru(struct kauth_identity
*kip
)
1494 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1495 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1496 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1502 * kauth_identity_guid_expired
1504 * Description: Handle lazy expiration of GUID translations.
1506 * Parameters: kip kauth identity to check for
1509 * Returns: 1 Expired
1513 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1518 * Expiration time of 0 means this entry is persistent.
1520 if (kip
->ki_guid_expiry
== 0) {
1525 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1527 return (kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1532 * kauth_identity_ntsid_expired
1534 * Description: Handle lazy expiration of NTSID translations.
1536 * Parameters: kip kauth identity to check for
1539 * Returns: 1 Expired
1543 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1548 * Expiration time of 0 means this entry is persistent.
1550 if (kip
->ki_ntsid_expiry
== 0) {
1555 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1557 return (kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1561 * kauth_identity_groups_expired
1563 * Description: Handle lazy expiration of supplemental group translations.
1565 * Parameters: kip kauth identity to check for
1568 * Returns: 1 Expired
1572 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1577 * Expiration time of 0 means this entry is persistent.
1579 if (kip
->ki_groups_expiry
== 0) {
1584 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1586 return (kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0;
1590 * kauth_identity_find_uid
1592 * Description: Search for an entry by UID
1594 * Parameters: uid UID to find
1595 * kir Pointer to return area
1596 * getname Name buffer, if ki_name wanted
1602 * *klr Modified, if found
1605 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1607 struct kauth_identity
*kip
;
1609 KAUTH_IDENTITY_LOCK();
1610 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1611 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1612 kauth_identity_lru(kip
);
1613 /* Copy via structure assignment */
1615 /* If a name is wanted and one exists, copy it out */
1616 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1617 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1622 KAUTH_IDENTITY_UNLOCK();
1623 return (kip
== NULL
) ? ENOENT
: 0;
1628 * kauth_identity_find_gid
1630 * Description: Search for an entry by GID
1632 * Parameters: gid GID to find
1633 * kir Pointer to return area
1634 * getname Name buffer, if ki_name wanted
1640 * *klr Modified, if found
1643 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1645 struct kauth_identity
*kip
;
1647 KAUTH_IDENTITY_LOCK();
1648 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1649 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1650 kauth_identity_lru(kip
);
1651 /* Copy via structure assignment */
1653 /* If a name is wanted and one exists, copy it out */
1654 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1655 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1660 KAUTH_IDENTITY_UNLOCK();
1661 return (kip
== NULL
) ? ENOENT
: 0;
1666 * kauth_identity_find_guid
1668 * Description: Search for an entry by GUID
1670 * Parameters: guidp Pointer to GUID to find
1671 * kir Pointer to return area
1672 * getname Name buffer, if ki_name wanted
1678 * *klr Modified, if found
1680 * Note: The association may be expired, in which case the caller
1681 * may elect to call out to userland to revalidate.
1684 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1686 struct kauth_identity
*kip
;
1688 KAUTH_IDENTITY_LOCK();
1689 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1690 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1691 kauth_identity_lru(kip
);
1692 /* Copy via structure assignment */
1694 /* If a name is wanted and one exists, copy it out */
1695 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1696 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1701 KAUTH_IDENTITY_UNLOCK();
1702 return (kip
== NULL
) ? ENOENT
: 0;
1706 * kauth_identity_find_nam
1708 * Description: Search for an entry by name
1710 * Parameters: name Pointer to name to find
1711 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1712 * kir Pointer to return area
1718 * *klr Modified, if found
1721 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1723 struct kauth_identity
*kip
;
1725 KAUTH_IDENTITY_LOCK();
1726 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1727 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1728 kauth_identity_lru(kip
);
1729 /* Copy via structure assignment */
1734 KAUTH_IDENTITY_UNLOCK();
1735 return (kip
== NULL
) ? ENOENT
: 0;
1740 * kauth_identity_find_ntsid
1742 * Description: Search for an entry by NTSID
1744 * Parameters: ntsid Pointer to NTSID to find
1745 * kir Pointer to return area
1746 * getname Name buffer, if ki_name wanted
1752 * *klr Modified, if found
1754 * Note: The association may be expired, in which case the caller
1755 * may elect to call out to userland to revalidate.
1758 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1760 struct kauth_identity
*kip
;
1762 KAUTH_IDENTITY_LOCK();
1763 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1764 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1765 kauth_identity_lru(kip
);
1766 /* Copy via structure assignment */
1768 /* If a name is wanted and one exists, copy it out */
1769 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1770 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1775 KAUTH_IDENTITY_UNLOCK();
1776 return (kip
== NULL
) ? ENOENT
: 0;
1778 #endif /* CONFIG_EXT_RESOLVER */
1784 guid_t kauth_null_guid
;
1790 * Description: Determine the equality of two GUIDs
1792 * Parameters: guid1 Pointer to first GUID
1793 * guid2 Pointer to second GUID
1795 * Returns: 0 If GUIDs are unequal
1796 * !0 If GUIDs are equal
1799 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1801 return bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0;
1806 * kauth_wellknown_guid
1808 * Description: Determine if a GUID is a well-known GUID
1810 * Parameters: guid Pointer to GUID to check
1812 * Returns: KAUTH_WKG_NOT Not a well known GUID
1813 * KAUTH_WKG_EVERYBODY "Everybody"
1814 * KAUTH_WKG_NOBODY "Nobody"
1815 * KAUTH_WKG_OWNER "Other"
1816 * KAUTH_WKG_GROUP "Group"
1819 kauth_wellknown_guid(guid_t
*guid
)
1821 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1824 * All WKGs begin with the same 12 bytes.
1826 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1828 * The final 4 bytes are our code (in network byte order).
1830 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1833 return KAUTH_WKG_EVERYBODY
;
1835 return KAUTH_WKG_NOBODY
;
1837 return KAUTH_WKG_OWNER
;
1839 return KAUTH_WKG_GROUP
;
1842 return KAUTH_WKG_NOT
;
1849 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1851 * Parameters: sid1 Pointer to first NTSID
1852 * sid2 Pointer to second NTSID
1854 * Returns: 0 If GUIDs are unequal
1855 * !0 If GUIDs are equal
1858 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1860 /* check sizes for equality, also sanity-check size while we're at it */
1861 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1862 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1863 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0) {
1873 * We support four tokens representing identity:
1874 * - Credential reference
1877 * - NT security identifier
1879 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1886 * kauth_cred_change_egid
1888 * Description: Set EGID by changing the first element of cr_groups for the
1889 * passed credential; if the new EGID exists in the list of
1890 * groups already, then rotate the old EGID into its position,
1891 * otherwise replace it
1893 * Parameters: cred Pointer to the credential to modify
1894 * new_egid The new EGID to set
1896 * Returns: 0 The egid did not displace a member of
1897 * the supplementary group list
1898 * 1 The egid being set displaced a member
1899 * of the supplementary groups list
1901 * Note: Utility function; internal use only because of locking.
1903 * This function operates on the credential passed; the caller
1904 * must operate either on a newly allocated credential (one for
1905 * which there is no hash cache reference and no externally
1906 * visible pointer reference), or a template credential.
1909 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1915 #endif /* radar_4600026 */
1916 gid_t old_egid
= kauth_cred_getgid(cred
);
1917 posix_cred_t pcred
= posix_cred_get(cred
);
1919 /* Ignoring the first entry, scan for a match for the new egid */
1920 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1922 * If we find a match, swap them so we don't lose overall
1925 if (pcred
->cr_groups
[i
] == new_egid
) {
1926 pcred
->cr_groups
[i
] = old_egid
;
1927 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1934 #error Fix radar 4600026 first!!!
1937 * This is correct for memberd behaviour, but incorrect for POSIX; to address
1938 * this, we would need to automatically opt-out any SUID/SGID binary, and force
1939 * it to use initgroups to opt back in. We take the approach of considering it
1940 * opt'ed out in any group of 16 displacement instead, since it's a much more
1941 * conservative approach (i.e. less likely to cause things to break).
1945 * If we displaced a member of the supplementary groups list of the
1946 * credential, and we have not opted out of memberd, then if memberd
1947 * says that the credential is a member of the group, then it has not
1948 * actually been displaced.
1950 * NB: This is typically a cold code path.
1952 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1953 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1956 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1958 #endif /* radar_4600026 */
1960 /* set the new EGID into the old spot */
1961 pcred
->cr_groups
[0] = new_egid
;
1970 * Description: Fetch UID from credential
1972 * Parameters: cred Credential to examine
1974 * Returns: (uid_t) UID associated with credential
1977 kauth_cred_getuid(kauth_cred_t cred
)
1979 NULLCRED_CHECK(cred
);
1980 return posix_cred_get(cred
)->cr_uid
;
1985 * kauth_cred_getruid
1987 * Description: Fetch RUID from credential
1989 * Parameters: cred Credential to examine
1991 * Returns: (uid_t) RUID associated with credential
1994 kauth_cred_getruid(kauth_cred_t cred
)
1996 NULLCRED_CHECK(cred
);
1997 return posix_cred_get(cred
)->cr_ruid
;
2002 * kauth_cred_getsvuid
2004 * Description: Fetch SVUID from credential
2006 * Parameters: cred Credential to examine
2008 * Returns: (uid_t) SVUID associated with credential
2011 kauth_cred_getsvuid(kauth_cred_t cred
)
2013 NULLCRED_CHECK(cred
);
2014 return posix_cred_get(cred
)->cr_svuid
;
2021 * Description: Fetch GID from credential
2023 * Parameters: cred Credential to examine
2025 * Returns: (gid_t) GID associated with credential
2028 kauth_cred_getgid(kauth_cred_t cred
)
2030 NULLCRED_CHECK(cred
);
2031 return posix_cred_get(cred
)->cr_gid
;
2036 * kauth_cred_getrgid
2038 * Description: Fetch RGID from credential
2040 * Parameters: cred Credential to examine
2042 * Returns: (gid_t) RGID associated with credential
2045 kauth_cred_getrgid(kauth_cred_t cred
)
2047 NULLCRED_CHECK(cred
);
2048 return posix_cred_get(cred
)->cr_rgid
;
2053 * kauth_cred_getsvgid
2055 * Description: Fetch SVGID from credential
2057 * Parameters: cred Credential to examine
2059 * Returns: (gid_t) SVGID associated with credential
2062 kauth_cred_getsvgid(kauth_cred_t cred
)
2064 NULLCRED_CHECK(cred
);
2065 return posix_cred_get(cred
)->cr_svgid
;
2069 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2071 #if CONFIG_EXT_RESOLVER == 0
2073 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
2076 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2078 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2079 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2080 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2081 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2086 case KI_VALID_UID
: {
2087 id_t uid
= htonl(*(id_t
*)src
);
2089 if (to
== KI_VALID_GUID
) {
2091 memcpy(uu
, _user_compat_prefix
, sizeof(_user_compat_prefix
));
2092 memcpy(&uu
[COMPAT_PREFIX_LEN
], &uid
, sizeof(uid
));
2097 case KI_VALID_GID
: {
2098 id_t gid
= htonl(*(id_t
*)src
);
2100 if (to
== KI_VALID_GUID
) {
2102 memcpy(uu
, _group_compat_prefix
, sizeof(_group_compat_prefix
));
2103 memcpy(&uu
[COMPAT_PREFIX_LEN
], &gid
, sizeof(gid
));
2108 case KI_VALID_GUID
: {
2109 const uint8_t *uu
= src
;
2111 if (to
== KI_VALID_UID
) {
2112 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2114 memcpy(&uid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(uid
));
2115 *(id_t
*)dst
= ntohl(uid
);
2118 } else if (to
== KI_VALID_GID
) {
2119 if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2121 memcpy(&gid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(gid
));
2122 *(id_t
*)dst
= ntohl(gid
);
2129 /* NOT IMPLEMENTED */
2136 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2138 * Structure to hold supplemental groups. Used for impedance matching with
2139 * kauth_cred_cache_lookup below.
2147 * kauth_cred_uid2groups
2149 * Description: Fetch supplemental GROUPS from UID
2151 * Parameters: uid UID to examine
2152 * groups pointer to an array of gid_ts
2153 * gcount pointer to the number of groups wanted/returned
2155 * Returns: 0 Success
2156 * kauth_cred_cache_lookup:EINVAL
2159 * *groups Modified, if successful
2160 * *gcount Modified, if successful
2164 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, size_t *gcount
)
2168 struct supgroups supgroups
;
2169 supgroups
.count
= gcount
;
2170 supgroups
.groups
= groups
;
2172 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2179 * kauth_cred_guid2pwnam
2181 * Description: Fetch PWNAM from GUID
2183 * Parameters: guidp Pointer to GUID to examine
2184 * pwnam Pointer to user@domain buffer
2186 * Returns: 0 Success
2187 * kauth_cred_cache_lookup:EINVAL
2190 * *pwnam Modified, if successful
2192 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2195 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2197 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
);
2202 * kauth_cred_guid2grnam
2204 * Description: Fetch GRNAM from GUID
2206 * Parameters: guidp Pointer to GUID to examine
2207 * grnam Pointer to group@domain buffer
2209 * Returns: 0 Success
2210 * kauth_cred_cache_lookup:EINVAL
2213 * *grnam Modified, if successful
2215 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2218 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2220 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
);
2225 * kauth_cred_pwnam2guid
2227 * Description: Fetch PWNAM from GUID
2229 * Parameters: pwnam String containing user@domain
2230 * guidp Pointer to buffer for GUID
2232 * Returns: 0 Success
2233 * kauth_cred_cache_lookup:EINVAL
2236 * *guidp Modified, if successful
2238 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2239 * bytes in size, including the NUL termination of the string.
2242 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2244 return kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
);
2249 * kauth_cred_grnam2guid
2251 * Description: Fetch GRNAM from GUID
2253 * Parameters: grnam String containing group@domain
2254 * guidp Pointer to buffer for GUID
2256 * Returns: 0 Success
2257 * kauth_cred_cache_lookup:EINVAL
2260 * *guidp Modified, if successful
2262 * Notes: grnam should not point to a request larger than MAXPATHLEN
2263 * bytes in size, including the NUL termination of the string.
2266 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2268 return kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
);
2273 * kauth_cred_guid2uid
2275 * Description: Fetch UID from GUID
2277 * Parameters: guidp Pointer to GUID to examine
2278 * uidp Pointer to buffer for UID
2280 * Returns: 0 Success
2281 * kauth_cred_cache_lookup:EINVAL
2284 * *uidp Modified, if successful
2287 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2289 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
);
2294 * kauth_cred_guid2gid
2296 * Description: Fetch GID from GUID
2298 * Parameters: guidp Pointer to GUID to examine
2299 * gidp Pointer to buffer for GID
2301 * Returns: 0 Success
2302 * kauth_cred_cache_lookup:EINVAL
2305 * *gidp Modified, if successful
2308 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2310 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
);
2314 * kauth_cred_nfs4domain2dsnode
2316 * Description: Fetch dsnode from nfs4domain
2318 * Parameters: nfs4domain Pointer to a string nfs4 domain
2319 * dsnode Pointer to buffer for dsnode
2321 * Returns: 0 Success
2322 * ENOENT For now just a stub that always fails
2325 * *dsnode Modified, if successuful
2328 kauth_cred_nfs4domain2dsnode(__unused
char *nfs4domain
, __unused
char *dsnode
)
2334 * kauth_cred_dsnode2nfs4domain
2336 * Description: Fetch nfs4domain from dsnode
2338 * Parameters: nfs4domain Pointer to string dsnode
2339 * dsnode Pointer to buffer for nfs4domain
2341 * Returns: 0 Success
2342 * ENOENT For now just a stub that always fails
2345 * *nfs4domain Modified, if successuful
2348 kauth_cred_dsnode2nfs4domain(__unused
char *dsnode
, __unused
char *nfs4domain
)
2354 * kauth_cred_ntsid2uid
2356 * Description: Fetch UID from NTSID
2358 * Parameters: sidp Pointer to NTSID to examine
2359 * uidp Pointer to buffer for UID
2361 * Returns: 0 Success
2362 * kauth_cred_cache_lookup:EINVAL
2365 * *uidp Modified, if successful
2368 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2370 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
);
2375 * kauth_cred_ntsid2gid
2377 * Description: Fetch GID from NTSID
2379 * Parameters: sidp Pointer to NTSID to examine
2380 * gidp Pointer to buffer for GID
2382 * Returns: 0 Success
2383 * kauth_cred_cache_lookup:EINVAL
2386 * *gidp Modified, if successful
2389 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2391 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
);
2396 * kauth_cred_ntsid2guid
2398 * Description: Fetch GUID from NTSID
2400 * Parameters: sidp Pointer to NTSID to examine
2401 * guidp Pointer to buffer for GUID
2403 * Returns: 0 Success
2404 * kauth_cred_cache_lookup:EINVAL
2407 * *guidp Modified, if successful
2410 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2412 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
);
2417 * kauth_cred_uid2guid
2419 * Description: Fetch GUID from UID
2421 * Parameters: uid UID to examine
2422 * guidp Pointer to buffer for GUID
2424 * Returns: 0 Success
2425 * kauth_cred_cache_lookup:EINVAL
2428 * *guidp Modified, if successful
2431 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2433 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
);
2438 * kauth_cred_getguid
2440 * Description: Fetch GUID from credential
2442 * Parameters: cred Credential to examine
2443 * guidp Pointer to buffer for GUID
2445 * Returns: 0 Success
2446 * kauth_cred_cache_lookup:EINVAL
2449 * *guidp Modified, if successful
2452 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2454 NULLCRED_CHECK(cred
);
2455 return kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
);
2460 * kauth_cred_getguid
2462 * Description: Fetch GUID from GID
2464 * Parameters: gid GID to examine
2465 * guidp Pointer to buffer for GUID
2467 * Returns: 0 Success
2468 * kauth_cred_cache_lookup:EINVAL
2471 * *guidp Modified, if successful
2474 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2476 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
);
2481 * kauth_cred_uid2ntsid
2483 * Description: Fetch NTSID from UID
2485 * Parameters: uid UID to examine
2486 * sidp Pointer to buffer for NTSID
2488 * Returns: 0 Success
2489 * kauth_cred_cache_lookup:EINVAL
2492 * *sidp Modified, if successful
2495 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2497 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
);
2502 * kauth_cred_getntsid
2504 * Description: Fetch NTSID from credential
2506 * Parameters: cred Credential to examine
2507 * sidp Pointer to buffer for NTSID
2509 * Returns: 0 Success
2510 * kauth_cred_cache_lookup:EINVAL
2513 * *sidp Modified, if successful
2516 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2518 NULLCRED_CHECK(cred
);
2519 return kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
);
2524 * kauth_cred_gid2ntsid
2526 * Description: Fetch NTSID from GID
2528 * Parameters: gid GID to examine
2529 * sidp Pointer to buffer for NTSID
2531 * Returns: 0 Success
2532 * kauth_cred_cache_lookup:EINVAL
2535 * *sidp Modified, if successful
2538 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2540 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
);
2545 * kauth_cred_guid2ntsid
2547 * Description: Fetch NTSID from GUID
2549 * Parameters: guidp Pointer to GUID to examine
2550 * sidp Pointer to buffer for NTSID
2552 * Returns: 0 Success
2553 * kauth_cred_cache_lookup:EINVAL
2556 * *sidp Modified, if successful
2559 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2561 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
);
2566 * kauth_cred_cache_lookup
2568 * Description: Lookup a translation in the cache; if one is not found, and
2569 * the attempt was not fatal, submit the request to the resolver
2570 * instead, and wait for it to complete or be aborted.
2572 * Parameters: from Identity information we have
2573 * to Identity information we want
2574 * src Pointer to buffer containing
2575 * the source identity
2576 * dst Pointer to buffer to receive
2577 * the target identity
2579 * Returns: 0 Success
2580 * EINVAL Unknown source identity type
2582 #if CONFIG_EXT_RESOLVER
2584 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2586 struct kauth_identity ki
;
2587 struct kauth_identity_extlookup el
;
2589 uint64_t extend_data
= 0ULL;
2590 int (* expired
)(struct kauth_identity
*kip
);
2591 char *namebuf
= NULL
;
2593 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2596 * Look for an existing cache entry for this association.
2597 * If the entry has not expired, return the cached information.
2598 * We do not cache user@domain translations here; they use too
2599 * much memory to hold onto forever, and can not be updated
2602 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2612 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2615 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2618 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2620 case KI_VALID_NTSID
:
2621 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2623 case KI_VALID_PWNAM
:
2624 case KI_VALID_GRNAM
:
2625 /* Names are unique in their 'from' space */
2626 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2631 /* If we didn't get what we're asking for. Call the resolver */
2632 if (!error
&& !(to
& ki
.ki_valid
)) {
2635 /* lookup failure or error */
2637 /* any other error is fatal */
2638 if (error
!= ENOENT
) {
2639 /* XXX bogus check - this is not possible */
2640 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2644 /* found a valid cached entry, check expiry */
2647 expired
= kauth_identity_guid_expired
;
2649 case KI_VALID_NTSID
:
2650 expired
= kauth_identity_ntsid_expired
;
2652 case KI_VALID_GROUPS
:
2653 expired
= kauth_identity_groups_expired
;
2658 expired
= kauth_identity_guid_expired
;
2660 case KI_VALID_NTSID
:
2661 expired
= kauth_identity_ntsid_expired
;
2669 * If no expiry function, or not expired, we have found
2673 if (!expired(&ki
)) {
2674 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2675 expired
= NULL
; /* must clear it is used as a flag */
2678 * We leave ki_valid set here; it contains a
2679 * translation but the TTL has expired. If we can't
2680 * get a result from the resolver, we will use it as
2681 * a better-than nothing alternative.
2684 KAUTH_DEBUG("CACHE - expired entry found");
2687 KAUTH_DEBUG("CACHE - no expiry function");
2691 /* do we have a translation? */
2692 if (ki
.ki_valid
& to
) {
2693 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2694 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2698 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2699 * If we went looking for a translation from GUID or NTSID and
2700 * found a translation that wasn't for our desired type, then
2701 * don't bother calling the resolver. We know that this
2702 * GUID/NTSID can't translate to our desired type.
2706 case KI_VALID_NTSID
:
2709 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2710 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2715 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2716 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2728 * We failed to find a cache entry; call the resolver.
2730 * Note: We ask for as much non-extended data as we can get,
2731 * and only provide (or ask for) extended information if
2732 * we have a 'from' (or 'to') which requires it. This
2733 * way we don't pay for the extra transfer overhead for
2734 * data we don't need.
2736 bzero(&el
, sizeof(el
));
2737 el
.el_info_pid
= current_proc()->p_pid
;
2740 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2741 el
.el_uid
= *(uid_t
*)src
;
2744 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2745 el
.el_gid
= *(gid_t
*)src
;
2748 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2749 el
.el_uguid
= *(guid_t
*)src
;
2750 el
.el_gguid
= *(guid_t
*)src
;
2752 case KI_VALID_NTSID
:
2753 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2754 el
.el_usid
= *(ntsid_t
*)src
;
2755 el
.el_gsid
= *(ntsid_t
*)src
;
2757 case KI_VALID_PWNAM
:
2758 /* extra overhead */
2759 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2760 extend_data
= CAST_USER_ADDR_T(src
);
2762 case KI_VALID_GRNAM
:
2763 /* extra overhead */
2764 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2765 extend_data
= CAST_USER_ADDR_T(src
);
2771 * Here we ask for everything all at once, to avoid having to work
2772 * out what we really want now, or might want soon.
2774 * Asking for SID translations when we don't know we need them right
2775 * now is going to cause excess work to be done if we're connected
2776 * to a network that thinks it can translate them. This list needs
2777 * to get smaller/smarter.
2779 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2780 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2781 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2782 if (to
== KI_VALID_PWNAM
) {
2783 /* extra overhead */
2784 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2785 extend_data
= CAST_USER_ADDR_T(dst
);
2787 if (to
== KI_VALID_GRNAM
) {
2788 /* extra overhead */
2789 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2790 extend_data
= CAST_USER_ADDR_T(dst
);
2792 if (to
== KI_VALID_GROUPS
) {
2793 /* Expensive and only useful for an NFS client not using kerberos */
2794 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2795 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2797 * Copy the current supplemental groups for the resolver.
2798 * The resolver should check these groups first and if
2799 * the user (uid) is still a member it should endeavor to
2800 * keep them in the list. Otherwise NFS clients could get
2801 * changing access to server file system objects on each
2804 if (ki
.ki_supgrpcnt
> NGROUPS
) {
2805 panic("kauth data structure corrupted. kauth identity 0x%p with %u groups, greater than max of %d",
2806 &ki
, ki
.ki_supgrpcnt
, NGROUPS
);
2809 el
.el_sup_grp_cnt
= (uint32_t)ki
.ki_supgrpcnt
;
2811 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof(el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2812 /* Let the resolver know these were the previous valid groups */
2813 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2814 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2816 KAUTH_DEBUG("GROUPS: no valid groups to send");
2821 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2823 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2825 error
= kauth_resolver_submit(&el
, extend_data
);
2827 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2829 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2831 /* was the external lookup successful? */
2834 * Save the results from the lookup - we may have other
2835 * information, even if we didn't get a guid or the
2838 * If we came from a name, we know the extend_data is valid.
2840 if (from
== KI_VALID_PWNAM
) {
2841 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2842 } else if (from
== KI_VALID_GRNAM
) {
2843 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2846 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2849 * Check to see if we have a valid cache entry
2850 * originating from the result.
2852 if (!(ki
.ki_valid
& to
)) {
2861 * Copy from the appropriate struct kauth_identity cache entry
2862 * structure into the destination buffer area.
2866 *(uid_t
*)dst
= ki
.ki_uid
;
2869 *(gid_t
*)dst
= ki
.ki_gid
;
2872 *(guid_t
*)dst
= ki
.ki_guid
;
2874 case KI_VALID_NTSID
:
2875 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2877 case KI_VALID_GROUPS
: {
2878 struct supgroups
*gp
= (struct supgroups
*)dst
;
2879 size_t limit
= ki
.ki_supgrpcnt
;
2882 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2886 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2889 case KI_VALID_PWNAM
:
2890 case KI_VALID_GRNAM
:
2891 /* handled in kauth_resolver_complete() */
2896 KAUTH_DEBUG("CACHE - returned successfully");
2902 * Group membership cache.
2904 * XXX the linked-list implementation here needs to be optimized.
2908 * kauth_groups_expired
2910 * Description: Handle lazy expiration of group membership cache entries
2912 * Parameters: gm group membership entry to
2913 * check for expiration
2915 * Returns: 1 Expired
2919 kauth_groups_expired(struct kauth_group_membership
*gm
)
2924 * Expiration time of 0 means this entry is persistent.
2926 if (gm
->gm_expiry
== 0) {
2932 return (gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0;
2939 * Description: Promote the entry to the head of the LRU, assumes the cache
2942 * Parameters: kip group membership entry to move
2943 * to the head of the LRU list,
2944 * if it's not already there
2948 * Notes: This is called even if the entry has expired; typically an
2949 * expired entry that's been looked up is about to be revalidated,
2950 * and having it closer to the head of the LRU means finding it
2951 * quickly again when the revalidation comes through.
2954 kauth_groups_lru(struct kauth_group_membership
*gm
)
2956 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2957 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2958 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2964 * kauth_groups_updatecache
2966 * Description: Given a lookup result, add any group cache associations that
2967 * we don't currently have.
2969 * Parameters: elp External lookup result from
2970 * user space daemon to kernel
2971 * rkip pointer to returned kauth
2977 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2979 struct kauth_group_membership
*gm
;
2982 /* need a valid response if we are to cache anything */
2984 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2985 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) {
2992 * Search for an existing record for this association before inserting
2993 * a new one; if we find one, update it instead of creating a new one
2995 KAUTH_GROUPS_LOCK();
2996 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2997 if ((el
->el_uid
== gm
->gm_uid
) &&
2998 (el
->el_gid
== gm
->gm_gid
)) {
2999 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3000 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3002 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3004 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3005 kauth_groups_lru(gm
);
3009 KAUTH_GROUPS_UNLOCK();
3011 /* if we found an entry to update, stop here */
3016 /* allocate a new record */
3017 gm
= kheap_alloc(KM_KAUTH
, sizeof(struct kauth_group_membership
),
3020 gm
->gm_uid
= el
->el_uid
;
3021 gm
->gm_gid
= el
->el_gid
;
3022 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3023 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3025 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3027 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3031 * Insert the new entry. Note that it's possible to race ourselves
3032 * here and end up with duplicate entries in the list. Wasteful, but
3033 * harmless since the first into the list will never be looked up,
3034 * and thus will eventually just fall off the end.
3036 KAUTH_GROUPS_LOCK();
3037 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3038 if (++kauth_groups_count
> kauth_groups_cachemax
) {
3039 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3040 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3041 kauth_groups_count
--;
3045 KAUTH_GROUPS_UNLOCK();
3047 /* free expired cache entry */
3048 kheap_free(KM_KAUTH
, gm
, sizeof(struct kauth_group_membership
));
3052 * Trim older entries from the group membership cache.
3054 * Must be called with the group cache lock held.
3057 kauth_groups_trimcache(int new_size
)
3059 struct kauth_group_membership
*gm
;
3061 lck_mtx_assert(&kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
3063 while (kauth_groups_count
> new_size
) {
3064 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3065 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3066 kauth_groups_count
--;
3067 kheap_free(KM_KAUTH
, gm
, sizeof(struct kauth_group_membership
));
3070 #endif /* CONFIG_EXT_RESOLVER */
3073 * Group membership KPI
3077 * kauth_cred_ismember_gid
3079 * Description: Given a credential and a GID, determine if the GID is a member
3080 * of one of the supplementary groups associated with the given
3083 * Parameters: cred Credential to check in
3084 * gid GID to check for membership
3085 * resultp Pointer to int to contain the
3086 * result of the call
3088 * Returns: 0 Success
3089 * ENOENT Could not perform lookup
3090 * kauth_resolver_submit:EWOULDBLOCK
3091 * kauth_resolver_submit:EINTR
3092 * kauth_resolver_submit:ENOMEM
3093 * kauth_resolver_submit:ENOENT User space daemon did not vend
3095 * kauth_resolver_submit:??? Unlikely error from user space
3098 * *resultp (modified) 1 Is member
3101 * Notes: This function guarantees not to modify resultp when returning
3104 * This function effectively checks the EGID as well, since the
3105 * EGID is cr_groups[0] as an implementation detail.
3108 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3110 posix_cred_t pcred
= posix_cred_get(cred
);
3114 * Check the per-credential list of override groups.
3116 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3117 * the cache should be used for that case.
3119 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3120 if (gid
== pcred
->cr_groups
[i
]) {
3127 * If we don't have a UID for group membership checks, the in-cred list
3128 * was authoritative and we can stop here.
3130 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3135 #if CONFIG_EXT_RESOLVER
3136 struct kauth_group_membership
*gm
;
3137 struct kauth_identity_extlookup el
;
3141 * If the resolver hasn't checked in yet, we are early in the boot
3142 * phase and the local group list is complete and authoritative.
3144 if (!kauth_resolver_registered
) {
3150 /* XXX check supplementary groups */
3151 /* XXX check whiteout groups */
3152 /* XXX nesting of supplementary/whiteout groups? */
3155 * Check the group cache.
3157 KAUTH_GROUPS_LOCK();
3158 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3159 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3160 kauth_groups_lru(gm
);
3165 /* did we find a membership entry? */
3167 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3169 KAUTH_GROUPS_UNLOCK();
3171 /* if we did, we can return now */
3173 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3177 /* nothing in the cache, need to go to userland */
3178 bzero(&el
, sizeof(el
));
3179 el
.el_info_pid
= current_proc()->p_pid
;
3180 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3181 el
.el_uid
= pcred
->cr_gmuid
;
3183 el
.el_member_valid
= 0; /* XXX set by resolver? */
3185 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3187 error
= kauth_resolver_submit(&el
, 0ULL);
3189 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3194 /* save the results from the lookup */
3195 kauth_groups_updatecache(&el
);
3197 /* if we successfully ascertained membership, report */
3198 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3199 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3211 * kauth_cred_ismember_guid
3213 * Description: Determine whether the supplied credential is a member of the
3214 * group nominated by GUID.
3216 * Parameters: cred Credential to check in
3217 * guidp Pointer to GUID whose group
3218 * we are testing for membership
3219 * resultp Pointer to int to contain the
3220 * result of the call
3222 * Returns: 0 Success
3223 * kauth_cred_guid2gid:EINVAL
3224 * kauth_cred_ismember_gid:ENOENT
3225 * kauth_resolver_submit:ENOENT User space daemon did not vend
3227 * kauth_cred_ismember_gid:EWOULDBLOCK
3228 * kauth_cred_ismember_gid:EINTR
3229 * kauth_cred_ismember_gid:ENOMEM
3230 * kauth_cred_ismember_gid:??? Unlikely error from user space
3233 * *resultp (modified) 1 Is member
3237 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3241 switch (kauth_wellknown_guid(guidp
)) {
3242 case KAUTH_WKG_NOBODY
:
3245 case KAUTH_WKG_EVERYBODY
:
3251 #if CONFIG_EXT_RESOLVER
3252 struct kauth_identity ki
;
3255 * Grovel the identity cache looking for this GUID.
3256 * If we find it, and it is for a user record, return
3257 * false because it's not a group.
3259 * This is necessary because we don't have -ve caching
3260 * of group memberships, and we really want to avoid
3261 * calling out to the resolver if at all possible.
3263 * Because we're called by the ACL evaluator, and the
3264 * ACL evaluator is likely to encounter ACEs for users,
3265 * this is expected to be a common case.
3268 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3269 !kauth_identity_guid_expired(&ki
)) {
3270 if (ki
.ki_valid
& KI_VALID_GID
) {
3271 /* It's a group after all... */
3275 if (ki
.ki_valid
& KI_VALID_UID
) {
3280 #endif /* CONFIG_EXT_RESOLVER */
3282 * Attempt to translate the GUID to a GID. Even if
3283 * this fails, we will have primed the cache if it is
3284 * a user record and we'll see it above the next time
3287 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3289 * If we have no guid -> gid translation, it's not a group and
3290 * thus the cred can't be a member.
3292 if (error
== ENOENT
) {
3297 #if CONFIG_EXT_RESOLVER
3299 #endif /* CONFIG_EXT_RESOLVER */
3300 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3309 * kauth_cred_gid_subset
3311 * Description: Given two credentials, determine if all GIDs associated with
3312 * the first are also associated with the second
3314 * Parameters: cred1 Credential to check for
3315 * cred2 Credential to check in
3316 * resultp Pointer to int to contain the
3317 * result of the call
3319 * Returns: 0 Success
3320 * non-zero See kauth_cred_ismember_gid for
3324 * *resultp (modified) 1 Is subset
3327 * Notes: This function guarantees not to modify resultp when returning
3331 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3333 int i
, err
, res
= 1;
3335 posix_cred_t pcred1
= posix_cred_get(cred1
);
3336 posix_cred_t pcred2
= posix_cred_get(cred2
);
3338 /* First, check the local list of groups */
3339 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3340 gid
= pcred1
->cr_groups
[i
];
3341 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3345 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3351 /* Check real gid */
3352 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3356 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3357 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3362 /* Finally, check saved gid */
3363 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0) {
3367 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3368 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3379 * kauth_cred_issuser
3381 * Description: Fast replacement for issuser()
3383 * Parameters: cred Credential to check for super
3386 * Returns: 0 Not super user
3389 * Notes: This function uses a magic number which is not a manifest
3390 * constant; this is bad practice.
3393 kauth_cred_issuser(kauth_cred_t cred
)
3395 return kauth_cred_getuid(cred
) == 0;
3403 /* lock protecting credential hash table */
3404 static LCK_MTX_DECLARE(kauth_cred_hash_mtx
, &kauth_lck_grp
);
3405 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(&kauth_cred_hash_mtx);
3406 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(&kauth_cred_hash_mtx);
3407 #define KAUTH_CRED_HASH_LOCK_ASSERT() LCK_MTX_ASSERT(&kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3413 * Description: Initialize the credential hash cache
3415 * Parameters: (void)
3419 * Notes: Intialize the credential hash cache for use; the credential
3420 * hash cache is used convert duplicate credentials into a
3421 * single reference counted credential in order to save wired
3422 * kernel memory. In practice, this generally means a desktop
3423 * system runs with a few tens of credentials, instead of one
3424 * per process, one per thread, one per vnode cache entry, and
3425 * so on. This generally results in savings of 200K or more
3426 * (potentially much more on server systems).
3428 * The hash cache internally has a reference on the credential
3429 * for itself as a means of avoiding a reclaim race for a
3430 * credential in the process of having it's last non-hash
3431 * reference released. This would otherwise result in the
3432 * possibility of a freed credential that was still in uses due
3433 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3435 * On final release, the hash reference is droped, and the
3436 * credential is freed back to the system.
3438 * This function is called from kauth_init() in the file
3439 * kern_authorization.c.
3442 kauth_cred_init(void)
3444 for (int i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
3445 LIST_INIT(&kauth_cred_table_anchor
[i
]);
3453 * Description: Get the current thread's effective UID.
3455 * Parameters: (void)
3457 * Returns: (uid_t) The effective UID of the
3463 return kauth_cred_getuid(kauth_cred_get());
3470 * Description: Get the current thread's real UID.
3472 * Parameters: (void)
3474 * Returns: (uid_t) The real UID of the current
3480 return kauth_cred_getruid(kauth_cred_get());
3487 * Description: Get the current thread's effective GID.
3489 * Parameters: (void)
3491 * Returns: (gid_t) The effective GID of the
3497 return kauth_cred_getgid(kauth_cred_get());
3504 * Description: Get the current thread's real GID.
3506 * Parameters: (void)
3508 * Returns: (gid_t) The real GID of the current
3514 return kauth_cred_getrgid(kauth_cred_get());
3521 * Description: Returns a pointer to the current thread's credential
3523 * Parameters: (void)
3525 * Returns: (kauth_cred_t) Pointer to the current thread's
3528 * Notes: This function does not take a reference; because of this, the
3529 * caller MUST NOT do anything that would let the thread's
3530 * credential change while using the returned value, without
3531 * first explicitly taking their own reference.
3533 * If a caller intends to take a reference on the resulting
3534 * credential pointer from calling this function, it is strongly
3535 * recommended that the caller use kauth_cred_get_with_ref()
3536 * instead, to protect against any future changes to the cred
3537 * locking protocols; such changes could otherwise potentially
3538 * introduce race windows in the callers code.
3541 kauth_cred_get(void)
3544 struct uthread
*uthread
;
3546 uthread
= get_bsdthread_info(current_thread());
3548 if (uthread
== NULL
) {
3549 panic("thread wants credential but has no BSD thread info");
3552 * We can lazy-bind credentials to threads, as long as their processes
3555 * XXX If we later inline this function, the code in this block
3556 * XXX should probably be called out in a function.
3558 if (uthread
->uu_ucred
== NOCRED
) {
3559 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
) {
3560 panic("thread wants credential but has no BSD process");
3562 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3564 return uthread
->uu_ucred
;
3568 mach_kauth_cred_uthread_update(void)
3573 uthread
= get_bsdthread_info(current_thread());
3574 proc
= current_proc();
3576 kauth_cred_uthread_update(uthread
, proc
);
3580 * kauth_cred_uthread_update
3582 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3583 * late-bind the uthread cred to the proc cred.
3585 * Parameters: uthread_t The uthread to update
3586 * proc_t The process to update to
3590 * Notes: This code is common code called from system call or trap entry
3591 * in the case that the process thread may have been changed
3592 * since the last time the thread entered the kernel. It is
3593 * generally only called with the current uthread and process as
3597 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3599 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3600 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3601 kauth_cred_t old
= uthread
->uu_ucred
;
3602 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3603 if (IS_VALID_CRED(old
)) {
3604 kauth_cred_unref(&old
);
3611 * kauth_cred_get_with_ref
3613 * Description: Takes a reference on the current thread's credential, and then
3614 * returns a pointer to it to the caller.
3616 * Parameters: (void)
3618 * Returns: (kauth_cred_t) Pointer to the current thread's
3619 * newly referenced credential
3621 * Notes: This function takes a reference on the credential before
3622 * returning it to the caller.
3624 * It is the responsibility of the calling code to release this
3625 * reference when the credential is no longer in use.
3627 * Since the returned reference may be a persistent reference
3628 * (e.g. one cached in another data structure with a lifetime
3629 * longer than the calling function), this release may be delayed
3630 * until such time as the persistent reference is to be destroyed.
3631 * An example of this would be the per vnode credential cache used
3632 * to accelerate lookup operations.
3635 kauth_cred_get_with_ref(void)
3637 struct uthread
*uthread
= current_uthread();
3640 * We can lazy-bind credentials to threads, as long as their processes
3643 * XXX If we later inline this function, the code in this block
3644 * XXX should probably be called out in a function.
3646 if (uthread
->uu_ucred
== NOCRED
) {
3647 /* take reference for new cred in thread */
3648 uthread
->uu_ucred
= kauth_cred_proc_ref(current_proc());
3650 /* take a reference for our caller */
3651 kauth_cred_ref(uthread
->uu_ucred
);
3652 return uthread
->uu_ucred
;
3657 * kauth_cred_proc_ref
3659 * Description: Takes a reference on the current process's credential, and
3660 * then returns a pointer to it to the caller.
3662 * Parameters: procp Process whose credential we
3663 * intend to take a reference on
3665 * Returns: (kauth_cred_t) Pointer to the process's
3666 * newly referenced credential
3668 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3669 * after the refeence is taken to protect the p_ucred field of
3670 * the process referred to by procp.
3672 * Notes: This function takes a reference on the credential before
3673 * returning it to the caller.
3675 * It is the responsibility of the calling code to release this
3676 * reference when the credential is no longer in use.
3678 * Since the returned reference may be a persistent reference
3679 * (e.g. one cached in another data structure with a lifetime
3680 * longer than the calling function), this release may be delayed
3681 * until such time as the persistent reference is to be destroyed.
3682 * An example of this would be the per vnode credential cache used
3683 * to accelerate lookup operations.
3686 kauth_cred_proc_ref(proc_t procp
)
3690 proc_ucred_lock(procp
);
3691 cred
= proc_ucred(procp
);
3692 kauth_cred_ref(cred
);
3693 proc_ucred_unlock(procp
);
3700 * Description: Allocate a new credential
3702 * Parameters: (void)
3704 * Returns: !NULL Newly allocated credential
3705 * NULL Insufficient memory
3707 * Notes: The newly allocated credential is zero'ed as part of the
3708 * allocation process, with the exception of the reference
3709 * count, which is set to 0 to indicate the caller still has
3710 * to call kauth_cred_add().
3712 * Since newly allocated credentials have no external pointers
3713 * referencing them, prior to making them visible in an externally
3714 * visible pointer (e.g. by adding them to the credential hash
3715 * cache) is the only legal time in which an existing credential
3716 * can be safely iinitialized or modified directly.
3718 * After initialization, the caller is expected to call the
3719 * function kauth_cred_add() to add the credential to the hash
3720 * cache, after which time it's frozen and becomes publically
3723 * The release protocol depends on kauth_hash_add() being called
3724 * before kauth_cred_rele() (there is a diagnostic panic which
3725 * will trigger if this protocol is not observed).
3727 * XXX: This function really ought to be static, rather than being
3728 * exported as KPI, since a failure of kauth_cred_add() can only
3729 * be handled by an explicit free of the credential; such frees
3730 * depend on knowlegdge of the allocation method used, which is
3731 * permitted to change between kernel revisions.
3733 * XXX: In the insufficient resource case, this code panic's rather
3734 * than returning a NULL pointer; the code that calls this
3735 * function needs to be audited before this can be changed.
3738 kauth_cred_alloc(void)
3740 kauth_cred_t newcred
;
3742 newcred
= zalloc_flags(ucred_zone
, Z_WAITOK
| Z_ZERO
);
3743 posix_cred_get(newcred
)->cr_gmuid
= KAUTH_UID_NONE
;
3744 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3745 /* must do this, or cred has same group membership as uid 0 */
3747 mac_cred_label_init(newcred
);
3756 * Description: Destroy a credential
3758 * Parameters: cred Credential to destroy.
3761 kauth_cred_free(kauth_cred_t cred
)
3763 assert(os_atomic_load(&cred
->cr_ref
, relaxed
) == 0);
3765 mac_cred_label_destroy(cred
);
3767 AUDIT_SESSION_UNREF(cred
);
3768 zfree(ucred_zone
, cred
);
3774 * Description: Look to see if we already have a known credential in the hash
3775 * cache; if one is found, bump the reference count and return
3776 * it. If there are no credentials that match the given
3777 * credential, then allocate a new credential.
3779 * Parameters: cred Template for credential to
3782 * Returns: (kauth_cred_t) The credential that was found
3783 * in the hash or created
3784 * NULL kauth_cred_add() failed, or
3785 * there was not an egid specified
3787 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3788 * maintain this field, we can't expect callers to know how it
3789 * needs to be set. Callers should be prepared for this field
3790 * to be overwritten.
3793 kauth_cred_create(kauth_cred_t cred
)
3795 kauth_cred_t found_cred
, new_cred
= NULL
;
3796 posix_cred_t pcred
= posix_cred_get(cred
);
3799 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3800 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3803 * If the template credential is not opting out of external
3804 * group membership resolution, then we need to check that
3805 * the UID we will be using is resolvable by the external
3806 * resolver. If it's not, then we opt it out anyway, since
3807 * all future external resolution requests will be failing
3808 * anyway, and potentially taking a long time to do it. We
3809 * use gid 0 because we always know it will exist and not
3810 * trigger additional lookups. This is OK, because we end up
3811 * precatching the information here as a result.
3813 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3815 * It's a recognized value; we don't really care about
3816 * the answer, so long as it's something the external
3817 * resolver could have vended.
3819 pcred
->cr_gmuid
= pcred
->cr_uid
;
3822 * It's not something the external resolver could
3823 * have vended, so we don't want to ask it more
3824 * questions about the credential in the future. This
3825 * speeds up future lookups, as long as the caller
3826 * caches results; otherwise, it the same recurring
3827 * cost. Since most credentials are used multiple
3828 * times, we still get some performance win from this.
3830 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3831 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3835 /* Caller *must* specify at least the egid in cr_groups[0] */
3836 if (pcred
->cr_ngroups
< 1) {
3840 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
3842 KAUTH_CRED_HASH_LOCK();
3843 found_cred
= kauth_cred_find_and_ref(cred
, bucket
);
3844 KAUTH_CRED_HASH_UNLOCK();
3845 if (found_cred
!= NULL
) {
3850 * No existing credential found. Create one and add it to
3853 new_cred
= kauth_cred_alloc();
3854 if (new_cred
!= NULL
) {
3855 *posix_cred_get(new_cred
) = *pcred
;
3857 new_cred
->cr_audit
= cred
->cr_audit
;
3859 new_cred
= kauth_cred_add(new_cred
, bucket
);
3867 * kauth_cred_setresuid
3869 * Description: Update the given credential using the UID arguments. The given
3870 * UIDs are used to set the effective UID, real UID, saved UID,
3871 * and GMUID (used for group membership checking).
3873 * Parameters: cred The original credential
3874 * ruid The new real UID
3875 * euid The new effective UID
3876 * svuid The new saved UID
3877 * gmuid KAUTH_UID_NONE -or- the new
3878 * group membership UID
3880 * Returns: (kauth_cred_t) The updated credential
3882 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3883 * setting, so if you don't want it to change, pass it the
3884 * previous value, explicitly.
3886 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3887 * if it returns a credential other than the one it is passed,
3888 * will have dropped the reference on the passed credential. All
3889 * callers should be aware of this, and treat this function as an
3890 * unref + ref, potentially on different credentials.
3892 * Because of this, the caller is expected to take its own
3893 * reference on the credential passed as the first parameter,
3894 * and be prepared to release the reference on the credential
3895 * that is returned to them, if it is not intended to be a
3896 * persistent reference.
3899 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3901 struct ucred temp_cred
;
3902 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3903 posix_cred_t pcred
= posix_cred_get(cred
);
3905 NULLCRED_CHECK(cred
);
3908 * We don't need to do anything if the UIDs we are changing are
3909 * already the same as the UIDs passed in
3911 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3912 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3913 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3914 (pcred
->cr_gmuid
== gmuid
)) {
3915 /* no change needed */
3920 * Look up in cred hash table to see if we have a matching credential
3921 * with the new values; this is done by calling kauth_cred_update().
3924 if (euid
!= KAUTH_UID_NONE
) {
3925 temp_pcred
->cr_uid
= euid
;
3927 if (ruid
!= KAUTH_UID_NONE
) {
3928 temp_pcred
->cr_ruid
= ruid
;
3930 if (svuid
!= KAUTH_UID_NONE
) {
3931 temp_pcred
->cr_svuid
= svuid
;
3935 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3936 * opt out of participation in external group resolution, unless we
3937 * unless we explicitly opt back in later.
3939 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3940 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3943 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
3948 * kauth_cred_setresgid
3950 * Description: Update the given credential using the GID arguments. The given
3951 * GIDs are used to set the effective GID, real GID, and saved
3954 * Parameters: cred The original credential
3955 * rgid The new real GID
3956 * egid The new effective GID
3957 * svgid The new saved GID
3959 * Returns: (kauth_cred_t) The updated credential
3961 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3962 * if it returns a credential other than the one it is passed,
3963 * will have dropped the reference on the passed credential. All
3964 * callers should be aware of this, and treat this function as an
3965 * unref + ref, potentially on different credentials.
3967 * Because of this, the caller is expected to take its own
3968 * reference on the credential passed as the first parameter,
3969 * and be prepared to release the reference on the credential
3970 * that is returned to them, if it is not intended to be a
3971 * persistent reference.
3974 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3976 struct ucred temp_cred
;
3977 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3978 posix_cred_t pcred
= posix_cred_get(cred
);
3980 NULLCRED_CHECK(cred
);
3981 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3984 * We don't need to do anything if the given GID are already the
3985 * same as the GIDs in the credential.
3987 if (pcred
->cr_groups
[0] == egid
&&
3988 pcred
->cr_rgid
== rgid
&&
3989 pcred
->cr_svgid
== svgid
) {
3990 /* no change needed */
3995 * Look up in cred hash table to see if we have a matching credential
3996 * with the new values; this is done by calling kauth_cred_update().
3999 if (egid
!= KAUTH_GID_NONE
) {
4000 /* displacing a supplementary group opts us out of memberd */
4001 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
4002 DEBUG_CRED_CHANGE("displaced!\n");
4003 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4004 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4006 DEBUG_CRED_CHANGE("not displaced\n");
4009 if (rgid
!= KAUTH_GID_NONE
) {
4010 temp_pcred
->cr_rgid
= rgid
;
4012 if (svgid
!= KAUTH_GID_NONE
) {
4013 temp_pcred
->cr_svgid
= svgid
;
4016 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4021 * Update the given credential with the given groups. We only allocate a new
4022 * credential when the given gid actually results in changes to the existing
4024 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4025 * which will be used for group membership checking.
4028 * kauth_cred_setgroups
4030 * Description: Update the given credential using the provide supplementary
4031 * group list and group membership UID
4033 * Parameters: cred The original credential
4034 * groups Pointer to gid_t array which
4035 * contains the new group list
4036 * groupcount The count of valid groups which
4037 * are contained in 'groups'
4038 * gmuid KAUTH_UID_NONE -or- the new
4039 * group membership UID
4041 * Returns: (kauth_cred_t) The updated credential
4043 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4044 * setting, so if you don't want it to change, pass it the
4045 * previous value, explicitly.
4047 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4048 * if it returns a credential other than the one it is passed,
4049 * will have dropped the reference on the passed credential. All
4050 * callers should be aware of this, and treat this function as an
4051 * unref + ref, potentially on different credentials.
4053 * Because of this, the caller is expected to take its own
4054 * reference on the credential passed as the first parameter,
4055 * and be prepared to release the reference on the credential
4056 * that is returned to them, if it is not intended to be a
4057 * persistent reference.
4059 * XXX: Changes are determined in ordinal order - if the caller passes
4060 * in the same groups list that is already present in the
4061 * credential, but the members are in a different order, even if
4062 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4063 * is considered a modification to the credential, and a new
4064 * credential is created.
4066 * This should perhaps be better optimized, but it is considered
4067 * to be the caller's problem.
4070 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, size_t groupcount
, uid_t gmuid
)
4073 struct ucred temp_cred
;
4074 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4077 NULLCRED_CHECK(cred
);
4078 assert(groupcount
<= NGROUPS
);
4079 groupcount
= MIN(groupcount
, NGROUPS
);
4081 pcred
= posix_cred_get(cred
);
4084 * We don't need to do anything if the given list of groups does not
4087 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4088 for (i
= 0; i
< groupcount
; i
++) {
4089 if (pcred
->cr_groups
[i
] != groups
[i
]) {
4093 if (i
== groupcount
) {
4094 /* no change needed */
4100 * Look up in cred hash table to see if we have a matching credential
4101 * with new values. If we are setting or clearing the gmuid, then
4102 * update the cr_flags, since clearing it is sticky. This permits an
4103 * opt-out of memberd processing using setgroups(), and an opt-in
4104 * using initgroups(). This is required for POSIX conformance.
4107 temp_pcred
->cr_ngroups
= (short)groupcount
;
4108 bcopy(groups
, temp_pcred
->cr_groups
, groupcount
* sizeof(temp_pcred
->cr_groups
[0]));
4109 temp_pcred
->cr_gmuid
= gmuid
;
4110 if (gmuid
== KAUTH_UID_NONE
) {
4111 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4113 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4116 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4120 * Notes: The return value exists to account for the possibility of a
4121 * kauth_cred_t without a POSIX label. This will be the case in
4122 * the future (see posix_cred_get() below, for more details).
4124 #if CONFIG_EXT_RESOLVER
4125 int kauth_external_supplementary_groups_supported
= 1;
4127 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4131 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, size_t *countp
)
4133 size_t limit
= NGROUPS
;
4136 pcred
= posix_cred_get(cred
);
4138 #if CONFIG_EXT_RESOLVER
4140 * If we've not opted out of using the resolver, then convert the cred to a list
4141 * of supplemental groups. We do this only if there has been a resolver to talk to,
4142 * since we may be too early in boot, or in an environment that isn't using DS.
4144 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4145 uid_t uid
= kauth_cred_getuid(cred
);
4148 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4153 /* On error just fall through */
4154 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4156 #endif /* CONFIG_EXT_RESOLVER */
4159 * If they just want a copy of the groups list, they may not care
4160 * about the actual count. If they specify an input count, however,
4161 * treat it as an indicator of the buffer size available in grouplist,
4162 * and limit the returned list to that size.
4165 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4169 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4176 * kauth_cred_setuidgid
4178 * Description: Update the given credential using the UID and GID arguments.
4179 * The given UID is used to set the effective UID, real UID, and
4180 * saved UID. The given GID is used to set the effective GID,
4181 * real GID, and saved GID.
4183 * Parameters: cred The original credential
4184 * uid The new UID to use
4185 * gid The new GID to use
4187 * Returns: (kauth_cred_t) The updated credential
4189 * Notes: We set the gmuid to uid if the credential we are inheriting
4190 * from has not opted out of memberd participation; otherwise
4191 * we set it to KAUTH_UID_NONE
4193 * This code is only ever called from the per-thread credential
4194 * code path in the "set per thread credential" case; and in
4195 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4198 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4199 * if it returns a credential other than the one it is passed,
4200 * will have dropped the reference on the passed credential. All
4201 * callers should be aware of this, and treat this function as an
4202 * unref + ref, potentially on different credentials.
4204 * Because of this, the caller is expected to take its own
4205 * reference on the credential passed as the first parameter,
4206 * and be prepared to release the reference on the credential
4207 * that is returned to them, if it is not intended to be a
4208 * persistent reference.
4211 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4213 struct ucred temp_cred
;
4214 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4217 NULLCRED_CHECK(cred
);
4219 pcred
= posix_cred_get(cred
);
4222 * We don't need to do anything if the effective, real and saved
4223 * user IDs are already the same as the user ID passed into us.
4225 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4226 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4227 /* no change needed */
4232 * Look up in cred hash table to see if we have a matching credential
4233 * with the new values.
4235 bzero(&temp_cred
, sizeof(temp_cred
));
4236 temp_pcred
->cr_uid
= uid
;
4237 temp_pcred
->cr_ruid
= uid
;
4238 temp_pcred
->cr_svuid
= uid
;
4239 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4240 /* inherit the opt-out of memberd */
4241 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4242 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4243 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4245 temp_pcred
->cr_gmuid
= uid
;
4246 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4248 temp_pcred
->cr_ngroups
= 1;
4249 /* displacing a supplementary group opts us out of memberd */
4250 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4251 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4252 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4254 temp_pcred
->cr_rgid
= gid
;
4255 temp_pcred
->cr_svgid
= gid
;
4257 temp_cred
.cr_label
= cred
->cr_label
;
4260 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4265 * kauth_cred_setsvuidgid
4267 * Description: Function used by execve to set the saved uid and gid values
4268 * for suid/sgid programs
4270 * Parameters: cred The credential to update
4271 * uid The saved uid to set
4272 * gid The saved gid to set
4274 * Returns: (kauth_cred_t) The updated credential
4276 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4277 * if it returns a credential other than the one it is passed,
4278 * will have dropped the reference on the passed credential. All
4279 * callers should be aware of this, and treat this function as an
4280 * unref + ref, potentially on different credentials.
4282 * Because of this, the caller is expected to take its own
4283 * reference on the credential passed as the first parameter,
4284 * and be prepared to release the reference on the credential
4285 * that is returned to them, if it is not intended to be a
4286 * persistent reference.
4289 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4291 struct ucred temp_cred
;
4292 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4295 NULLCRED_CHECK(cred
);
4297 pcred
= posix_cred_get(cred
);
4299 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4302 * We don't need to do anything if the effective, real and saved
4303 * uids are already the same as the uid provided. This check is
4304 * likely insufficient.
4306 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4307 /* no change needed */
4310 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4312 /* look up in cred hash table to see if we have a matching credential
4316 temp_pcred
->cr_svuid
= uid
;
4317 temp_pcred
->cr_svgid
= gid
;
4319 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4324 * kauth_cred_setauditinfo
4326 * Description: Update the given credential using the given au_session_t.
4328 * Parameters: cred The original credential
4329 * auditinfo_p Pointer to ne audit information
4331 * Returns: (kauth_cred_t) The updated credential
4333 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4334 * if it returns a credential other than the one it is passed,
4335 * will have dropped the reference on the passed credential. All
4336 * callers should be aware of this, and treat this function as an
4337 * unref + ref, potentially on different credentials.
4339 * Because of this, the caller is expected to take its own
4340 * reference on the credential passed as the first parameter,
4341 * and be prepared to release the reference on the credential
4342 * that is returned to them, if it is not intended to be a
4343 * persistent reference.
4346 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4348 struct ucred temp_cred
;
4350 NULLCRED_CHECK(cred
);
4353 * We don't need to do anything if the audit info is already the
4354 * same as the audit info in the credential provided.
4356 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4357 /* no change needed */
4362 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4364 return kauth_cred_update(cred
, &temp_cred
, FALSE
);
4369 * kauth_cred_label_update
4371 * Description: Update the MAC label associated with a credential
4373 * Parameters: cred The original credential
4374 * label The MAC label to set
4376 * Returns: (kauth_cred_t) The updated credential
4378 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4379 * if it returns a credential other than the one it is passed,
4380 * will have dropped the reference on the passed credential. All
4381 * callers should be aware of this, and treat this function as an
4382 * unref + ref, potentially on different credentials.
4384 * Because of this, the caller is expected to take its own
4385 * reference on the credential passed as the first parameter,
4386 * and be prepared to release the reference on the credential
4387 * that is returned to them, if it is not intended to be a
4388 * persistent reference.
4391 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4393 kauth_cred_t newcred
;
4394 struct ucred temp_cred
;
4398 mac_cred_label_init(&temp_cred
);
4399 mac_cred_label_associate(cred
, &temp_cred
);
4400 mac_cred_label_update(&temp_cred
, label
);
4402 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4403 mac_cred_label_destroy(&temp_cred
);
4408 * kauth_cred_label_update_execve
4410 * Description: Update the MAC label associated with a credential as
4413 * Parameters: cred The original credential
4415 * scriptl The script MAC label
4416 * execl The executable MAC label
4417 * disjointp Pointer to flag to set if old
4418 * and returned credentials are
4421 * Returns: (kauth_cred_t) The updated credential
4424 * *disjointp Set to 1 for disjoint creds
4426 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4427 * if it returns a credential other than the one it is passed,
4428 * will have dropped the reference on the passed credential. All
4429 * callers should be aware of this, and treat this function as an
4430 * unref + ref, potentially on different credentials.
4432 * Because of this, the caller is expected to take its own
4433 * reference on the credential passed as the first parameter,
4434 * and be prepared to release the reference on the credential
4435 * that is returned to them, if it is not intended to be a
4436 * persistent reference.
4441 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4442 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4443 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4445 kauth_cred_t newcred
;
4446 struct ucred temp_cred
;
4450 mac_cred_label_init(&temp_cred
);
4451 mac_cred_label_associate(cred
, &temp_cred
);
4452 mac_cred_label_update_execve(ctx
, &temp_cred
,
4453 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4454 macextensions
, disjointp
, labelupdateerror
);
4456 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4457 mac_cred_label_destroy(&temp_cred
);
4462 * kauth_proc_label_update
4464 * Description: Update the label inside the credential associated with the process.
4466 * Parameters: p The process to modify
4467 * label The label to place in the process credential
4469 * Notes: The credential associated with the process may change as a result
4470 * of this call. The caller should not assume the process reference to
4471 * the old credential still exists.
4474 kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4476 kauth_cred_t my_cred
, my_new_cred
;
4478 my_cred
= kauth_cred_proc_ref(p
);
4480 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4482 /* get current credential and take a reference while we muck with it */
4485 * Set the credential with new info. If there is no change,
4486 * we get back the same credential we passed in; if there is
4487 * a change, we drop the reference on the credential we
4488 * passed in. The subsequent compare is safe, because it is
4489 * a pointer compare rather than a contents compare.
4491 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4492 if (my_cred
!= my_new_cred
) {
4493 DEBUG_CRED_CHANGE("kauth_proc_setlabel_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
);
4497 * We need to protect for a race where another thread
4498 * also changed the credential after we took our
4499 * reference. If p_ucred has changed then we should
4500 * restart this again with the new cred.
4502 if (p
->p_ucred
!= my_cred
) {
4503 proc_ucred_unlock(p
);
4504 kauth_cred_unref(&my_new_cred
);
4505 my_cred
= kauth_cred_proc_ref(p
);
4509 p
->p_ucred
= my_new_cred
;
4510 /* update cred on proc */
4511 PROC_UPDATE_CREDS_ONPROC(p
);
4513 proc_ucred_unlock(p
);
4517 /* Drop old proc reference or our extra reference */
4518 kauth_cred_unref(&my_cred
);
4524 * kauth_proc_label_update_execve
4526 * Description: Update the label inside the credential associated with the
4527 * process as part of a transitioning execve. The label will
4528 * be updated by the policies as part of this processing, not
4529 * provided up front.
4531 * Parameters: p The process to modify
4532 * ctx The context of the exec
4533 * vp The vnode being exec'ed
4534 * scriptl The script MAC label
4535 * execl The executable MAC label
4536 * lupdateerror The error place holder for MAC label authority
4537 * to update about possible termination
4539 * Returns: 0 Label update did not make credential
4541 * 1 Label update caused credential to be
4544 * Notes: The credential associated with the process WILL change as a
4545 * result of this call. The caller should not assume the process
4546 * reference to the old credential still exists.
4550 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4551 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4552 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4554 kauth_cred_t my_cred
, my_new_cred
;
4555 my_cred
= kauth_cred_proc_ref(p
);
4557 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4559 /* get current credential and take a reference while we muck with it */
4562 * Set the credential with new info. If there is no change,
4563 * we get back the same credential we passed in; if there is
4564 * a change, we drop the reference on the credential we
4565 * passed in. The subsequent compare is safe, because it is
4566 * a pointer compare rather than a contents compare.
4568 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4569 if (my_cred
!= my_new_cred
) {
4570 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
);
4574 * We need to protect for a race where another thread
4575 * also changed the credential after we took our
4576 * reference. If p_ucred has changed then we should
4577 * restart this again with the new cred.
4579 if (p
->p_ucred
!= my_cred
) {
4580 proc_ucred_unlock(p
);
4581 kauth_cred_unref(&my_new_cred
);
4582 my_cred
= kauth_cred_proc_ref(p
);
4586 p
->p_ucred
= my_new_cred
;
4587 /* update cred on proc */
4588 PROC_UPDATE_CREDS_ONPROC(p
);
4589 proc_ucred_unlock(p
);
4593 /* Drop old proc reference or our extra reference */
4594 kauth_cred_unref(&my_cred
);
4599 * for temporary binary compatibility
4601 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4603 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4605 return kauth_cred_label_update(cred
, label
);
4608 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4610 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4612 return kauth_proc_label_update(p
, label
);
4618 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4619 * Since we cannot build our export lists based on the kernel configuration we need
4623 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4629 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4636 * for temporary binary compatibility
4638 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4640 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4645 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4647 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4654 // TODO: move to os_refcnt once the ABI issue is resolved
4656 #define KAUTH_CRED_REF_MAX 0x0ffffffful
4658 __attribute__((noinline
, cold
, noreturn
))
4660 kauth_cred_panic_resurrection(kauth_cred_t cred
)
4662 panic("kauth_cred_unref: cred %p resurrected", cred
);
4663 __builtin_unreachable();
4666 __attribute__((noinline
, cold
, noreturn
))
4668 kauth_cred_panic_over_released(kauth_cred_t cred
)
4670 panic("kauth_cred_unref: cred %p over-released", cred
);
4671 __builtin_unreachable();
4674 __attribute__((noinline
, cold
, noreturn
))
4676 kauth_cred_panic_over_retain(kauth_cred_t cred
)
4678 panic("kauth_cred_ref: cred %p over-retained", cred
);
4679 __builtin_unreachable();
4685 * Description: Tries to take a reference, used from kauth_cred_find_and_ref
4686 * to debounce the race with kauth_cred_unref.
4688 * Parameters: cred The credential to reference
4690 * Returns: (bool) Whether the reference was taken
4693 kauth_cred_tryref(kauth_cred_t cred
)
4695 u_long old_ref
, new_ref
;
4696 os_atomic_rmw_loop(&cred
->cr_ref
, old_ref
, new_ref
, relaxed
, {
4698 os_atomic_rmw_loop_give_up(return false);
4700 new_ref
= old_ref
+ 1;
4702 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4703 kauth_cred_panic_over_retain(cred
);
4712 * Description: Add a reference to the passed credential
4714 * Parameters: cred The credential to reference
4719 kauth_cred_ref(kauth_cred_t cred
)
4721 u_long old_ref
= os_atomic_inc_orig(&cred
->cr_ref
, relaxed
);
4723 if (__improbable(old_ref
< 1)) {
4724 kauth_cred_panic_resurrection(cred
);
4726 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4727 kauth_cred_panic_over_retain(cred
);
4732 * kauth_cred_unref_fast
4734 * Description: Release a credential reference.
4736 * Parameters: credp Pointer to address containing
4737 * credential to be freed
4739 * Returns: true This was the last reference.
4740 * false The object has more refs.
4744 kauth_cred_unref_fast(kauth_cred_t cred
)
4746 u_long old_ref
= os_atomic_dec_orig(&cred
->cr_ref
, relaxed
);
4748 if (__improbable(old_ref
<= 0)) {
4749 kauth_cred_panic_over_released(cred
);
4751 return old_ref
== 1;
4757 * Description: Release a credential reference.
4758 * Frees the credential if it is the last ref.
4760 * Parameters: credp Pointer to address containing
4761 * credential to be freed
4766 * *credp Set to NOCRED
4770 kauth_cred_unref(kauth_cred_t
*credp
)
4772 if (kauth_cred_unref_fast(*credp
)) {
4773 KAUTH_CRED_HASH_LOCK();
4774 kauth_cred_remove_locked(*credp
);
4775 KAUTH_CRED_HASH_UNLOCK();
4776 kauth_cred_free(*credp
);
4787 * Description: release a credential reference; when the last reference is
4788 * released, the credential will be freed
4790 * Parameters: cred Credential to release
4794 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4795 * clear the pointer in the caller to avoid multiple releases of
4796 * the same credential. The currently recommended interface is
4797 * kauth_cred_unref().
4800 kauth_cred_rele(kauth_cred_t cred
)
4802 kauth_cred_unref(&cred
);
4804 #endif /* !__LP64__ */
4810 * Description: Duplicate a credential via alloc and copy; the new credential
4813 * Parameters: cred The credential to duplicate
4815 * Returns: (kauth_cred_t) The duplicate credential
4817 * Notes: The typical value to calling this routine is if you are going
4818 * to modify an existing credential, and expect to need a new one
4819 * from the hash cache.
4821 * This should probably not be used in the majority of cases;
4822 * if you are using it instead of kauth_cred_create(), you are
4823 * likely making a mistake.
4825 * The newly allocated credential is copied as part of the
4826 * allocation process, with the exception of the reference
4827 * count, which is set to 0 to indicate the caller still has
4828 * to call kauth_cred_add().
4830 * Since newly allocated credentials have no external pointers
4831 * referencing them, prior to making them visible in an externally
4832 * visible pointer (e.g. by adding them to the credential hash
4833 * cache) is the only legal time in which an existing credential
4834 * can be safely initialized or modified directly.
4836 * After initialization, the caller is expected to call the
4837 * function kauth_cred_add() to add the credential to the hash
4838 * cache, after which time it's frozen and becomes publicly
4841 * The release protocol depends on kauth_hash_add() being called
4842 * before kauth_cred_rele() (there is a diagnostic panic which
4843 * will trigger if this protocol is not observed).
4847 kauth_cred_dup(kauth_cred_t cred
)
4849 kauth_cred_t newcred
;
4851 assert(cred
!= NOCRED
&& cred
!= FSCRED
);
4852 newcred
= kauth_cred_alloc();
4853 if (newcred
!= NULL
) {
4854 newcred
->cr_posix
= cred
->cr_posix
;
4856 newcred
->cr_audit
= cred
->cr_audit
;
4859 mac_cred_label_associate(cred
, newcred
);
4861 AUDIT_SESSION_REF(cred
);
4867 * kauth_cred_copy_real
4869 * Description: Returns a credential based on the passed credential but which
4870 * reflects the real rather than effective UID and GID.
4872 * Parameters: cred The credential from which to
4873 * derive the new credential
4875 * Returns: (kauth_cred_t) The copied credential
4877 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4878 * result, the caller is responsible for dropping BOTH the
4879 * additional reference on the passed cred (if any), and the
4880 * credential returned by this function. The drop should be
4881 * via the kauth_cred_unref() KPI.
4884 kauth_cred_copy_real(kauth_cred_t cred
)
4886 kauth_cred_t newcred
= NULL
, found_cred
;
4887 struct ucred temp_cred
;
4888 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4889 posix_cred_t pcred
= posix_cred_get(cred
);
4891 /* if the credential is already 'real', just take a reference */
4892 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4893 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4894 kauth_cred_ref(cred
);
4899 * Look up in cred hash table to see if we have a matching credential
4900 * with the new values.
4903 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4904 /* displacing a supplementary group opts us out of memberd */
4905 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4906 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4907 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4910 * If the cred is not opted out, make sure we are using the r/euid
4913 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
) {
4914 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4917 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
4919 KAUTH_CRED_HASH_LOCK();
4920 found_cred
= kauth_cred_find_and_ref(&temp_cred
, bucket
);
4921 KAUTH_CRED_HASH_UNLOCK();
4928 * Must allocate a new credential, copy in old credential
4929 * data and update the real user and group IDs.
4931 newcred
= kauth_cred_dup(&temp_cred
);
4932 return kauth_cred_add(newcred
, bucket
);
4939 * Description: Common code to update a credential
4941 * Parameters: old_cred Reference counted credential
4943 * model_cred Non-reference counted model
4944 * credential to apply to the
4945 * credential to be updated
4946 * retain_auditinfo Flag as to whether or not the
4947 * audit information should be
4948 * copied from the old_cred into
4951 * Returns: (kauth_cred_t) The updated credential
4953 * IMPORTANT: This function will potentially return a credential other than
4954 * the one it is passed, and if so, it will have dropped the
4955 * reference on the passed credential. All callers should be
4956 * aware of this, and treat this function as an unref + ref,
4957 * potentially on different credentials.
4959 * Because of this, the caller is expected to take its own
4960 * reference on the credential passed as the first parameter,
4961 * and be prepared to release the reference on the credential
4962 * that is returned to them, if it is not intended to be a
4963 * persistent reference.
4966 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4967 boolean_t retain_auditinfo
)
4972 * Make sure we carry the auditinfo forward to the new credential
4973 * unless we are actually updating the auditinfo.
4975 if (retain_auditinfo
) {
4976 model_cred
->cr_audit
= old_cred
->cr_audit
;
4979 if (kauth_cred_is_equal(old_cred
, model_cred
)) {
4983 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(model_cred
);
4985 KAUTH_CRED_HASH_LOCK();
4986 cred
= kauth_cred_find_and_ref(model_cred
, bucket
);
4989 * We found a hit, so we can get rid of the old_cred.
4990 * If we didn't, then we need to keep the old_cred around,
4991 * because `model_cred` has copies of things such as the cr_label
4992 * or audit session that it has not refcounts for.
4994 bool needs_free
= kauth_cred_unref_fast(old_cred
);
4996 kauth_cred_remove_locked(old_cred
);
4998 KAUTH_CRED_HASH_UNLOCK();
5000 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n",
5003 kauth_cred_free(old_cred
);
5008 KAUTH_CRED_HASH_UNLOCK();
5011 * Must allocate a new credential using the model. also
5012 * adds the new credential to the credential hash table.
5014 cred
= kauth_cred_dup(model_cred
);
5015 cred
= kauth_cred_add(cred
, bucket
);
5016 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n",
5021 * This can't be done before the kauth_cred_dup() as the model_cred
5022 * has pointers that old_cred owns references for.
5024 kauth_cred_unref(&old_cred
);
5032 * Description: Add the given credential to our credential hash table and
5033 * take an initial reference to account for the object being
5036 * Parameters: new_cred Credential to insert into cred
5037 * hash cache, or to destroy when
5038 * a collision is detected.
5040 * Returns: (kauth_thread_t) The inserted cred, or the
5041 * collision that was found.
5043 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5046 kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
)
5048 kauth_cred_t found_cred
;
5051 KAUTH_CRED_HASH_LOCK();
5052 found_cred
= kauth_cred_find_and_ref(new_cred
, bucket
);
5054 KAUTH_CRED_HASH_UNLOCK();
5055 kauth_cred_free(new_cred
);
5059 old_ref
= os_atomic_xchg(&new_cred
->cr_ref
, 1, relaxed
);
5061 panic("kauth_cred_add: invalid cred %p", new_cred
);
5064 /* insert the credential into the hash table */
5065 LIST_INSERT_HEAD(bucket
, new_cred
, cr_link
);
5067 KAUTH_CRED_HASH_UNLOCK();
5072 * kauth_cred_remove_locked
5074 * Description: Remove the given credential from our credential hash table.
5076 * Parameters: cred Credential to remove.
5078 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5081 kauth_cred_remove_locked(kauth_cred_t cred
)
5083 KAUTH_CRED_HASH_LOCK_ASSERT();
5085 if (cred
->cr_link
.le_prev
== NULL
) {
5086 panic("kauth_cred_unref: cred %p never added", cred
);
5089 LIST_REMOVE(cred
, cr_link
);
5093 * kauth_cred_is_equal
5095 * Description: Returns whether two credentions are identical.
5097 * Parameters: cred1 Credential to compare
5098 * cred2 Credential to compare
5100 * Returns: true Credentials are equal
5101 * false Credentials are different
5104 kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
)
5106 posix_cred_t pcred1
= posix_cred_get(cred1
);
5107 posix_cred_t pcred2
= posix_cred_get(cred2
);
5110 * don't worry about the label unless the flags in
5111 * either credential tell us to.
5113 if (memcmp(pcred1
, pcred2
, sizeof(*pcred1
))) {
5116 if (memcmp(&cred1
->cr_audit
, &cred2
->cr_audit
, sizeof(cred1
->cr_audit
))) {
5120 /* Note: we know the flags are equal, so we only need to test one */
5121 if (pcred1
->cr_flags
& CRF_MAC_ENFORCE
) {
5122 if (!mac_cred_label_is_equal(cred1
->cr_label
, cred2
->cr_label
)) {
5131 * kauth_cred_find_and_ref
5133 * Description: Using the given credential data, look for a match in our
5134 * credential hash table
5136 * Parameters: cred Credential to lookup in cred
5139 * Returns: NULL Not found
5140 * !NULL Matching credential already in
5141 * cred hash cache, with a +1 ref
5143 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5146 kauth_cred_find_and_ref(kauth_cred_t cred
, struct kauth_cred_entry_head
*bucket
)
5148 kauth_cred_t found_cred
;
5150 KAUTH_CRED_HASH_LOCK_ASSERT();
5152 /* Find cred in the credential hash table */
5153 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5154 if (kauth_cred_is_equal(found_cred
, cred
)) {
5156 * newer entries are inserted at the head,
5157 * no hit further in the chain can possibly
5158 * be successfully retained.
5160 if (!kauth_cred_tryref(found_cred
)) {
5173 * Description: This interface is sadly KPI but people can't possibly use it,
5174 * as they need to hold a lock that isn't exposed.
5176 * Parameters: cred Credential to lookup in cred
5179 * Returns: NULL Not found
5180 * !NULL Matching credential already in
5183 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5186 kauth_cred_find(kauth_cred_t cred
)
5188 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
5189 kauth_cred_t found_cred
;
5191 KAUTH_CRED_HASH_LOCK_ASSERT();
5193 /* Find cred in the credential hash table */
5194 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5195 if (kauth_cred_is_equal(found_cred
, cred
)) {
5205 * kauth_cred_get_bucket
5207 * Description: Generate a hash key using data that makes up a credential;
5208 * based on ElfHash. We hash on the entire credential data,
5209 * not including the ref count or the TAILQ, which are mutable;
5210 * everything else isn't.
5212 * Returns the bucket correspondong to this hash key.
5214 * Parameters: cred Credential for which hash is
5217 * Returns: (kauth_cred_entry_head *) Returned bucket.
5219 * Notes: When actually moving the POSIX credential into a real label,
5220 * remember to update this hash computation.
5222 static struct kauth_cred_entry_head
*
5223 kauth_cred_get_bucket(kauth_cred_t cred
)
5226 posix_cred_t pcred
= posix_cred_get(cred
);
5228 uint32_t hash_key
= 0;
5230 hash_key
= os_hash_jenkins_update(&cred
->cr_posix
,
5231 sizeof(struct posix_cred
), hash_key
);
5233 hash_key
= os_hash_jenkins_update(&cred
->cr_audit
,
5234 sizeof(struct au_session
), hash_key
);
5236 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5237 hash_key
= mac_cred_label_hash_update(cred
->cr_label
, hash_key
);
5239 #endif /* CONFIG_MACF */
5241 hash_key
= os_hash_jenkins_finish(hash_key
);
5242 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5243 return &kauth_cred_table_anchor
[hash_key
];
5248 **********************************************************************
5249 * The following routines will be moved to a policy_posix.c module at
5250 * some future point.
5251 **********************************************************************
5257 * Description: Helper function to create a kauth_cred_t credential that is
5258 * initally labelled with a specific POSIX credential label
5260 * Parameters: pcred The posix_cred_t to use as the initial
5263 * Returns: (kauth_cred_t) The credential that was found in the
5265 * NULL kauth_cred_add() failed, or there was
5266 * no egid specified, or we failed to
5267 * attach a label to the new credential
5269 * Notes: This function currently wraps kauth_cred_create(), and is the
5270 * only consumer of that ill-fated function, apart from bsd_init().
5271 * It exists solely to support the NFS server code creation of
5272 * credentials based on the over-the-wire RPC calls containing
5273 * traditional POSIX credential information being tunneled to
5274 * the server host from the client machine.
5276 * In the future, we hope this function goes away.
5278 * In the short term, it creates a temporary credential, puts
5279 * the POSIX information from NFS into it, and then calls
5280 * kauth_cred_create(), as an internal implementation detail.
5282 * If we have to keep it around in the medium term, it will
5283 * create a new kauth_cred_t, then label it with a POSIX label
5284 * corresponding to the contents of the kauth_cred_t. If the
5285 * policy_posix MACF module is not loaded, it will instead
5286 * substitute a posix_cred_t which GRANTS all access (effectively
5287 * a "root" credential) in order to not prevent NFS from working
5288 * in the case that we are not supporting POSIX credentials.
5291 posix_cred_create(posix_cred_t pcred
)
5293 struct ucred temp_cred
;
5295 bzero(&temp_cred
, sizeof(temp_cred
));
5296 temp_cred
.cr_posix
= *pcred
;
5298 return kauth_cred_create(&temp_cred
);
5305 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5306 * any, which is associated with it.
5308 * Parameters: cred The credential to obtain the label from
5310 * Returns: posix_cred_t The POSIX credential label
5312 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5313 * this function will return a pointer to a posix_cred_t which
5314 * GRANTS all access (effectively, a "root" credential). This is
5315 * necessary to support legacy code which insists on tightly
5316 * integrating POSIX credentials into its APIs, including, but
5317 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5318 * NFSv3, signals, dtrace, and a large number of kauth routines
5319 * used to implement POSIX permissions related system calls.
5321 * In the event that the policy_posix MACF module IS loaded, and
5322 * there is no POSIX label on the kauth_cred_t credential, this
5323 * function will return a pointer to a posix_cred_t which DENIES
5324 * all access (effectively, a "deny rights granted by POSIX"
5325 * credential). This is necessary to support the concept of a
5326 * transiently loaded POSIX policy, or kauth_cred_t credentials
5327 * which can not be used in conjunctions with POSIX permissions
5330 * This function currently returns the address of the cr_posix
5331 * field of the supplied kauth_cred_t credential, and as such
5332 * currently can not fail. In the future, this will not be the
5336 posix_cred_get(kauth_cred_t cred
)
5338 return &cred
->cr_posix
;
5345 * Description: Label a kauth_cred_t with a POSIX credential label
5347 * Parameters: cred The credential to label
5348 * pcred The POSIX credential t label it with
5352 * Notes: This function is currently void in order to permit it to fit
5353 * in with the current MACF framework label methods which allow
5354 * labeling to fail silently. This is like acceptable for
5355 * mandatory access controls, but not for POSIX, since those
5356 * access controls are advisory. We will need to consider a
5357 * return value in a future version of the MACF API.
5359 * This operation currently cannot fail, as currently the POSIX
5360 * credential is a subfield of the kauth_cred_t (ucred), which
5361 * MUST be valid. In the future, this will not be the case.
5364 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5366 cred
->cr_posix
= *pcred
; /* structure assign for now */
5373 * Description: Perform a POSIX access check for a protected object
5375 * Parameters: cred The credential to check
5376 * object_uid The POSIX UID of the protected object
5377 * object_gid The POSIX GID of the protected object
5378 * object_mode The POSIX mode of the protected object
5379 * mode_req The requested POSIX access rights
5381 * Returns 0 Access is granted
5382 * EACCES Access is denied
5384 * Notes: This code optimizes the case where the world and group rights
5385 * would both grant the requested rights to avoid making a group
5386 * membership query. This is a big performance win in the case
5387 * where this is true.
5390 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5393 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5394 mode_t mode_group
= (mode_t
)((object_mode
& S_IRWXG
) << 3);
5395 mode_t mode_world
= (mode_t
)((object_mode
& S_IRWXO
) << 6);
5398 * Check first for owner rights
5400 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
) {
5405 * Combined group and world rights check, if we don't have owner rights
5407 * OPTIMIZED: If group and world rights would grant the same bits, and
5408 * they set of requested bits is in both, then we can simply check the
5409 * world rights, avoiding a group membership check, which is expensive.
5411 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5415 * NON-OPTIMIZED: requires group membership check.
5417 if ((mode_req
& mode_group
) != mode_req
) {
5419 * exclusion group : treat errors as "is a member"
5421 * NON-OPTIMIZED: +group would deny; must check group
5423 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5425 * DENY: +group denies
5429 if ((mode_req
& mode_world
) != mode_req
) {
5431 * DENY: both -group & world would deny
5436 * ALLOW: allowed by -group and +world
5443 * inclusion group; treat errors as "not a member"
5445 * NON-OPTIMIZED: +group allows, world denies; must
5448 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5450 * ALLOW: allowed by +group
5454 if ((mode_req
& mode_world
) != mode_req
) {
5456 * DENY: both -group & world would deny
5461 * ALLOW: allowed by -group and +world