2 * Copyright (c) 2004 Apple Computer, 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@
30 * Kernel Authorization framework: Management of process/thread credentials and identity information.
34 #include <sys/param.h> /* XXX trim includes */
36 #include <sys/systm.h>
37 #include <sys/ucred.h>
38 #include <sys/proc_internal.h>
40 #include <sys/timeb.h>
41 #include <sys/times.h>
42 #include <sys/malloc.h>
43 #include <sys/kauth.h>
44 #include <sys/kernel.h>
46 #include <bsm/audit_kernel.h>
48 #include <sys/mount.h>
49 #include <sys/sysproto.h>
50 #include <mach/message.h>
51 #include <mach/host_security.h>
53 #include <libkern/OSAtomic.h>
55 #include <kern/task.h>
56 #include <kern/lock.h>
60 #define MACH_ASSERT 1 /* XXX so bogus */
61 #include <kern/assert.h>
63 #define CRED_DIAGNOSTIC 1
65 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("bad credential %p", _c);} while(0)
68 * Interface to external identity resolver.
70 * The architecture of the interface is simple; the external resolver calls in to
71 * get work, then calls back with completed work. It also calls us to let us know
72 * that it's (re)started, so that we can resubmit work if it times out.
75 static lck_mtx_t
*kauth_resolver_mtx
;
76 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
77 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
79 static volatile pid_t kauth_resolver_identity
;
80 static int kauth_resolver_registered
;
81 static uint32_t kauth_resolver_sequence
;
83 struct kauth_resolver_work
{
84 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
85 struct kauth_identity_extlookup kr_work
;
89 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
90 #define KAUTH_REQUEST_SUBMITTED (1<<1)
91 #define KAUTH_REQUEST_DONE (1<<2)
95 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
96 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
97 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
99 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
100 static int kauth_resolver_complete(user_addr_t message
);
101 static int kauth_resolver_getwork(user_addr_t message
);
103 #define KAUTH_CRED_PRIMES_COUNT 7
104 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = {97, 241, 397, 743, 1499, 3989, 7499};
105 static int kauth_cred_primes_index
= 0;
106 static int kauth_cred_table_size
= 0;
108 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
109 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
111 #define KAUTH_CRED_HASH_DEBUG 0
113 static int kauth_cred_add(kauth_cred_t new_cred
);
114 static void kauth_cred_remove(kauth_cred_t cred
);
115 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
116 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
117 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
118 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
120 #if KAUTH_CRED_HASH_DEBUG
121 static int kauth_cred_count
= 0;
122 static void kauth_cred_hash_print(void);
123 static void kauth_cred_print(kauth_cred_t cred
);
127 kauth_resolver_init(void)
129 TAILQ_INIT(&kauth_resolver_unsubmitted
);
130 TAILQ_INIT(&kauth_resolver_submitted
);
131 TAILQ_INIT(&kauth_resolver_done
);
132 kauth_resolver_sequence
= 31337;
133 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
137 * Allocate a work queue entry, submit the work and wait for completion.
139 * XXX do we want an 'interruptible' flag vs. always being interruptible?
142 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
144 struct kauth_resolver_work
*workp
, *killp
;
146 int error
, shouldfree
;
148 /* no point actually blocking if the resolver isn't up yet */
149 if (kauth_resolver_identity
== 0) {
151 * We've already waited an initial 30 seconds with no result.
152 * Sleep on a stack address so no one wakes us before timeout;
153 * we sleep a half a second in case we are a high priority
154 * process, so that memberd doesn't starve while we are in a
155 * tight loop between user and kernel, eating all the CPU.
157 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
158 if (kauth_resolver_identity
== 0) {
160 * if things haven't changed while we were asleep,
161 * tell the caller we couldn't get an authoritative
168 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
172 workp
->kr_work
= *lkp
;
174 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
175 workp
->kr_result
= 0;
178 * We insert the request onto the unsubmitted queue, the call in from the
179 * resolver will it to the submitted thread when appropriate.
181 KAUTH_RESOLVER_LOCK();
182 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
183 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
185 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
186 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
188 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
190 /* we could compute a better timeout here */
193 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
194 /* request has been completed? */
195 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
197 /* woken because the resolver has died? */
198 if (kauth_resolver_identity
== 0) {
206 /* if the request was processed, copy the result */
208 *lkp
= workp
->kr_work
;
211 * If the request timed out and was never collected, the resolver is dead and
212 * probably not coming back anytime soon. In this case we revert to no-resolver
213 * behaviour, and punt all the other sleeping requests to clear the backlog.
215 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
216 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
217 kauth_resolver_identity
= 0;
218 /* kill all the other requestes that are waiting as well */
219 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
221 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
225 /* drop our reference on the work item, and note whether we should free it or not */
226 if (--workp
->kr_refs
<= 0) {
227 /* work out which list we have to remove it from */
228 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
229 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
230 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
231 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
232 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
233 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
235 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
239 /* someone else still has a reference on this request */
242 /* collect request result */
244 error
= workp
->kr_result
;
245 KAUTH_RESOLVER_UNLOCK();
247 * If we dropped the last reference, free the request.
250 FREE(workp
, M_KAUTH
);
252 KAUTH_DEBUG("RESOLVER - returning %d", error
);
257 * System call interface for the external identity resolver.
260 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
262 int opcode
= uap
->opcode
;
263 user_addr_t message
= uap
->message
;
264 struct kauth_resolver_work
*workp
;
269 * New server registering itself.
271 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
272 new_id
= current_proc()->p_pid
;
273 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
274 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
277 KAUTH_RESOLVER_LOCK();
278 if (kauth_resolver_identity
!= new_id
) {
279 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
281 * We have a new server, so assume that all the old requests have been lost.
283 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
284 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
285 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
286 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
287 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
289 kauth_resolver_identity
= new_id
;
290 kauth_resolver_registered
= 1;
291 wakeup(&kauth_resolver_unsubmitted
);
293 KAUTH_RESOLVER_UNLOCK();
298 * Beyond this point, we must be the resolver process.
300 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
301 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
306 * Got a result returning?
308 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
309 if ((error
= kauth_resolver_complete(message
)) != 0)
314 * Caller wants to take more work?
316 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
317 if ((error
= kauth_resolver_getwork(message
)) != 0)
325 * Get work for a caller.
328 kauth_resolver_getwork(user_addr_t message
)
330 struct kauth_resolver_work
*workp
;
333 KAUTH_RESOLVER_LOCK();
335 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
336 error
= msleep(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0);
341 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
342 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
345 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
346 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
347 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
348 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
352 KAUTH_RESOLVER_UNLOCK();
357 * Return a result from userspace.
360 kauth_resolver_complete(user_addr_t message
)
362 struct kauth_identity_extlookup extl
;
363 struct kauth_resolver_work
*workp
;
366 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
367 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
371 KAUTH_RESOLVER_LOCK();
375 switch (extl
.el_result
) {
376 case KAUTH_EXTLOOKUP_INPROG
:
380 /* XXX this should go away once memberd is updated */
382 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
387 case KAUTH_EXTLOOKUP_SUCCESS
:
390 case KAUTH_EXTLOOKUP_FATAL
:
391 /* fatal error means the resolver is dead */
392 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
393 kauth_resolver_identity
= 0;
394 /* XXX should we terminate all outstanding requests? */
397 case KAUTH_EXTLOOKUP_BADRQ
:
398 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
401 case KAUTH_EXTLOOKUP_FAILURE
:
402 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
406 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
412 * In the case of a fatal error, we assume that the resolver will restart
413 * quickly and re-collect all of the outstanding requests. Thus, we don't
414 * complete the request which returned the fatal error status.
416 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
417 /* scan our list for this request */
418 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
420 if (workp
->kr_seqno
== extl
.el_seqno
) {
422 workp
->kr_work
= extl
;
423 /* move onto completed list and wake up requester(s) */
424 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
425 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
426 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
427 workp
->kr_result
= result
;
428 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
435 * Note that it's OK for us not to find anything; if the request has
436 * timed out the work record will be gone.
438 KAUTH_RESOLVER_UNLOCK();
448 struct kauth_identity
{
449 TAILQ_ENTRY(kauth_identity
) ki_link
;
451 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
452 #define KI_VALID_GID (1<<1)
453 #define KI_VALID_GUID (1<<2)
454 #define KI_VALID_NTSID (1<<3)
460 * Expiry times are the earliest time at which we will disregard the cached state and go to
461 * userland. Before then if the valid bit is set, we will return the cached value. If it's
462 * not set, we will not go to userland to resolve, just assume that there is no answer
465 time_t ki_guid_expiry
;
466 time_t ki_ntsid_expiry
;
469 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
470 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
471 static int kauth_identity_count
;
473 static lck_mtx_t
*kauth_identity_mtx
;
474 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
475 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
478 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
479 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
480 static void kauth_identity_register(struct kauth_identity
*kip
);
481 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
482 static void kauth_identity_lru(struct kauth_identity
*kip
);
483 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
484 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
485 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
486 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
487 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
488 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
491 kauth_identity_init(void)
493 TAILQ_INIT(&kauth_identities
);
494 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
498 kauth_identity_resolve(__unused
struct kauth_identity_extlookup
*el
)
500 return(kauth_resolver_submit(el
));
503 static struct kauth_identity
*
504 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
506 struct kauth_identity
*kip
;
508 /* get and fill in a new identity */
509 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
511 if (gid
!= KAUTH_GID_NONE
) {
513 kip
->ki_valid
= KI_VALID_GID
;
515 if (uid
!= KAUTH_UID_NONE
) {
516 if (kip
->ki_valid
& KI_VALID_GID
)
517 panic("can't allocate kauth identity with both uid and gid");
519 kip
->ki_valid
= KI_VALID_UID
;
522 kip
->ki_guid
= *guidp
;
523 kip
->ki_valid
|= KI_VALID_GUID
;
525 kip
->ki_guid_expiry
= guid_expiry
;
526 if (ntsidp
!= NULL
) {
527 kip
->ki_ntsid
= *ntsidp
;
528 kip
->ki_valid
|= KI_VALID_NTSID
;
530 kip
->ki_ntsid_expiry
= ntsid_expiry
;
536 * Register an association between identity tokens.
539 kauth_identity_register(struct kauth_identity
*kip
)
541 struct kauth_identity
*ip
;
544 * We search the cache for the UID listed in the incoming association. If we
545 * already have an entry, the new information is merged.
548 KAUTH_IDENTITY_LOCK();
549 if (kip
->ki_valid
& KI_VALID_UID
) {
550 if (kip
->ki_valid
& KI_VALID_GID
)
551 panic("kauth_identity: can't insert record with both UID and GID as key");
552 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
553 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
555 } else if (kip
->ki_valid
& KI_VALID_GID
) {
556 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
557 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
560 panic("kauth_identity: can't insert record without UID or GID as key");
564 /* we already have an entry, merge/overwrite */
565 if (kip
->ki_valid
& KI_VALID_GUID
) {
566 ip
->ki_guid
= kip
->ki_guid
;
567 ip
->ki_valid
|= KI_VALID_GUID
;
569 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
570 if (kip
->ki_valid
& KI_VALID_NTSID
) {
571 ip
->ki_ntsid
= kip
->ki_ntsid
;
572 ip
->ki_valid
|= KI_VALID_NTSID
;
574 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
575 /* and discard the incoming identity */
579 /* don't have any information on this identity, so just add it */
580 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
581 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
582 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
583 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
584 kauth_identity_count
--;
587 KAUTH_IDENTITY_UNLOCK();
588 /* have to drop lock before freeing expired entry */
594 * Given a lookup result, add any associations that we don't
598 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
601 struct kauth_identity
*kip
;
606 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
607 KAUTH_IDENTITY_LOCK();
608 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
609 /* matching record */
610 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
611 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
612 kip
->ki_guid
= elp
->el_uguid
;
613 kip
->ki_valid
|= KI_VALID_GUID
;
615 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
616 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
617 kip
->ki_ntsid
= elp
->el_usid
;
618 kip
->ki_valid
|= KI_VALID_NTSID
;
620 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
621 kauth_identity_lru(kip
);
624 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
628 KAUTH_IDENTITY_UNLOCK();
629 /* not found in cache, add new record */
631 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
632 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
633 tv
.tv_sec
+ elp
->el_uguid_valid
,
634 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
635 tv
.tv_sec
+ elp
->el_usid_valid
);
639 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
640 kauth_identity_register(kip
);
645 /* group identity? */
646 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
647 KAUTH_IDENTITY_LOCK();
648 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
649 /* matching record */
650 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
651 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
652 kip
->ki_guid
= elp
->el_gguid
;
653 kip
->ki_valid
|= KI_VALID_GUID
;
655 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
656 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
657 kip
->ki_ntsid
= elp
->el_gsid
;
658 kip
->ki_valid
|= KI_VALID_NTSID
;
660 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
661 kauth_identity_lru(kip
);
664 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
668 KAUTH_IDENTITY_UNLOCK();
669 /* not found in cache, add new record */
671 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
672 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
673 tv
.tv_sec
+ elp
->el_gguid_valid
,
674 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
675 tv
.tv_sec
+ elp
->el_gsid_valid
);
679 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
680 kauth_identity_register(kip
);
688 * Promote the entry to the head of the LRU, assumes the cache is locked.
690 * This is called even if the entry has expired; typically an expired entry
691 * that's been looked up is about to be revalidated, and having it closer to
692 * the head of the LRU means finding it quickly again when the revalidation
696 kauth_identity_lru(struct kauth_identity
*kip
)
698 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
699 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
700 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
705 * Handly lazy expiration of translations.
708 kauth_identity_guid_expired(struct kauth_identity
*kip
)
713 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
714 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
718 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
723 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
724 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
728 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
729 * association exists for the UID.
732 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
734 struct kauth_identity
*kip
;
736 KAUTH_IDENTITY_LOCK();
737 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
738 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
739 kauth_identity_lru(kip
);
744 KAUTH_IDENTITY_UNLOCK();
745 return((kip
== NULL
) ? ENOENT
: 0);
750 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
751 * association exists for the GID.
754 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
756 struct kauth_identity
*kip
;
758 KAUTH_IDENTITY_LOCK();
759 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
760 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
761 kauth_identity_lru(kip
);
766 KAUTH_IDENTITY_UNLOCK();
767 return((kip
== NULL
) ? ENOENT
: 0);
772 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
773 * association exists for the GUID. Note that the association may be expired,
774 * in which case the caller may elect to call out to userland to revalidate.
777 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
779 struct kauth_identity
*kip
;
781 KAUTH_IDENTITY_LOCK();
782 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
783 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
784 kauth_identity_lru(kip
);
789 KAUTH_IDENTITY_UNLOCK();
790 return((kip
== NULL
) ? ENOENT
: 0);
794 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
795 * association exists for the SID. Note that the association may be expired,
796 * in which case the caller may elect to call out to userland to revalidate.
799 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
801 struct kauth_identity
*kip
;
803 KAUTH_IDENTITY_LOCK();
804 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
805 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
806 kauth_identity_lru(kip
);
811 KAUTH_IDENTITY_UNLOCK();
812 return((kip
== NULL
) ? ENOENT
: 0);
818 guid_t kauth_null_guid
;
821 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
823 return(!bcmp(guid1
, guid2
, sizeof(*guid1
)));
827 * Look for well-known GUIDs.
830 kauth_wellknown_guid(guid_t
*guid
)
832 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
835 * All WKGs begin with the same 12 bytes.
837 if (!bcmp((void *)guid
, fingerprint
, 12)) {
839 * The final 4 bytes are our code.
841 code
= *(u_int32_t
*)&guid
->g_guid
[12];
844 return(KAUTH_WKG_EVERYBODY
);
846 return(KAUTH_WKG_NOBODY
);
848 return(KAUTH_WKG_OWNER
);
850 return(KAUTH_WKG_GROUP
);
853 return(KAUTH_WKG_NOT
);
858 * NT Security Identifier handling.
861 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
863 /* check sizes for equality, also sanity-check size while we're at it */
864 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
865 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
866 !bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)))
874 * We support four tokens representing identity:
875 * - Credential reference
878 * - NT security identifier
880 * Of these, the UID is the ubiquitous identifier; cross-referencing should
884 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
887 * Fetch UID from credential.
890 kauth_cred_getuid(kauth_cred_t cred
)
892 NULLCRED_CHECK(cred
);
893 return(cred
->cr_uid
);
897 * Fetch GID from credential.
900 kauth_cred_getgid(kauth_cred_t cred
)
902 NULLCRED_CHECK(cred
);
903 return(cred
->cr_gid
);
907 * Fetch UID from GUID.
910 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
912 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
916 * Fetch GID from GUID.
919 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
921 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
925 * Fetch UID from NT SID.
928 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
930 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
934 * Fetch GID from NT SID.
937 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
939 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
943 * Fetch GUID from NT SID.
946 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
948 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
952 * Fetch GUID from UID.
955 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
957 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
961 * Fetch user GUID from credential.
964 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
966 NULLCRED_CHECK(cred
);
967 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
971 * Fetch GUID from GID.
974 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
976 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
980 * Fetch NT SID from UID.
983 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
985 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
989 * Fetch NT SID from credential.
992 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
994 NULLCRED_CHECK(cred
);
995 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
999 * Fetch NT SID from GID.
1002 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1004 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1008 * Fetch NT SID from GUID.
1011 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1013 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1019 * Lookup a translation in the cache.
1022 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1024 struct kauth_identity ki
;
1025 struct kauth_identity_extlookup el
;
1027 int (* expired
)(struct kauth_identity
*kip
);
1029 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1032 * Look for an existing cache entry for this association.
1033 * If the entry has not expired, return the cached information.
1038 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1041 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1044 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1046 case KI_VALID_NTSID
:
1047 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1052 /* lookup failure or error */
1054 /* any other error is fatal */
1055 if (error
!= ENOENT
) {
1056 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1060 /* do we have a translation? */
1061 if (ki
.ki_valid
& to
) {
1062 /* found a valid cached entry, check expiry */
1065 expired
= kauth_identity_guid_expired
;
1067 case KI_VALID_NTSID
:
1068 expired
= kauth_identity_ntsid_expired
;
1073 expired
= kauth_identity_guid_expired
;
1075 case KI_VALID_NTSID
:
1076 expired
= kauth_identity_ntsid_expired
;
1082 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1084 * If no expiry function, or not expired, we have found
1088 KAUTH_DEBUG("CACHE - no expiry function");
1091 if (!expired(&ki
)) {
1092 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1096 * We leave ki_valid set here; it contains a translation but the TTL has
1097 * expired. If we can't get a result from the resolver, we will
1098 * use it as a better-than nothing alternative.
1100 KAUTH_DEBUG("CACHE - expired entry found");
1105 * Call the resolver. We ask for as much data as we can get.
1109 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1110 el
.el_uid
= *(uid_t
*)src
;
1113 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1114 el
.el_gid
= *(gid_t
*)src
;
1117 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1118 el
.el_uguid
= *(guid_t
*)src
;
1119 el
.el_gguid
= *(guid_t
*)src
;
1121 case KI_VALID_NTSID
:
1122 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1123 el
.el_usid
= *(ntsid_t
*)src
;
1124 el
.el_gsid
= *(ntsid_t
*)src
;
1130 * Here we ask for everything all at once, to avoid having to work
1131 * out what we really want now, or might want soon.
1133 * Asking for SID translations when we don't know we need them right
1134 * now is going to cause excess work to be done if we're connected
1135 * to a network that thinks it can translate them. This list needs
1136 * to get smaller/smarter.
1138 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1139 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1140 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1141 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1142 error
= kauth_identity_resolve(&el
);
1143 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1144 /* was the lookup successful? */
1147 * Save the results from the lookup - may have other information even if we didn't
1150 kauth_identity_updatecache(&el
, &ki
);
1153 * Check to see if we have a valid result.
1155 if (!error
&& !(ki
.ki_valid
& to
))
1162 *(uid_t
*)dst
= ki
.ki_uid
;
1165 *(gid_t
*)dst
= ki
.ki_gid
;
1168 *(guid_t
*)dst
= ki
.ki_guid
;
1170 case KI_VALID_NTSID
:
1171 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1176 KAUTH_DEBUG("CACHE - returned successfully");
1182 * Group membership cache.
1184 * XXX the linked-list implementation here needs to be optimized.
1187 struct kauth_group_membership
{
1188 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1189 uid_t gm_uid
; /* the identity whose membership we're recording */
1190 gid_t gm_gid
; /* group of which they are a member */
1191 time_t gm_expiry
; /* TTL for the membership */
1193 #define KAUTH_GROUP_ISMEMBER (1<<0)
1196 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1197 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1198 static int kauth_groups_count
;
1200 static lck_mtx_t
*kauth_groups_mtx
;
1201 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1202 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1204 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1205 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1206 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1209 kauth_groups_init(void)
1211 TAILQ_INIT(&kauth_groups
);
1212 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1216 kauth_groups_expired(struct kauth_group_membership
*gm
)
1221 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1225 kauth_groups_lru(struct kauth_group_membership
*gm
)
1227 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1228 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1229 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1234 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1236 struct kauth_group_membership
*gm
;
1239 /* need a valid response if we are to cache anything */
1241 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1242 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1247 /* search for an existing record for this association before inserting */
1248 KAUTH_GROUPS_LOCK();
1249 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1250 if ((el
->el_uid
== gm
->gm_uid
) &&
1251 (el
->el_gid
== gm
->gm_gid
)) {
1252 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1253 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1255 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1257 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1258 kauth_groups_lru(gm
);
1262 KAUTH_GROUPS_UNLOCK();
1264 /* if we found an entry to update, stop here */
1268 /* allocate a new record */
1269 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
1271 gm
->gm_uid
= el
->el_uid
;
1272 gm
->gm_gid
= el
->el_gid
;
1273 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1274 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1276 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1278 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1282 * Insert the new entry. Note that it's possible to race ourselves here
1283 * and end up with duplicate entries in the list. Wasteful, but harmless
1284 * since the first into the list will never be looked up, and thus will
1285 * eventually just fall off the end.
1287 KAUTH_GROUPS_LOCK();
1288 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1289 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
1290 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
1291 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1292 kauth_groups_count
--;
1296 KAUTH_GROUPS_UNLOCK();
1298 /* free expired cache entry */
1304 * Group membership KPI
1307 * This function guarantees not to modify resultp when returning an error.
1310 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
1312 struct kauth_group_membership
*gm
;
1313 struct kauth_identity_extlookup el
;
1317 * Check the per-credential list of override groups.
1319 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1320 * the cache should be used for that case.
1322 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
1323 if (gid
== cred
->cr_groups
[i
]) {
1330 * If we don't have a UID for group membership checks, the in-cred list
1331 * was authoritative and we can stop here.
1333 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
1340 * If the resolver hasn't checked in yet, we are early in the boot phase and
1341 * the local group list is complete and authoritative.
1343 if (!kauth_resolver_registered
) {
1349 /* XXX check supplementary groups */
1350 /* XXX check whiteout groups */
1351 /* XXX nesting of supplementary/whiteout groups? */
1354 * Check the group cache.
1356 KAUTH_GROUPS_LOCK();
1357 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1358 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
1359 kauth_groups_lru(gm
);
1364 /* did we find a membership entry? */
1366 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
1367 KAUTH_GROUPS_UNLOCK();
1369 /* if we did, we can return now */
1373 /* nothing in the cache, need to go to userland */
1374 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
1375 el
.el_uid
= cred
->cr_gmuid
;
1377 error
= kauth_identity_resolve(&el
);
1380 /* save the results from the lookup */
1381 kauth_groups_updatecache(&el
);
1383 /* if we successfully ascertained membership, report */
1384 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
1385 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
1393 * Determine whether the supplied credential is a member of the
1394 * group nominated by GUID.
1397 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
1403 wkg
= kauth_wellknown_guid(guidp
);
1405 case KAUTH_WKG_NOBODY
:
1408 case KAUTH_WKG_EVERYBODY
:
1412 /* translate guid to gid */
1413 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
1415 * If we have no guid -> gid translation, it's not a group and
1416 * thus the cred can't be a member.
1418 if (error
== ENOENT
) {
1423 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
1430 * Fast replacement for issuser()
1433 kauth_cred_issuser(kauth_cred_t cred
)
1435 return(cred
->cr_uid
== 0);
1442 /* lock protecting credential hash table */
1443 static lck_mtx_t
*kauth_cred_hash_mtx
;
1444 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1445 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1446 #if KAUTH_CRED_HASH_DEBUG
1447 #define KAUTH_CRED_HASH_LOCK_ASSERT() _mutex_assert(kauth_cred_hash_mtx, MA_OWNED)
1448 #else /* !KAUTH_CRED_HASH_DEBUG */
1449 #define KAUTH_CRED_HASH_LOCK_ASSERT()
1450 #endif /* !KAUTH_CRED_HASH_DEBUG */
1453 kauth_cred_init(void)
1457 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1458 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
1460 /*allocate credential hash table */
1461 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
1462 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
1463 M_KAUTH
, M_WAITOK
| M_ZERO
);
1464 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
1465 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
1470 * Return the current thread's effective UID.
1475 return(kauth_cred_get()->cr_uid
);
1479 * Return the current thread's real UID.
1484 return(kauth_cred_get()->cr_ruid
);
1488 * Return the current thread's effective GID.
1493 return(kauth_cred_get()->cr_groups
[0]);
1497 * Return the current thread's real GID.
1502 return(kauth_cred_get()->cr_rgid
);
1506 * Returns a pointer to the current thread's credential, does not take a
1507 * reference (so the caller must not do anything that would let the thread's
1508 * credential change while using the returned value).
1511 kauth_cred_get(void)
1514 struct uthread
*uthread
;
1516 uthread
= get_bsdthread_info(current_thread());
1518 if (uthread
== NULL
)
1519 panic("thread wants credential but has no BSD thread info");
1521 * We can lazy-bind credentials to threads, as long as their processes have them.
1522 * If we later inline this function, the code in this block should probably be
1523 * called out in a function.
1525 if (uthread
->uu_ucred
== NOCRED
) {
1526 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1527 panic("thread wants credential but has no BSD process");
1528 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
1530 return(uthread
->uu_ucred
);
1534 * Returns a pointer to the current thread's credential, takes a reference.
1537 kauth_cred_get_with_ref(void)
1540 struct uthread
*uthread
;
1542 uthread
= get_bsdthread_info(current_thread());
1544 if (uthread
== NULL
)
1545 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
1546 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1547 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
1550 * We can lazy-bind credentials to threads, as long as their processes have them.
1551 * If we later inline this function, the code in this block should probably be
1552 * called out in a function.
1554 if (uthread
->uu_ucred
== NOCRED
) {
1555 /* take reference for new cred in thread */
1556 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
1558 /* take a reference for our caller */
1559 kauth_cred_ref(uthread
->uu_ucred
);
1560 return(uthread
->uu_ucred
);
1564 * Returns a pointer to the given process's credential, takes a reference.
1567 kauth_cred_proc_ref(proc_t procp
)
1572 cred
= proc_ucred(procp
);
1573 kauth_cred_ref(cred
);
1579 * Allocates a new credential.
1582 kauth_cred_alloc(void)
1584 kauth_cred_t newcred
;
1586 MALLOC(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
| M_ZERO
);
1588 newcred
->cr_ref
= 1;
1589 /* must do this, or cred has same group membership as uid 0 */
1590 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
1593 panic("kauth_cred_alloc: couldn't allocate credential");
1597 #if KAUTH_CRED_HASH_DEBUG
1605 * Looks to see if we already have a known credential and if found bumps the
1606 * reference count and returns it. If there are no credentials that match
1607 * the given credential then we allocate a new credential.
1609 * Note that the gmuid is hard-defaulted to the UID specified. Since we maintain
1610 * this field, we can't expect callers to know how it needs to be set. Callers
1611 * should be prepared for this field to be overwritten.
1614 kauth_cred_create(kauth_cred_t cred
)
1616 kauth_cred_t found_cred
, new_cred
= NULL
;
1618 cred
->cr_gmuid
= cred
->cr_uid
;
1621 KAUTH_CRED_HASH_LOCK();
1622 found_cred
= kauth_cred_find(cred
);
1623 if (found_cred
!= NULL
) {
1624 /* found an existing credential so we'll bump reference count and return */
1625 kauth_cred_ref(found_cred
);
1626 KAUTH_CRED_HASH_UNLOCK();
1629 KAUTH_CRED_HASH_UNLOCK();
1631 /* no existing credential found. create one and add it to our hash table */
1632 new_cred
= kauth_cred_alloc();
1633 if (new_cred
!= NULL
) {
1635 new_cred
->cr_uid
= cred
->cr_uid
;
1636 new_cred
->cr_ruid
= cred
->cr_ruid
;
1637 new_cred
->cr_svuid
= cred
->cr_svuid
;
1638 new_cred
->cr_rgid
= cred
->cr_rgid
;
1639 new_cred
->cr_svgid
= cred
->cr_svgid
;
1640 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
1641 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
1642 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
1643 KAUTH_CRED_HASH_LOCK();
1644 err
= kauth_cred_add(new_cred
);
1645 KAUTH_CRED_HASH_UNLOCK();
1647 /* retry if kauth_cred_add returns non zero value */
1650 FREE(new_cred
, M_CRED
);
1659 * Update the given credential using the uid argument. The given uid is used
1660 * set the effective user ID, real user ID, and saved user ID. We only
1661 * allocate a new credential when the given uid actually results in changes to
1662 * the existing credential.
1665 kauth_cred_setuid(kauth_cred_t cred
, uid_t uid
)
1667 struct ucred temp_cred
;
1669 NULLCRED_CHECK(cred
);
1671 /* don't need to do anything if the effective, real and saved user IDs are
1672 * already the same as the user ID passed in
1674 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
) {
1675 /* no change needed */
1679 /* look up in cred hash table to see if we have a matching credential
1682 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1683 temp_cred
.cr_uid
= uid
;
1684 temp_cred
.cr_ruid
= uid
;
1685 temp_cred
.cr_svuid
= uid
;
1686 temp_cred
.cr_gmuid
= uid
;
1688 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1692 * Update the given credential using the euid argument. The given uid is used
1693 * set the effective user ID. We only allocate a new credential when the given
1694 * uid actually results in changes to the existing credential.
1697 kauth_cred_seteuid(kauth_cred_t cred
, uid_t euid
)
1699 struct ucred temp_cred
;
1701 NULLCRED_CHECK(cred
);
1703 /* don't need to do anything if the given effective user ID is already the
1704 * same as the effective user ID in the credential.
1706 if (cred
->cr_uid
== euid
) {
1707 /* no change needed */
1711 /* look up in cred hash table to see if we have a matching credential
1714 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1715 temp_cred
.cr_uid
= euid
;
1717 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1721 * Update the given credential using the gid argument. The given gid is used
1722 * set the effective group ID, real group ID, and saved group ID. We only
1723 * allocate a new credential when the given gid actually results in changes to
1724 * the existing credential.
1727 kauth_cred_setgid(kauth_cred_t cred
, gid_t gid
)
1729 struct ucred temp_cred
;
1731 NULLCRED_CHECK(cred
);
1733 /* don't need to do anything if the given group ID is already the
1734 * same as the group ID in the credential.
1736 if (cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1737 /* no change needed */
1741 /* look up in cred hash table to see if we have a matching credential
1744 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1745 temp_cred
.cr_groups
[0] = gid
;
1746 temp_cred
.cr_rgid
= gid
;
1747 temp_cred
.cr_svgid
= gid
;
1749 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1754 * Update the given credential using the egid argument. The given gid is used
1755 * set the effective user ID. We only allocate a new credential when the given
1756 * gid actually results in changes to the existing credential.
1759 kauth_cred_setegid(kauth_cred_t cred
, gid_t egid
)
1761 struct ucred temp_cred
;
1763 NULLCRED_CHECK(cred
);
1765 /* don't need to do anything if the given group ID is already the
1766 * same as the group Id in the credential.
1768 if (cred
->cr_groups
[0] == egid
) {
1769 /* no change needed */
1773 /* look up in cred hash table to see if we have a matching credential
1776 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1777 temp_cred
.cr_groups
[0] = egid
;
1779 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1783 * Update the given credential with the given groups. We only allocate a new
1784 * credential when the given gid actually results in changes to the existing
1786 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1787 * which will be used for group membership checking.
1790 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
1793 struct ucred temp_cred
;
1795 NULLCRED_CHECK(cred
);
1797 /* don't need to do anything if the given list of groups does not change.
1799 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
1800 for (i
= 0; i
< groupcount
; i
++) {
1801 if (cred
->cr_groups
[i
] != groups
[i
])
1804 if (i
== groupcount
) {
1805 /* no change needed */
1810 /* look up in cred hash table to see if we have a matching credential
1813 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1814 temp_cred
.cr_ngroups
= groupcount
;
1815 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
1816 temp_cred
.cr_gmuid
= gmuid
;
1818 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1822 * Update the given credential using the uid and gid arguments. The given uid
1823 * is used set the effective user ID, real user ID, and saved user ID.
1824 * The given gid is used set the effective group ID, real group ID, and saved
1826 * We only allocate a new credential when the given uid and gid actually results
1827 * in changes to the existing credential.
1830 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1832 struct ucred temp_cred
;
1834 NULLCRED_CHECK(cred
);
1836 /* don't need to do anything if the effective, real and saved user IDs are
1837 * already the same as the user ID passed in
1839 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
1840 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1841 /* no change needed */
1845 /* look up in cred hash table to see if we have a matching credential
1848 bzero(&temp_cred
, sizeof(temp_cred
));
1849 temp_cred
.cr_uid
= uid
;
1850 temp_cred
.cr_ruid
= uid
;
1851 temp_cred
.cr_svuid
= uid
;
1852 temp_cred
.cr_gmuid
= uid
;
1853 temp_cred
.cr_ngroups
= 1;
1854 temp_cred
.cr_groups
[0] = gid
;
1855 temp_cred
.cr_rgid
= gid
;
1856 temp_cred
.cr_svgid
= gid
;
1858 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1862 * Update the given credential using the uid and gid arguments. The given uid
1863 * is used to set the saved user ID. The given gid is used to set the
1865 * We only allocate a new credential when the given uid and gid actually results
1866 * in changes to the existing credential.
1869 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1871 struct ucred temp_cred
;
1873 NULLCRED_CHECK(cred
);
1875 /* don't need to do anything if the effective, real and saved user IDs are
1876 * already the same as the user ID passed in
1878 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
1879 /* no change needed */
1883 /* look up in cred hash table to see if we have a matching credential
1886 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1887 temp_cred
.cr_svuid
= uid
;
1888 temp_cred
.cr_svgid
= gid
;
1890 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1894 * Update the given credential using the given auditinfo_t.
1895 * We only allocate a new credential when the given auditinfo_t actually results
1896 * in changes to the existing credential.
1899 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
1901 struct ucred temp_cred
;
1903 NULLCRED_CHECK(cred
);
1905 /* don't need to do anything if the audit info is already the same as the
1906 * audit info in the credential passed in
1908 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
1909 /* no change needed */
1913 /* look up in cred hash table to see if we have a matching credential
1916 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1917 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
1919 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
1923 * Add a reference to the passed credential.
1926 kauth_cred_ref(kauth_cred_t cred
)
1930 NULLCRED_CHECK(cred
);
1932 old_value
= OSAddAtomic(1, &cred
->cr_ref
);
1935 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1941 * Drop a reference from the passed credential, potentially destroying it.
1943 * Note: Assumes credential hash is NOT locked
1946 kauth_cred_unref(kauth_cred_t
*credp
)
1948 KAUTH_CRED_HASH_LOCK();
1949 kauth_cred_unref_hashlocked(credp
);
1950 KAUTH_CRED_HASH_UNLOCK();
1954 * Drop a reference from the passed credential, potentially destroying it.
1956 * Note: Assumes credential hash IS locked
1959 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
1963 KAUTH_CRED_HASH_LOCK_ASSERT();
1964 NULLCRED_CHECK(*credp
);
1965 old_value
= OSAddAtomic(-1, &(*credp
)->cr_ref
);
1969 panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
1971 panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
1974 if (old_value
< 3) {
1975 /* the last reference is our credential hash table */
1976 kauth_cred_remove(*credp
);
1982 kauth_cred_rele(kauth_cred_t cred
)
1984 kauth_cred_unref(&cred
);
1988 * Duplicate a credential.
1989 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1992 kauth_cred_dup(kauth_cred_t cred
)
1994 kauth_cred_t newcred
;
1997 if (cred
== NOCRED
|| cred
== FSCRED
)
1998 panic("kauth_cred_dup: bad credential");
2000 newcred
= kauth_cred_alloc();
2001 if (newcred
!= NULL
) {
2002 bcopy(cred
, newcred
, sizeof(*newcred
));
2003 newcred
->cr_ref
= 1;
2009 * Returns a credential based on the passed credential but which
2010 * reflects the real rather than effective UID and GID.
2011 * NOTE - we do NOT decrement cred reference count on passed in credential
2014 kauth_cred_copy_real(kauth_cred_t cred
)
2016 kauth_cred_t newcred
= NULL
, found_cred
;
2017 struct ucred temp_cred
;
2019 /* if the credential is already 'real', just take a reference */
2020 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
2021 (cred
->cr_rgid
== cred
->cr_gid
)) {
2022 kauth_cred_ref(cred
);
2026 /* look up in cred hash table to see if we have a matching credential
2029 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2030 temp_cred
.cr_uid
= cred
->cr_ruid
;
2031 temp_cred
.cr_groups
[0] = cred
->cr_rgid
;
2033 * if the cred is not opted out, make sure we are using the r/euid
2036 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
2037 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
2042 KAUTH_CRED_HASH_LOCK();
2043 found_cred
= kauth_cred_find(&temp_cred
);
2044 if (found_cred
== cred
) {
2045 /* same cred so just bail */
2046 KAUTH_CRED_HASH_UNLOCK();
2049 if (found_cred
!= NULL
) {
2051 * found a match so we bump reference count on new one.
2052 * we leave the old one alone.
2054 kauth_cred_ref(found_cred
);
2055 KAUTH_CRED_HASH_UNLOCK();
2059 /* must allocate a new credential, copy in old credential data and update
2060 * with real user and group IDs.
2062 newcred
= kauth_cred_dup(&temp_cred
);
2063 err
= kauth_cred_add(newcred
);
2064 KAUTH_CRED_HASH_UNLOCK();
2066 /* retry if kauth_cred_add returns non zero value */
2069 FREE(newcred
, M_CRED
);
2077 * Common code to update a credential. model_cred is a temporary, non
2078 * reference counted credential used only for comparison and modeling
2079 * purposes. old_cred is a live reference counted credential that we
2080 * intend to update using model_cred as our model.
2082 * IMPORTANT: If the old_cred ends up updated by this process, we will, as
2083 * a side effect, drop the reference we held on it going in.
2086 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
, boolean_t retain_auditinfo
)
2088 kauth_cred_t found_cred
, new_cred
= NULL
;
2090 /* make sure we carry the auditinfo forward to the new credential unless
2091 * we are actually updating the auditinfo.
2093 if (retain_auditinfo
)
2094 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
2099 KAUTH_CRED_HASH_LOCK();
2100 found_cred
= kauth_cred_find(model_cred
);
2101 if (found_cred
== old_cred
) {
2102 /* same cred so just bail */
2103 KAUTH_CRED_HASH_UNLOCK();
2106 if (found_cred
!= NULL
) {
2108 * found a match so we bump reference count on new
2109 * one and decrement reference count on the old one.
2111 kauth_cred_ref(found_cred
);
2112 kauth_cred_unref_hashlocked(&old_cred
);
2113 KAUTH_CRED_HASH_UNLOCK();
2117 /* must allocate a new credential using the model. also
2118 * adds the new credential to the credential hash table.
2120 new_cred
= kauth_cred_dup(model_cred
);
2121 err
= kauth_cred_add(new_cred
);
2122 KAUTH_CRED_HASH_UNLOCK();
2124 /* retry if kauth_cred_add returns non zero value */
2127 FREE(new_cred
, M_CRED
);
2131 kauth_cred_unref(&old_cred
);
2136 * Add the given credential to our credential hash table and take an additional
2137 * reference to account for our use of the credential in the hash table.
2138 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2141 kauth_cred_add(kauth_cred_t new_cred
)
2145 KAUTH_CRED_HASH_LOCK_ASSERT();
2147 hash_key
= kauth_cred_get_hashkey(new_cred
);
2148 hash_key
%= kauth_cred_table_size
;
2150 /* race fix - there is a window where another matching credential
2151 * could have been inserted between the time this one was created and we
2152 * got the hash lock. If we find a match return an error and have the
2155 if (kauth_cred_find(new_cred
) != NULL
) {
2159 /* take a reference for our use in credential hash table */
2160 kauth_cred_ref(new_cred
);
2162 /* insert the credential into the hash table */
2163 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
2170 * Remove the given credential from our credential hash table.
2171 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2174 kauth_cred_remove(kauth_cred_t cred
)
2177 kauth_cred_t found_cred
;
2179 KAUTH_CRED_HASH_LOCK_ASSERT();
2181 hash_key
= kauth_cred_get_hashkey(cred
);
2182 hash_key
%= kauth_cred_table_size
;
2185 if (cred
->cr_ref
< 1)
2186 panic("cred reference underflow");
2187 if (cred
->cr_ref
> 1) {
2188 return; /* someone else got a ref */
2191 /* find cred in the credential hash table */
2192 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2193 if (found_cred
== cred
) {
2194 /* found a match, remove it from the hash table */
2195 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
2197 #if KAUTH_CRED_HASH_DEBUG
2204 /* did not find a match. this should not happen! */
2205 printf("%s:%s - %d - %s - did not find a match for 0x%08x\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
2210 * Using the given credential data, look for a match in our credential hash
2212 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2215 kauth_cred_find(kauth_cred_t cred
)
2218 kauth_cred_t found_cred
;
2220 KAUTH_CRED_HASH_LOCK_ASSERT();
2222 #if KAUTH_CRED_HASH_DEBUG
2223 static int test_count
= 0;
2226 if ((test_count
% 200) == 0) {
2227 kauth_cred_hash_print();
2231 hash_key
= kauth_cred_get_hashkey(cred
);
2232 hash_key
%= kauth_cred_table_size
;
2234 /* find cred in the credential hash table */
2235 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2236 if (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
, (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
))) == 0) {
2241 /* no match found */
2246 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2249 kauth_cred_get_hashkey(kauth_cred_t cred
)
2251 u_long hash_key
= 0;
2253 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
2254 (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
)),
2260 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2262 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
2264 u_long hash_key
= start_key
;
2267 while (data_len
> 0) {
2268 hash_key
= (hash_key
<< 4) + *datap
++;
2269 temp
= hash_key
& 0xF0000000;
2271 hash_key
^= temp
>> 24;
2279 #if KAUTH_CRED_HASH_DEBUG
2280 static void kauth_cred_hash_print(void)
2283 kauth_cred_t found_cred
;
2285 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
2286 /* count slot hits, misses, collisions, and max depth */
2287 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2288 printf("[%02d] ", i
);
2290 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
2295 kauth_cred_print(found_cred
);
2299 printf("NOCRED \n");
2305 static void kauth_cred_print(kauth_cred_t cred
)
2309 printf("0x%02X - refs %d uids %d %d %d ", cred
, cred
->cr_ref
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
);
2310 printf("group count %d gids ", cred
->cr_ngroups
);
2311 for (i
= 0; i
< NGROUPS
; i
++) {
2312 printf("%d ", cred
->cr_groups
[i
]);
2314 printf("%d %d %d ", cred
->cr_rgid
, cred
->cr_svgid
, cred
->cr_gmuid
);
2315 printf("auditinfo %d %d %d %d %d %d ",
2316 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
2317 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);