2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
32 * Kernel Authorization framework: Management of process/thread credentials and identity information.
36 #include <sys/param.h> /* XXX trim includes */
38 #include <sys/systm.h>
39 #include <sys/ucred.h>
40 #include <sys/proc_internal.h>
42 #include <sys/timeb.h>
43 #include <sys/times.h>
44 #include <sys/malloc.h>
45 #include <sys/kauth.h>
46 #include <sys/kernel.h>
48 #include <bsm/audit_kernel.h>
50 #include <sys/mount.h>
51 #include <sys/sysproto.h>
52 #include <mach/message.h>
53 #include <mach/host_security.h>
55 #include <libkern/OSAtomic.h>
57 #include <kern/task.h>
58 #include <kern/lock.h>
62 #define MACH_ASSERT 1 /* XXX so bogus */
63 #include <kern/assert.h>
65 #define CRED_DIAGNOSTIC 1
67 # define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
70 * Interface to external identity resolver.
72 * The architecture of the interface is simple; the external resolver calls in to
73 * get work, then calls back with completed work. It also calls us to let us know
74 * that it's (re)started, so that we can resubmit work if it times out.
77 static lck_mtx_t
*kauth_resolver_mtx
;
78 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
79 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
81 static volatile pid_t kauth_resolver_identity
;
82 static int kauth_resolver_registered
;
83 static uint32_t kauth_resolver_sequence
;
85 struct kauth_resolver_work
{
86 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
87 struct kauth_identity_extlookup kr_work
;
91 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
92 #define KAUTH_REQUEST_SUBMITTED (1<<1)
93 #define KAUTH_REQUEST_DONE (1<<2)
97 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
98 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
99 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
101 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
102 static int kauth_resolver_complete(user_addr_t message
);
103 static int kauth_resolver_getwork(user_addr_t message
);
105 #define KAUTH_CRED_PRIMES_COUNT 7
106 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = {97, 241, 397, 743, 1499, 3989, 7499};
107 static int kauth_cred_primes_index
= 0;
108 static int kauth_cred_table_size
= 0;
110 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
111 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
113 #define KAUTH_CRED_HASH_DEBUG 0
115 static int kauth_cred_add(kauth_cred_t new_cred
);
116 static void kauth_cred_remove(kauth_cred_t cred
);
117 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
118 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
119 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
121 #if KAUTH_CRED_HASH_DEBUG
122 static int kauth_cred_count
= 0;
123 static void kauth_cred_hash_print(void);
124 static void kauth_cred_print(kauth_cred_t cred
);
128 kauth_resolver_init(void)
130 TAILQ_INIT(&kauth_resolver_unsubmitted
);
131 TAILQ_INIT(&kauth_resolver_submitted
);
132 TAILQ_INIT(&kauth_resolver_done
);
133 kauth_resolver_sequence
= 31337;
134 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
138 * Allocate a work queue entry, submit the work and wait for completion.
140 * XXX do we want an 'interruptible' flag vs. always being interruptible?
143 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
145 struct kauth_resolver_work
*workp
, *killp
;
147 int error
, shouldfree
;
149 /* no point actually blocking if the resolver isn't up yet */
150 if (kauth_resolver_identity
== 0) {
152 * We've already waited an initial 30 seconds with no result.
153 * Sleep on a stack address so no one wakes us before timeout;
154 * we sleep a half a second in case we are a high priority
155 * process, so that memberd doesn't starve while we are in a
156 * tight loop between user and kernel, eating all the CPU.
158 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
159 if (kauth_resolver_identity
== 0) {
161 * if things haven't changed while we were asleep,
162 * tell the caller we couldn't get an authoritative
169 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
173 workp
->kr_work
= *lkp
;
175 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
176 workp
->kr_result
= 0;
179 * We insert the request onto the unsubmitted queue, the call in from the
180 * resolver will it to the submitted thread when appropriate.
182 KAUTH_RESOLVER_LOCK();
183 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
184 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
186 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
187 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
189 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
191 /* we could compute a better timeout here */
194 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
195 /* request has been completed? */
196 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
198 /* woken because the resolver has died? */
199 if (kauth_resolver_identity
== 0) {
207 /* if the request was processed, copy the result */
209 *lkp
= workp
->kr_work
;
212 * If the request timed out and was never collected, the resolver is dead and
213 * probably not coming back anytime soon. In this case we revert to no-resolver
214 * behaviour, and punt all the other sleeping requests to clear the backlog.
216 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
217 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
218 kauth_resolver_identity
= 0;
219 /* kill all the other requestes that are waiting as well */
220 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
222 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
226 /* drop our reference on the work item, and note whether we should free it or not */
227 if (--workp
->kr_refs
<= 0) {
228 /* work out which list we have to remove it from */
229 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
230 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
231 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
232 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
233 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
234 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
236 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
240 /* someone else still has a reference on this request */
243 /* collect request result */
245 error
= workp
->kr_result
;
246 KAUTH_RESOLVER_UNLOCK();
248 * If we dropped the last reference, free the request.
251 FREE(workp
, M_KAUTH
);
253 KAUTH_DEBUG("RESOLVER - returning %d", error
);
258 * System call interface for the external identity resolver.
261 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
263 int opcode
= uap
->opcode
;
264 user_addr_t message
= uap
->message
;
265 struct kauth_resolver_work
*workp
;
270 * New server registering itself.
272 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
273 new_id
= current_proc()->p_pid
;
274 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
275 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
278 KAUTH_RESOLVER_LOCK();
279 if (kauth_resolver_identity
!= new_id
) {
280 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
282 * We have a new server, so assume that all the old requests have been lost.
284 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
285 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
286 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
287 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
288 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
290 kauth_resolver_identity
= new_id
;
291 kauth_resolver_registered
= 1;
292 wakeup(&kauth_resolver_unsubmitted
);
294 KAUTH_RESOLVER_UNLOCK();
299 * Beyond this point, we must be the resolver process.
301 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
302 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
307 * Got a result returning?
309 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
310 if ((error
= kauth_resolver_complete(message
)) != 0)
315 * Caller wants to take more work?
317 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
318 if ((error
= kauth_resolver_getwork(message
)) != 0)
326 * Get work for a caller.
329 kauth_resolver_getwork(user_addr_t message
)
331 struct kauth_resolver_work
*workp
;
334 KAUTH_RESOLVER_LOCK();
336 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
337 error
= msleep(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0);
342 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
343 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
346 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
347 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
348 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
349 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
353 KAUTH_RESOLVER_UNLOCK();
358 * Return a result from userspace.
361 kauth_resolver_complete(user_addr_t message
)
363 struct kauth_identity_extlookup extl
;
364 struct kauth_resolver_work
*workp
;
367 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
368 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
372 KAUTH_RESOLVER_LOCK();
376 switch (extl
.el_result
) {
377 case KAUTH_EXTLOOKUP_INPROG
:
381 /* XXX this should go away once memberd is updated */
383 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
388 case KAUTH_EXTLOOKUP_SUCCESS
:
391 case KAUTH_EXTLOOKUP_FATAL
:
392 /* fatal error means the resolver is dead */
393 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
394 kauth_resolver_identity
= 0;
395 /* XXX should we terminate all outstanding requests? */
398 case KAUTH_EXTLOOKUP_BADRQ
:
399 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
402 case KAUTH_EXTLOOKUP_FAILURE
:
403 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
407 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
413 * In the case of a fatal error, we assume that the resolver will restart
414 * quickly and re-collect all of the outstanding requests. Thus, we don't
415 * complete the request which returned the fatal error status.
417 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
418 /* scan our list for this request */
419 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
421 if (workp
->kr_seqno
== extl
.el_seqno
) {
423 workp
->kr_work
= extl
;
424 /* move onto completed list and wake up requester(s) */
425 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
426 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
427 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
428 workp
->kr_result
= result
;
429 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
436 * Note that it's OK for us not to find anything; if the request has
437 * timed out the work record will be gone.
439 KAUTH_RESOLVER_UNLOCK();
449 struct kauth_identity
{
450 TAILQ_ENTRY(kauth_identity
) ki_link
;
452 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
453 #define KI_VALID_GID (1<<1)
454 #define KI_VALID_GUID (1<<2)
455 #define KI_VALID_NTSID (1<<3)
461 * Expiry times are the earliest time at which we will disregard the cached state and go to
462 * userland. Before then if the valid bit is set, we will return the cached value. If it's
463 * not set, we will not go to userland to resolve, just assume that there is no answer
466 time_t ki_guid_expiry
;
467 time_t ki_ntsid_expiry
;
470 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
471 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
472 static int kauth_identity_count
;
474 static lck_mtx_t
*kauth_identity_mtx
;
475 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
476 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
479 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
480 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
481 static void kauth_identity_register(struct kauth_identity
*kip
);
482 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
483 static void kauth_identity_lru(struct kauth_identity
*kip
);
484 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
485 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
486 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
487 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
488 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
489 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
492 kauth_identity_init(void)
494 TAILQ_INIT(&kauth_identities
);
495 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
499 kauth_identity_resolve(__unused
struct kauth_identity_extlookup
*el
)
501 return(kauth_resolver_submit(el
));
504 static struct kauth_identity
*
505 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
507 struct kauth_identity
*kip
;
509 /* get and fill in a new identity */
510 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
512 if (gid
!= KAUTH_GID_NONE
) {
514 kip
->ki_valid
= KI_VALID_GID
;
516 if (uid
!= KAUTH_UID_NONE
) {
517 if (kip
->ki_valid
& KI_VALID_GID
)
518 panic("can't allocate kauth identity with both uid and gid");
520 kip
->ki_valid
= KI_VALID_UID
;
523 kip
->ki_guid
= *guidp
;
524 kip
->ki_valid
|= KI_VALID_GUID
;
526 kip
->ki_guid_expiry
= guid_expiry
;
527 if (ntsidp
!= NULL
) {
528 kip
->ki_ntsid
= *ntsidp
;
529 kip
->ki_valid
|= KI_VALID_NTSID
;
531 kip
->ki_ntsid_expiry
= ntsid_expiry
;
537 * Register an association between identity tokens.
540 kauth_identity_register(struct kauth_identity
*kip
)
542 struct kauth_identity
*ip
;
545 * We search the cache for the UID listed in the incoming association. If we
546 * already have an entry, the new information is merged.
549 KAUTH_IDENTITY_LOCK();
550 if (kip
->ki_valid
& KI_VALID_UID
) {
551 if (kip
->ki_valid
& KI_VALID_GID
)
552 panic("kauth_identity: can't insert record with both UID and GID as key");
553 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
554 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
556 } else if (kip
->ki_valid
& KI_VALID_GID
) {
557 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
558 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
561 panic("kauth_identity: can't insert record without UID or GID as key");
565 /* we already have an entry, merge/overwrite */
566 if (kip
->ki_valid
& KI_VALID_GUID
) {
567 ip
->ki_guid
= kip
->ki_guid
;
568 ip
->ki_valid
|= KI_VALID_GUID
;
570 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
571 if (kip
->ki_valid
& KI_VALID_NTSID
) {
572 ip
->ki_ntsid
= kip
->ki_ntsid
;
573 ip
->ki_valid
|= KI_VALID_NTSID
;
575 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
576 /* and discard the incoming identity */
580 /* don't have any information on this identity, so just add it */
581 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
582 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
583 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
584 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
585 kauth_identity_count
--;
588 KAUTH_IDENTITY_UNLOCK();
589 /* have to drop lock before freeing expired entry */
595 * Given a lookup result, add any associations that we don't
599 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
602 struct kauth_identity
*kip
;
607 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
608 KAUTH_IDENTITY_LOCK();
609 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
610 /* matching record */
611 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
612 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
613 kip
->ki_guid
= elp
->el_uguid
;
614 kip
->ki_valid
|= KI_VALID_GUID
;
616 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
617 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
618 kip
->ki_ntsid
= elp
->el_usid
;
619 kip
->ki_valid
|= KI_VALID_NTSID
;
621 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
622 kauth_identity_lru(kip
);
625 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
629 KAUTH_IDENTITY_UNLOCK();
630 /* not found in cache, add new record */
632 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
633 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
634 tv
.tv_sec
+ elp
->el_uguid_valid
,
635 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
636 tv
.tv_sec
+ elp
->el_usid_valid
);
640 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
641 kauth_identity_register(kip
);
646 /* group identity? */
647 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
648 KAUTH_IDENTITY_LOCK();
649 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
650 /* matching record */
651 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
652 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
653 kip
->ki_guid
= elp
->el_gguid
;
654 kip
->ki_valid
|= KI_VALID_GUID
;
656 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
657 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
658 kip
->ki_ntsid
= elp
->el_gsid
;
659 kip
->ki_valid
|= KI_VALID_NTSID
;
661 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
662 kauth_identity_lru(kip
);
665 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
669 KAUTH_IDENTITY_UNLOCK();
670 /* not found in cache, add new record */
672 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
673 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
674 tv
.tv_sec
+ elp
->el_gguid_valid
,
675 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
676 tv
.tv_sec
+ elp
->el_gsid_valid
);
680 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
681 kauth_identity_register(kip
);
689 * Promote the entry to the head of the LRU, assumes the cache is locked.
691 * This is called even if the entry has expired; typically an expired entry
692 * that's been looked up is about to be revalidated, and having it closer to
693 * the head of the LRU means finding it quickly again when the revalidation
697 kauth_identity_lru(struct kauth_identity
*kip
)
699 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
700 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
701 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
706 * Handly lazy expiration of translations.
709 kauth_identity_guid_expired(struct kauth_identity
*kip
)
714 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
715 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
719 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
724 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
725 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
729 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
730 * association exists for the UID.
733 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
735 struct kauth_identity
*kip
;
737 KAUTH_IDENTITY_LOCK();
738 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
739 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
740 kauth_identity_lru(kip
);
745 KAUTH_IDENTITY_UNLOCK();
746 return((kip
== NULL
) ? ENOENT
: 0);
751 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
752 * association exists for the GID.
755 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
757 struct kauth_identity
*kip
;
759 KAUTH_IDENTITY_LOCK();
760 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
761 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
762 kauth_identity_lru(kip
);
767 KAUTH_IDENTITY_UNLOCK();
768 return((kip
== NULL
) ? ENOENT
: 0);
773 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
774 * association exists for the GUID. Note that the association may be expired,
775 * in which case the caller may elect to call out to userland to revalidate.
778 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
780 struct kauth_identity
*kip
;
782 KAUTH_IDENTITY_LOCK();
783 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
784 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
785 kauth_identity_lru(kip
);
790 KAUTH_IDENTITY_UNLOCK();
791 return((kip
== NULL
) ? ENOENT
: 0);
795 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
796 * association exists for the SID. Note that the association may be expired,
797 * in which case the caller may elect to call out to userland to revalidate.
800 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
802 struct kauth_identity
*kip
;
804 KAUTH_IDENTITY_LOCK();
805 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
806 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
807 kauth_identity_lru(kip
);
812 KAUTH_IDENTITY_UNLOCK();
813 return((kip
== NULL
) ? ENOENT
: 0);
819 guid_t kauth_null_guid
;
822 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
824 return(!bcmp(guid1
, guid2
, sizeof(*guid1
)));
828 * Look for well-known GUIDs.
831 kauth_wellknown_guid(guid_t
*guid
)
833 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
836 * All WKGs begin with the same 12 bytes.
838 if (!bcmp((void *)guid
, fingerprint
, 12)) {
840 * The final 4 bytes are our code.
842 code
= *(u_int32_t
*)&guid
->g_guid
[12];
845 return(KAUTH_WKG_EVERYBODY
);
847 return(KAUTH_WKG_NOBODY
);
849 return(KAUTH_WKG_OWNER
);
851 return(KAUTH_WKG_GROUP
);
854 return(KAUTH_WKG_NOT
);
859 * NT Security Identifier handling.
862 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
864 /* check sizes for equality, also sanity-check size while we're at it */
865 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
866 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
867 !bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)))
875 * We support four tokens representing identity:
876 * - Credential reference
879 * - NT security identifier
881 * Of these, the UID is the ubiquitous identifier; cross-referencing should
885 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
888 * Fetch UID from credential.
891 kauth_cred_getuid(kauth_cred_t cred
)
893 NULLCRED_CHECK(cred
);
894 return(cred
->cr_uid
);
898 * Fetch GID from credential.
901 kauth_cred_getgid(kauth_cred_t cred
)
903 NULLCRED_CHECK(cred
);
904 return(cred
->cr_gid
);
908 * Fetch UID from GUID.
911 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
913 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
917 * Fetch GID from GUID.
920 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
922 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
926 * Fetch UID from NT SID.
929 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
931 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
935 * Fetch GID from NT SID.
938 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
940 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
944 * Fetch GUID from NT SID.
947 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
949 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
953 * Fetch GUID from UID.
956 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
958 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
962 * Fetch user GUID from credential.
965 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
967 NULLCRED_CHECK(cred
);
968 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
972 * Fetch GUID from GID.
975 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
977 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
981 * Fetch NT SID from UID.
984 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
986 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
990 * Fetch NT SID from credential.
993 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
995 NULLCRED_CHECK(cred
);
996 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
1000 * Fetch NT SID from GID.
1003 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1005 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1009 * Fetch NT SID from GUID.
1012 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1014 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1020 * Lookup a translation in the cache.
1023 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1025 struct kauth_identity ki
;
1026 struct kauth_identity_extlookup el
;
1028 int (* expired
)(struct kauth_identity
*kip
);
1030 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1033 * Look for an existing cache entry for this association.
1034 * If the entry has not expired, return the cached information.
1039 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1042 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1045 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1047 case KI_VALID_NTSID
:
1048 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1053 /* lookup failure or error */
1055 /* any other error is fatal */
1056 if (error
!= ENOENT
) {
1057 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1061 /* do we have a translation? */
1062 if (ki
.ki_valid
& to
) {
1063 /* found a valid cached entry, check expiry */
1066 expired
= kauth_identity_guid_expired
;
1068 case KI_VALID_NTSID
:
1069 expired
= kauth_identity_ntsid_expired
;
1074 expired
= kauth_identity_guid_expired
;
1076 case KI_VALID_NTSID
:
1077 expired
= kauth_identity_ntsid_expired
;
1083 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1085 * If no expiry function, or not expired, we have found
1089 KAUTH_DEBUG("CACHE - no expiry function");
1092 if (!expired(&ki
)) {
1093 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1097 * We leave ki_valid set here; it contains a translation but the TTL has
1098 * expired. If we can't get a result from the resolver, we will
1099 * use it as a better-than nothing alternative.
1101 KAUTH_DEBUG("CACHE - expired entry found");
1106 * Call the resolver. We ask for as much data as we can get.
1110 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1111 el
.el_uid
= *(uid_t
*)src
;
1114 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1115 el
.el_gid
= *(gid_t
*)src
;
1118 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1119 el
.el_uguid
= *(guid_t
*)src
;
1120 el
.el_gguid
= *(guid_t
*)src
;
1122 case KI_VALID_NTSID
:
1123 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1124 el
.el_usid
= *(ntsid_t
*)src
;
1125 el
.el_gsid
= *(ntsid_t
*)src
;
1131 * Here we ask for everything all at once, to avoid having to work
1132 * out what we really want now, or might want soon.
1134 * Asking for SID translations when we don't know we need them right
1135 * now is going to cause excess work to be done if we're connected
1136 * to a network that thinks it can translate them. This list needs
1137 * to get smaller/smarter.
1139 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1140 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1141 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1142 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1143 error
= kauth_identity_resolve(&el
);
1144 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1145 /* was the lookup successful? */
1148 * Save the results from the lookup - may have other information even if we didn't
1151 kauth_identity_updatecache(&el
, &ki
);
1154 * Check to see if we have a valid result.
1156 if (!error
&& !(ki
.ki_valid
& to
))
1163 *(uid_t
*)dst
= ki
.ki_uid
;
1166 *(gid_t
*)dst
= ki
.ki_gid
;
1169 *(guid_t
*)dst
= ki
.ki_guid
;
1171 case KI_VALID_NTSID
:
1172 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1177 KAUTH_DEBUG("CACHE - returned successfully");
1183 * Group membership cache.
1185 * XXX the linked-list implementation here needs to be optimized.
1188 struct kauth_group_membership
{
1189 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1190 uid_t gm_uid
; /* the identity whose membership we're recording */
1191 gid_t gm_gid
; /* group of which they are a member */
1192 time_t gm_expiry
; /* TTL for the membership */
1194 #define KAUTH_GROUP_ISMEMBER (1<<0)
1197 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1198 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1199 static int kauth_groups_count
;
1201 static lck_mtx_t
*kauth_groups_mtx
;
1202 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1203 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1205 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1206 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1207 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1210 kauth_groups_init(void)
1212 TAILQ_INIT(&kauth_groups
);
1213 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1217 kauth_groups_expired(struct kauth_group_membership
*gm
)
1222 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1226 kauth_groups_lru(struct kauth_group_membership
*gm
)
1228 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1229 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1230 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1235 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1237 struct kauth_group_membership
*gm
;
1240 /* need a valid response if we are to cache anything */
1242 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1243 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1248 /* search for an existing record for this association before inserting */
1249 KAUTH_GROUPS_LOCK();
1250 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1251 if ((el
->el_uid
== gm
->gm_uid
) &&
1252 (el
->el_gid
== gm
->gm_gid
)) {
1253 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1254 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1256 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1258 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1259 kauth_groups_lru(gm
);
1263 KAUTH_GROUPS_UNLOCK();
1265 /* if we found an entry to update, stop here */
1269 /* allocate a new record */
1270 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
1272 gm
->gm_uid
= el
->el_uid
;
1273 gm
->gm_gid
= el
->el_gid
;
1274 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1275 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1277 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1279 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1283 * Insert the new entry. Note that it's possible to race ourselves here
1284 * and end up with duplicate entries in the list. Wasteful, but harmless
1285 * since the first into the list will never be looked up, and thus will
1286 * eventually just fall off the end.
1288 KAUTH_GROUPS_LOCK();
1289 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1290 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
1291 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
1292 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1293 kauth_groups_count
--;
1297 KAUTH_GROUPS_UNLOCK();
1299 /* free expired cache entry */
1305 * Group membership KPI
1308 * This function guarantees not to modify resultp when returning an error.
1311 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
1313 struct kauth_group_membership
*gm
;
1314 struct kauth_identity_extlookup el
;
1318 * Check the per-credential list of override groups.
1320 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1321 * the cache should be used for that case.
1323 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
1324 if (gid
== cred
->cr_groups
[i
]) {
1331 * If we don't have a UID for group membership checks, the in-cred list
1332 * was authoritative and we can stop here.
1334 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
1341 * If the resolver hasn't checked in yet, we are early in the boot phase and
1342 * the local group list is complete and authoritative.
1344 if (!kauth_resolver_registered
) {
1350 /* XXX check supplementary groups */
1351 /* XXX check whiteout groups */
1352 /* XXX nesting of supplementary/whiteout groups? */
1355 * Check the group cache.
1357 KAUTH_GROUPS_LOCK();
1358 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1359 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
1360 kauth_groups_lru(gm
);
1365 /* did we find a membership entry? */
1367 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
1368 KAUTH_GROUPS_UNLOCK();
1370 /* if we did, we can return now */
1374 /* nothing in the cache, need to go to userland */
1375 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
1376 el
.el_uid
= cred
->cr_gmuid
;
1378 error
= kauth_identity_resolve(&el
);
1381 /* save the results from the lookup */
1382 kauth_groups_updatecache(&el
);
1384 /* if we successfully ascertained membership, report */
1385 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
1386 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
1394 * Determine whether the supplied credential is a member of the
1395 * group nominated by GUID.
1398 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
1404 wkg
= kauth_wellknown_guid(guidp
);
1406 case KAUTH_WKG_NOBODY
:
1409 case KAUTH_WKG_EVERYBODY
:
1413 /* translate guid to gid */
1414 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
1416 * If we have no guid -> gid translation, it's not a group and
1417 * thus the cred can't be a member.
1419 if (error
== ENOENT
) {
1424 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
1431 * Fast replacement for issuser()
1434 kauth_cred_issuser(kauth_cred_t cred
)
1436 return(cred
->cr_uid
== 0);
1443 /* lock protecting credential hash table */
1444 static lck_mtx_t
*kauth_cred_hash_mtx
;
1445 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1446 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1449 kauth_cred_init(void)
1453 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1454 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
1456 /*allocate credential hash table */
1457 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
1458 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
1459 M_KAUTH
, M_WAITOK
| M_ZERO
);
1460 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
1461 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
1466 * Return the current thread's effective UID.
1471 return(kauth_cred_get()->cr_uid
);
1475 * Return the current thread's real UID.
1480 return(kauth_cred_get()->cr_ruid
);
1484 * Return the current thread's effective GID.
1489 return(kauth_cred_get()->cr_groups
[0]);
1493 * Return the current thread's real GID.
1498 return(kauth_cred_get()->cr_rgid
);
1502 * Returns a pointer to the current thread's credential, does not take a
1503 * reference (so the caller must not do anything that would let the thread's
1504 * credential change while using the returned value).
1507 kauth_cred_get(void)
1510 struct uthread
*uthread
;
1512 uthread
= get_bsdthread_info(current_thread());
1514 if (uthread
== NULL
)
1515 panic("thread wants credential but has no BSD thread info");
1517 * We can lazy-bind credentials to threads, as long as their processes have them.
1518 * If we later inline this function, the code in this block should probably be
1519 * called out in a function.
1521 if (uthread
->uu_ucred
== NOCRED
) {
1522 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1523 panic("thread wants credential but has no BSD process");
1525 kauth_cred_ref(uthread
->uu_ucred
= p
->p_ucred
);
1528 return(uthread
->uu_ucred
);
1532 * Returns a pointer to the current thread's credential, takes a reference.
1535 kauth_cred_get_with_ref(void)
1538 struct uthread
*uthread
;
1540 uthread
= get_bsdthread_info(current_thread());
1542 if (uthread
== NULL
)
1543 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
1544 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1545 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
1548 * We can lazy-bind credentials to threads, as long as their processes have them.
1549 * If we later inline this function, the code in this block should probably be
1550 * called out in a function.
1553 if (uthread
->uu_ucred
== NOCRED
) {
1554 /* take reference for new cred in thread */
1555 kauth_cred_ref(uthread
->uu_ucred
= proc_ucred(procp
));
1557 /* take a reference for our caller */
1558 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_KAUTH
, 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_KAUTH
);
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
));
1753 * Update the given credential using the egid argument. The given gid is used
1754 * set the effective user ID. We only allocate a new credential when the given
1755 * gid actually results in changes to the existing credential.
1758 kauth_cred_setegid(kauth_cred_t cred
, gid_t egid
)
1760 struct ucred temp_cred
;
1762 NULLCRED_CHECK(cred
);
1764 /* don't need to do anything if the given group ID is already the
1765 * same as the group Id in the credential.
1767 if (cred
->cr_groups
[0] == egid
) {
1768 /* no change needed */
1772 /* look up in cred hash table to see if we have a matching credential
1775 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1776 temp_cred
.cr_groups
[0] = egid
;
1778 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1782 * Update the given credential with the given groups. We only allocate a new
1783 * credential when the given gid actually results in changes to the existing
1785 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1786 * which will be used for group membership checking.
1789 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
1792 struct ucred temp_cred
;
1794 NULLCRED_CHECK(cred
);
1796 /* don't need to do anything if the given list of groups does not change.
1798 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
1799 for (i
= 0; i
< groupcount
; i
++) {
1800 if (cred
->cr_groups
[i
] != groups
[i
])
1803 if (i
== groupcount
) {
1804 /* no change needed */
1809 /* look up in cred hash table to see if we have a matching credential
1812 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1813 temp_cred
.cr_ngroups
= groupcount
;
1814 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
1815 temp_cred
.cr_gmuid
= gmuid
;
1817 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1821 * Update the given credential using the uid and gid arguments. The given uid
1822 * is used set the effective user ID, real user ID, and saved user ID.
1823 * The given gid is used set the effective group ID, real group ID, and saved
1825 * We only allocate a new credential when the given uid and gid actually results
1826 * in changes to the existing credential.
1829 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1831 struct ucred temp_cred
;
1833 NULLCRED_CHECK(cred
);
1835 /* don't need to do anything if the effective, real and saved user IDs are
1836 * already the same as the user ID passed in
1838 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
1839 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1840 /* no change needed */
1844 /* look up in cred hash table to see if we have a matching credential
1847 bzero(&temp_cred
, sizeof(temp_cred
));
1848 temp_cred
.cr_uid
= uid
;
1849 temp_cred
.cr_ruid
= uid
;
1850 temp_cred
.cr_svuid
= uid
;
1851 temp_cred
.cr_gmuid
= uid
;
1852 temp_cred
.cr_ngroups
= 1;
1853 temp_cred
.cr_groups
[0] = gid
;
1854 temp_cred
.cr_rgid
= gid
;
1855 temp_cred
.cr_svgid
= gid
;
1857 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1861 * Update the given credential using the uid and gid arguments. The given uid
1862 * is used to set the saved user ID. The given gid is used to set the
1864 * We only allocate a new credential when the given uid and gid actually results
1865 * in changes to the existing credential.
1868 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1870 struct ucred temp_cred
;
1872 NULLCRED_CHECK(cred
);
1874 /* don't need to do anything if the effective, real and saved user IDs are
1875 * already the same as the user ID passed in
1877 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
1878 /* no change needed */
1882 /* look up in cred hash table to see if we have a matching credential
1885 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1886 temp_cred
.cr_svuid
= uid
;
1887 temp_cred
.cr_svgid
= gid
;
1889 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1893 * Update the given credential using the given auditinfo_t.
1894 * We only allocate a new credential when the given auditinfo_t actually results
1895 * in changes to the existing credential.
1898 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
1900 struct ucred temp_cred
;
1902 NULLCRED_CHECK(cred
);
1904 /* don't need to do anything if the audit info is already the same as the
1905 * audit info in the credential passed in
1907 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
1908 /* no change needed */
1912 /* look up in cred hash table to see if we have a matching credential
1915 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1916 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
1918 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
1922 * Add a reference to the passed credential.
1925 kauth_cred_ref(kauth_cred_t cred
)
1929 NULLCRED_CHECK(cred
);
1931 old_value
= OSAddAtomic(1, &cred
->cr_ref
);
1934 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1940 * Drop a reference from the passed credential, potentially destroying it.
1943 kauth_cred_rele(kauth_cred_t cred
)
1947 NULLCRED_CHECK(cred
);
1949 KAUTH_CRED_HASH_LOCK();
1950 old_value
= OSAddAtomic(-1, &cred
->cr_ref
);
1954 panic("kauth_cred_rele: dropping a reference on a cred with no references");
1957 if (old_value
< 3) {
1958 /* the last reference is our credential hash table */
1959 kauth_cred_remove(cred
);
1961 KAUTH_CRED_HASH_UNLOCK();
1965 * Duplicate a credential.
1966 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1969 kauth_cred_dup(kauth_cred_t cred
)
1971 kauth_cred_t newcred
;
1974 if (cred
== NOCRED
|| cred
== FSCRED
)
1975 panic("kauth_cred_dup: bad credential");
1977 newcred
= kauth_cred_alloc();
1978 if (newcred
!= NULL
) {
1979 bcopy(cred
, newcred
, sizeof(*newcred
));
1980 newcred
->cr_ref
= 1;
1986 * Returns a credential based on the passed credential but which
1987 * reflects the real rather than effective UID and GID.
1988 * NOTE - we do NOT decrement cred reference count on passed in credential
1991 kauth_cred_copy_real(kauth_cred_t cred
)
1993 kauth_cred_t newcred
= NULL
, found_cred
;
1994 struct ucred temp_cred
;
1996 /* if the credential is already 'real', just take a reference */
1997 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
1998 (cred
->cr_rgid
== cred
->cr_gid
)) {
1999 kauth_cred_ref(cred
);
2003 /* look up in cred hash table to see if we have a matching credential
2006 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2007 temp_cred
.cr_uid
= cred
->cr_ruid
;
2008 temp_cred
.cr_groups
[0] = cred
->cr_rgid
;
2009 /* if the cred is not opted out, make sure we are using the r/euid for group checks */
2010 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
2011 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
2016 KAUTH_CRED_HASH_LOCK();
2017 found_cred
= kauth_cred_find(&temp_cred
);
2018 if (found_cred
== cred
) {
2019 /* same cred so just bail */
2020 KAUTH_CRED_HASH_UNLOCK();
2023 if (found_cred
!= NULL
) {
2024 /* found a match so we bump reference count on new one and decrement
2025 * reference count on the old one.
2027 kauth_cred_ref(found_cred
);
2028 KAUTH_CRED_HASH_UNLOCK();
2032 /* must allocate a new credential, copy in old credential data and update
2033 * with real user and group IDs.
2035 newcred
= kauth_cred_dup(&temp_cred
);
2036 err
= kauth_cred_add(newcred
);
2037 KAUTH_CRED_HASH_UNLOCK();
2039 /* retry if kauth_cred_add returns non zero value */
2042 FREE(newcred
, M_KAUTH
);
2050 * common code to update a credential. model_cred is a temporary, non reference
2051 * counted credential used only for comparison and modeling purposes. old_cred
2052 * is a live reference counted credential that we intend to update using model_cred
2055 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
, boolean_t retain_auditinfo
)
2057 kauth_cred_t found_cred
, new_cred
= NULL
;
2059 /* make sure we carry the auditinfo forward to the new credential unless
2060 * we are actually updating the auditinfo.
2062 if (retain_auditinfo
)
2063 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
2068 KAUTH_CRED_HASH_LOCK();
2069 found_cred
= kauth_cred_find(model_cred
);
2070 if (found_cred
== old_cred
) {
2071 /* same cred so just bail */
2072 KAUTH_CRED_HASH_UNLOCK();
2075 if (found_cred
!= NULL
) {
2076 /* found a match so we bump reference count on new one and decrement
2077 * reference count on the old one.
2079 kauth_cred_ref(found_cred
);
2080 KAUTH_CRED_HASH_UNLOCK();
2081 kauth_cred_rele(old_cred
);
2085 /* must allocate a new credential using the model. also
2086 * adds the new credential to the credential hash table.
2088 new_cred
= kauth_cred_dup(model_cred
);
2089 err
= kauth_cred_add(new_cred
);
2090 KAUTH_CRED_HASH_UNLOCK();
2092 /* retry if kauth_cred_add returns non zero value */
2095 FREE(new_cred
, M_KAUTH
);
2099 kauth_cred_rele(old_cred
);
2104 * Add the given credential to our credential hash table and take an additional
2105 * reference to account for our use of the credential in the hash table.
2106 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2108 static int kauth_cred_add(kauth_cred_t new_cred
)
2112 hash_key
= kauth_cred_get_hashkey(new_cred
);
2113 hash_key
%= kauth_cred_table_size
;
2115 /* race fix - there is a window where another matching credential
2116 * could have been inserted between the time this one was created and we
2117 * got the hash lock. If we find a match return an error and have the
2120 if (kauth_cred_find(new_cred
) != NULL
) {
2124 /* take a reference for our use in credential hash table */
2125 kauth_cred_ref(new_cred
);
2127 /* insert the credential into the hash table */
2128 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
2134 * Remove the given credential from our credential hash table.
2135 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2137 static void kauth_cred_remove(kauth_cred_t cred
)
2140 kauth_cred_t found_cred
;
2142 hash_key
= kauth_cred_get_hashkey(cred
);
2143 hash_key
%= kauth_cred_table_size
;
2146 if (cred
->cr_ref
< 1)
2147 panic("cred reference underflow");
2148 if (cred
->cr_ref
> 1)
2149 return; /* someone else got a ref */
2151 /* find cred in the credential hash table */
2152 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2153 if (found_cred
== cred
) {
2154 /* found a match, remove it from the hash table */
2155 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
2156 FREE(cred
, M_KAUTH
);
2157 #if KAUTH_CRED_HASH_DEBUG
2164 /* did not find a match. this should not happen! */
2165 printf("%s - %d - %s - did not find a match \n", __FILE__
, __LINE__
, __FUNCTION__
);
2170 * Using the given credential data, look for a match in our credential hash
2172 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2174 kauth_cred_t
kauth_cred_find(kauth_cred_t cred
)
2177 kauth_cred_t found_cred
;
2179 #if KAUTH_CRED_HASH_DEBUG
2180 static int test_count
= 0;
2183 if ((test_count
% 200) == 0) {
2184 kauth_cred_hash_print();
2188 hash_key
= kauth_cred_get_hashkey(cred
);
2189 hash_key
%= kauth_cred_table_size
;
2191 /* find cred in the credential hash table */
2192 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2193 if (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
, (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
))) == 0) {
2198 /* no match found */
2203 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2205 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
)
2207 u_long hash_key
= 0;
2209 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
2210 (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
)),
2216 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2218 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
2220 u_long hash_key
= start_key
;
2223 while (data_len
> 0) {
2224 hash_key
= (hash_key
<< 4) + *datap
++;
2225 temp
= hash_key
& 0xF0000000;
2227 hash_key
^= temp
>> 24;
2235 #if KAUTH_CRED_HASH_DEBUG
2236 static void kauth_cred_hash_print(void)
2239 kauth_cred_t found_cred
;
2241 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
2242 /* count slot hits, misses, collisions, and max depth */
2243 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2244 printf("[%02d] ", i
);
2246 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
2251 kauth_cred_print(found_cred
);
2255 printf("NOCRED \n");
2261 static void kauth_cred_print(kauth_cred_t cred
)
2265 printf("0x%02X - refs %d uids %d %d %d ", cred
, cred
->cr_ref
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
);
2266 printf("group count %d gids ", cred
->cr_ngroups
);
2267 for (i
= 0; i
< NGROUPS
; i
++) {
2268 printf("%d ", cred
->cr_groups
[i
]);
2270 printf("%d %d %d ", cred
->cr_rgid
, cred
->cr_svgid
, cred
->cr_gmuid
);
2271 printf("auditinfo %d %d %d %d %d %d ",
2272 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
2273 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);