2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
24 * Kernel Authorization framework: Management of process/thread credentials and identity information.
28 #include <sys/param.h> /* XXX trim includes */
30 #include <sys/systm.h>
31 #include <sys/ucred.h>
32 #include <sys/proc_internal.h>
34 #include <sys/timeb.h>
35 #include <sys/times.h>
36 #include <sys/malloc.h>
37 #include <sys/kauth.h>
38 #include <sys/kernel.h>
40 #include <bsm/audit_kernel.h>
42 #include <sys/mount.h>
43 #include <sys/sysproto.h>
44 #include <mach/message.h>
45 #include <mach/host_security.h>
47 #include <libkern/OSAtomic.h>
49 #include <kern/task.h>
50 #include <kern/lock.h>
54 #define MACH_ASSERT 1 /* XXX so bogus */
55 #include <kern/assert.h>
57 #define CRED_DIAGNOSTIC 1
59 # define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
62 * Interface to external identity resolver.
64 * The architecture of the interface is simple; the external resolver calls in to
65 * get work, then calls back with completed work. It also calls us to let us know
66 * that it's (re)started, so that we can resubmit work if it times out.
69 static lck_mtx_t
*kauth_resolver_mtx
;
70 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
71 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
73 static volatile pid_t kauth_resolver_identity
;
74 static int kauth_resolver_registered
;
75 static uint32_t kauth_resolver_sequence
;
77 struct kauth_resolver_work
{
78 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
79 struct kauth_identity_extlookup kr_work
;
83 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
84 #define KAUTH_REQUEST_SUBMITTED (1<<1)
85 #define KAUTH_REQUEST_DONE (1<<2)
89 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
90 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
91 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
93 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
94 static int kauth_resolver_complete(user_addr_t message
);
95 static int kauth_resolver_getwork(user_addr_t message
);
97 #define KAUTH_CRED_PRIMES_COUNT 7
98 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = {97, 241, 397, 743, 1499, 3989, 7499};
99 static int kauth_cred_primes_index
= 0;
100 static int kauth_cred_table_size
= 0;
102 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
103 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
105 #define KAUTH_CRED_HASH_DEBUG 0
107 static int kauth_cred_add(kauth_cred_t new_cred
);
108 static void kauth_cred_remove(kauth_cred_t cred
);
109 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
110 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
111 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
113 #if KAUTH_CRED_HASH_DEBUG
114 static int kauth_cred_count
= 0;
115 static void kauth_cred_hash_print(void);
116 static void kauth_cred_print(kauth_cred_t cred
);
120 kauth_resolver_init(void)
122 TAILQ_INIT(&kauth_resolver_unsubmitted
);
123 TAILQ_INIT(&kauth_resolver_submitted
);
124 TAILQ_INIT(&kauth_resolver_done
);
125 kauth_resolver_sequence
= 31337;
126 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
130 * Allocate a work queue entry, submit the work and wait for completion.
132 * XXX do we want an 'interruptible' flag vs. always being interruptible?
135 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
137 struct kauth_resolver_work
*workp
, *killp
;
139 int error
, shouldfree
;
141 /* no point actually blocking if the resolver isn't up yet */
142 if (kauth_resolver_identity
== 0) {
144 * We've already waited an initial 30 seconds with no result.
145 * Sleep on a stack address so no one wakes us before timeout;
146 * we sleep a half a second in case we are a high priority
147 * process, so that memberd doesn't starve while we are in a
148 * tight loop between user and kernel, eating all the CPU.
150 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
151 if (kauth_resolver_identity
== 0) {
153 * if things haven't changed while we were asleep,
154 * tell the caller we couldn't get an authoritative
161 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
165 workp
->kr_work
= *lkp
;
167 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
168 workp
->kr_result
= 0;
171 * We insert the request onto the unsubmitted queue, the call in from the
172 * resolver will it to the submitted thread when appropriate.
174 KAUTH_RESOLVER_LOCK();
175 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
176 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
178 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
179 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
181 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
183 /* we could compute a better timeout here */
186 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
187 /* request has been completed? */
188 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
190 /* woken because the resolver has died? */
191 if (kauth_resolver_identity
== 0) {
199 /* if the request was processed, copy the result */
201 *lkp
= workp
->kr_work
;
204 * If the request timed out and was never collected, the resolver is dead and
205 * probably not coming back anytime soon. In this case we revert to no-resolver
206 * behaviour, and punt all the other sleeping requests to clear the backlog.
208 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
209 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
210 kauth_resolver_identity
= 0;
211 /* kill all the other requestes that are waiting as well */
212 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
214 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
218 /* drop our reference on the work item, and note whether we should free it or not */
219 if (--workp
->kr_refs
<= 0) {
220 /* work out which list we have to remove it from */
221 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
222 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
223 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
224 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
225 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
226 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
228 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
232 /* someone else still has a reference on this request */
235 /* collect request result */
237 error
= workp
->kr_result
;
238 KAUTH_RESOLVER_UNLOCK();
240 * If we dropped the last reference, free the request.
243 FREE(workp
, M_KAUTH
);
245 KAUTH_DEBUG("RESOLVER - returning %d", error
);
250 * System call interface for the external identity resolver.
253 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
255 int opcode
= uap
->opcode
;
256 user_addr_t message
= uap
->message
;
257 struct kauth_resolver_work
*workp
;
262 * New server registering itself.
264 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
265 new_id
= current_proc()->p_pid
;
266 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
267 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
270 KAUTH_RESOLVER_LOCK();
271 if (kauth_resolver_identity
!= new_id
) {
272 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
274 * We have a new server, so assume that all the old requests have been lost.
276 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
277 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
278 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
279 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
280 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
282 kauth_resolver_identity
= new_id
;
283 kauth_resolver_registered
= 1;
284 wakeup(&kauth_resolver_unsubmitted
);
286 KAUTH_RESOLVER_UNLOCK();
291 * Beyond this point, we must be the resolver process.
293 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
294 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
299 * Got a result returning?
301 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
302 if ((error
= kauth_resolver_complete(message
)) != 0)
307 * Caller wants to take more work?
309 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
310 if ((error
= kauth_resolver_getwork(message
)) != 0)
318 * Get work for a caller.
321 kauth_resolver_getwork(user_addr_t message
)
323 struct kauth_resolver_work
*workp
;
326 KAUTH_RESOLVER_LOCK();
328 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
329 error
= msleep(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0);
334 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
335 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
338 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
339 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
340 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
341 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
345 KAUTH_RESOLVER_UNLOCK();
350 * Return a result from userspace.
353 kauth_resolver_complete(user_addr_t message
)
355 struct kauth_identity_extlookup extl
;
356 struct kauth_resolver_work
*workp
;
359 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
360 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
364 KAUTH_RESOLVER_LOCK();
368 switch (extl
.el_result
) {
369 case KAUTH_EXTLOOKUP_INPROG
:
373 /* XXX this should go away once memberd is updated */
375 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
380 case KAUTH_EXTLOOKUP_SUCCESS
:
383 case KAUTH_EXTLOOKUP_FATAL
:
384 /* fatal error means the resolver is dead */
385 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
386 kauth_resolver_identity
= 0;
387 /* XXX should we terminate all outstanding requests? */
390 case KAUTH_EXTLOOKUP_BADRQ
:
391 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
394 case KAUTH_EXTLOOKUP_FAILURE
:
395 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
399 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
405 * In the case of a fatal error, we assume that the resolver will restart
406 * quickly and re-collect all of the outstanding requests. Thus, we don't
407 * complete the request which returned the fatal error status.
409 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
410 /* scan our list for this request */
411 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
413 if (workp
->kr_seqno
== extl
.el_seqno
) {
415 workp
->kr_work
= extl
;
416 /* move onto completed list and wake up requester(s) */
417 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
418 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
419 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
420 workp
->kr_result
= result
;
421 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
428 * Note that it's OK for us not to find anything; if the request has
429 * timed out the work record will be gone.
431 KAUTH_RESOLVER_UNLOCK();
441 struct kauth_identity
{
442 TAILQ_ENTRY(kauth_identity
) ki_link
;
444 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
445 #define KI_VALID_GID (1<<1)
446 #define KI_VALID_GUID (1<<2)
447 #define KI_VALID_NTSID (1<<3)
453 * Expiry times are the earliest time at which we will disregard the cached state and go to
454 * userland. Before then if the valid bit is set, we will return the cached value. If it's
455 * not set, we will not go to userland to resolve, just assume that there is no answer
458 time_t ki_guid_expiry
;
459 time_t ki_ntsid_expiry
;
462 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
463 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
464 static int kauth_identity_count
;
466 static lck_mtx_t
*kauth_identity_mtx
;
467 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
468 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
471 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
472 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
473 static void kauth_identity_register(struct kauth_identity
*kip
);
474 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
475 static void kauth_identity_lru(struct kauth_identity
*kip
);
476 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
477 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
478 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
479 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
480 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
481 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
484 kauth_identity_init(void)
486 TAILQ_INIT(&kauth_identities
);
487 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
491 kauth_identity_resolve(__unused
struct kauth_identity_extlookup
*el
)
493 return(kauth_resolver_submit(el
));
496 static struct kauth_identity
*
497 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
499 struct kauth_identity
*kip
;
501 /* get and fill in a new identity */
502 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
504 if (gid
!= KAUTH_GID_NONE
) {
506 kip
->ki_valid
= KI_VALID_GID
;
508 if (uid
!= KAUTH_UID_NONE
) {
509 if (kip
->ki_valid
& KI_VALID_GID
)
510 panic("can't allocate kauth identity with both uid and gid");
512 kip
->ki_valid
= KI_VALID_UID
;
515 kip
->ki_guid
= *guidp
;
516 kip
->ki_valid
|= KI_VALID_GUID
;
518 kip
->ki_guid_expiry
= guid_expiry
;
519 if (ntsidp
!= NULL
) {
520 kip
->ki_ntsid
= *ntsidp
;
521 kip
->ki_valid
|= KI_VALID_NTSID
;
523 kip
->ki_ntsid_expiry
= ntsid_expiry
;
529 * Register an association between identity tokens.
532 kauth_identity_register(struct kauth_identity
*kip
)
534 struct kauth_identity
*ip
;
537 * We search the cache for the UID listed in the incoming association. If we
538 * already have an entry, the new information is merged.
541 KAUTH_IDENTITY_LOCK();
542 if (kip
->ki_valid
& KI_VALID_UID
) {
543 if (kip
->ki_valid
& KI_VALID_GID
)
544 panic("kauth_identity: can't insert record with both UID and GID as key");
545 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
546 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
548 } else if (kip
->ki_valid
& KI_VALID_GID
) {
549 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
550 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
553 panic("kauth_identity: can't insert record without UID or GID as key");
557 /* we already have an entry, merge/overwrite */
558 if (kip
->ki_valid
& KI_VALID_GUID
) {
559 ip
->ki_guid
= kip
->ki_guid
;
560 ip
->ki_valid
|= KI_VALID_GUID
;
562 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
563 if (kip
->ki_valid
& KI_VALID_NTSID
) {
564 ip
->ki_ntsid
= kip
->ki_ntsid
;
565 ip
->ki_valid
|= KI_VALID_NTSID
;
567 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
568 /* and discard the incoming identity */
572 /* don't have any information on this identity, so just add it */
573 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
574 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
575 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
576 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
577 kauth_identity_count
--;
580 KAUTH_IDENTITY_UNLOCK();
581 /* have to drop lock before freeing expired entry */
587 * Given a lookup result, add any associations that we don't
591 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
594 struct kauth_identity
*kip
;
599 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
600 KAUTH_IDENTITY_LOCK();
601 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
602 /* matching record */
603 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
604 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
605 kip
->ki_guid
= elp
->el_uguid
;
606 kip
->ki_valid
|= KI_VALID_GUID
;
608 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
609 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
610 kip
->ki_ntsid
= elp
->el_usid
;
611 kip
->ki_valid
|= KI_VALID_NTSID
;
613 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
614 kauth_identity_lru(kip
);
617 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
621 KAUTH_IDENTITY_UNLOCK();
622 /* not found in cache, add new record */
624 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
625 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
626 tv
.tv_sec
+ elp
->el_uguid_valid
,
627 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
628 tv
.tv_sec
+ elp
->el_usid_valid
);
632 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
633 kauth_identity_register(kip
);
638 /* group identity? */
639 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
640 KAUTH_IDENTITY_LOCK();
641 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
642 /* matching record */
643 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
644 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
645 kip
->ki_guid
= elp
->el_gguid
;
646 kip
->ki_valid
|= KI_VALID_GUID
;
648 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
649 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
650 kip
->ki_ntsid
= elp
->el_gsid
;
651 kip
->ki_valid
|= KI_VALID_NTSID
;
653 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
654 kauth_identity_lru(kip
);
657 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
661 KAUTH_IDENTITY_UNLOCK();
662 /* not found in cache, add new record */
664 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
665 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
666 tv
.tv_sec
+ elp
->el_gguid_valid
,
667 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
668 tv
.tv_sec
+ elp
->el_gsid_valid
);
672 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
673 kauth_identity_register(kip
);
681 * Promote the entry to the head of the LRU, assumes the cache is locked.
683 * This is called even if the entry has expired; typically an expired entry
684 * that's been looked up is about to be revalidated, and having it closer to
685 * the head of the LRU means finding it quickly again when the revalidation
689 kauth_identity_lru(struct kauth_identity
*kip
)
691 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
692 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
693 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
698 * Handly lazy expiration of translations.
701 kauth_identity_guid_expired(struct kauth_identity
*kip
)
706 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
707 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
711 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
716 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
717 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
721 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
722 * association exists for the UID.
725 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
727 struct kauth_identity
*kip
;
729 KAUTH_IDENTITY_LOCK();
730 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
731 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
732 kauth_identity_lru(kip
);
737 KAUTH_IDENTITY_UNLOCK();
738 return((kip
== NULL
) ? ENOENT
: 0);
743 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
744 * association exists for the GID.
747 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
749 struct kauth_identity
*kip
;
751 KAUTH_IDENTITY_LOCK();
752 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
753 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
754 kauth_identity_lru(kip
);
759 KAUTH_IDENTITY_UNLOCK();
760 return((kip
== NULL
) ? ENOENT
: 0);
765 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
766 * association exists for the GUID. Note that the association may be expired,
767 * in which case the caller may elect to call out to userland to revalidate.
770 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
772 struct kauth_identity
*kip
;
774 KAUTH_IDENTITY_LOCK();
775 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
776 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
777 kauth_identity_lru(kip
);
782 KAUTH_IDENTITY_UNLOCK();
783 return((kip
== NULL
) ? ENOENT
: 0);
787 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
788 * association exists for the SID. Note that the association may be expired,
789 * in which case the caller may elect to call out to userland to revalidate.
792 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
794 struct kauth_identity
*kip
;
796 KAUTH_IDENTITY_LOCK();
797 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
798 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
799 kauth_identity_lru(kip
);
804 KAUTH_IDENTITY_UNLOCK();
805 return((kip
== NULL
) ? ENOENT
: 0);
811 guid_t kauth_null_guid
;
814 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
816 return(!bcmp(guid1
, guid2
, sizeof(*guid1
)));
820 * Look for well-known GUIDs.
823 kauth_wellknown_guid(guid_t
*guid
)
825 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
828 * All WKGs begin with the same 12 bytes.
830 if (!bcmp((void *)guid
, fingerprint
, 12)) {
832 * The final 4 bytes are our code.
834 code
= *(u_int32_t
*)&guid
->g_guid
[12];
837 return(KAUTH_WKG_EVERYBODY
);
839 return(KAUTH_WKG_NOBODY
);
841 return(KAUTH_WKG_OWNER
);
843 return(KAUTH_WKG_GROUP
);
846 return(KAUTH_WKG_NOT
);
851 * NT Security Identifier handling.
854 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
856 /* check sizes for equality, also sanity-check size while we're at it */
857 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
858 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
859 !bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)))
867 * We support four tokens representing identity:
868 * - Credential reference
871 * - NT security identifier
873 * Of these, the UID is the ubiquitous identifier; cross-referencing should
877 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
880 * Fetch UID from credential.
883 kauth_cred_getuid(kauth_cred_t cred
)
885 NULLCRED_CHECK(cred
);
886 return(cred
->cr_uid
);
890 * Fetch GID from credential.
893 kauth_cred_getgid(kauth_cred_t cred
)
895 NULLCRED_CHECK(cred
);
896 return(cred
->cr_gid
);
900 * Fetch UID from GUID.
903 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
905 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
909 * Fetch GID from GUID.
912 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
914 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
918 * Fetch UID from NT SID.
921 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
923 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
927 * Fetch GID from NT SID.
930 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
932 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
936 * Fetch GUID from NT SID.
939 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
941 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
945 * Fetch GUID from UID.
948 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
950 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
954 * Fetch user GUID from credential.
957 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
959 NULLCRED_CHECK(cred
);
960 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
964 * Fetch GUID from GID.
967 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
969 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
973 * Fetch NT SID from UID.
976 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
978 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
982 * Fetch NT SID from credential.
985 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
987 NULLCRED_CHECK(cred
);
988 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
992 * Fetch NT SID from GID.
995 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
997 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1001 * Fetch NT SID from GUID.
1004 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1006 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1012 * Lookup a translation in the cache.
1015 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1017 struct kauth_identity ki
;
1018 struct kauth_identity_extlookup el
;
1020 int (* expired
)(struct kauth_identity
*kip
);
1022 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1025 * Look for an existing cache entry for this association.
1026 * If the entry has not expired, return the cached information.
1031 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1034 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1037 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1039 case KI_VALID_NTSID
:
1040 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1045 /* lookup failure or error */
1047 /* any other error is fatal */
1048 if (error
!= ENOENT
) {
1049 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1053 /* do we have a translation? */
1054 if (ki
.ki_valid
& to
) {
1055 /* found a valid cached entry, check expiry */
1058 expired
= kauth_identity_guid_expired
;
1060 case KI_VALID_NTSID
:
1061 expired
= kauth_identity_ntsid_expired
;
1066 expired
= kauth_identity_guid_expired
;
1068 case KI_VALID_NTSID
:
1069 expired
= kauth_identity_ntsid_expired
;
1075 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1077 * If no expiry function, or not expired, we have found
1081 KAUTH_DEBUG("CACHE - no expiry function");
1084 if (!expired(&ki
)) {
1085 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1089 * We leave ki_valid set here; it contains a translation but the TTL has
1090 * expired. If we can't get a result from the resolver, we will
1091 * use it as a better-than nothing alternative.
1093 KAUTH_DEBUG("CACHE - expired entry found");
1098 * Call the resolver. We ask for as much data as we can get.
1102 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1103 el
.el_uid
= *(uid_t
*)src
;
1106 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1107 el
.el_gid
= *(gid_t
*)src
;
1110 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1111 el
.el_uguid
= *(guid_t
*)src
;
1112 el
.el_gguid
= *(guid_t
*)src
;
1114 case KI_VALID_NTSID
:
1115 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1116 el
.el_usid
= *(ntsid_t
*)src
;
1117 el
.el_gsid
= *(ntsid_t
*)src
;
1123 * Here we ask for everything all at once, to avoid having to work
1124 * out what we really want now, or might want soon.
1126 * Asking for SID translations when we don't know we need them right
1127 * now is going to cause excess work to be done if we're connected
1128 * to a network that thinks it can translate them. This list needs
1129 * to get smaller/smarter.
1131 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1132 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1133 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1134 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1135 error
= kauth_identity_resolve(&el
);
1136 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1137 /* was the lookup successful? */
1140 * Save the results from the lookup - may have other information even if we didn't
1143 kauth_identity_updatecache(&el
, &ki
);
1146 * Check to see if we have a valid result.
1148 if (!error
&& !(ki
.ki_valid
& to
))
1155 *(uid_t
*)dst
= ki
.ki_uid
;
1158 *(gid_t
*)dst
= ki
.ki_gid
;
1161 *(guid_t
*)dst
= ki
.ki_guid
;
1163 case KI_VALID_NTSID
:
1164 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1169 KAUTH_DEBUG("CACHE - returned successfully");
1175 * Group membership cache.
1177 * XXX the linked-list implementation here needs to be optimized.
1180 struct kauth_group_membership
{
1181 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1182 uid_t gm_uid
; /* the identity whose membership we're recording */
1183 gid_t gm_gid
; /* group of which they are a member */
1184 time_t gm_expiry
; /* TTL for the membership */
1186 #define KAUTH_GROUP_ISMEMBER (1<<0)
1189 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1190 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1191 static int kauth_groups_count
;
1193 static lck_mtx_t
*kauth_groups_mtx
;
1194 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1195 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1197 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1198 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1199 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1202 kauth_groups_init(void)
1204 TAILQ_INIT(&kauth_groups
);
1205 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1209 kauth_groups_expired(struct kauth_group_membership
*gm
)
1214 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1218 kauth_groups_lru(struct kauth_group_membership
*gm
)
1220 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1221 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1222 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1227 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1229 struct kauth_group_membership
*gm
;
1232 /* need a valid response if we are to cache anything */
1234 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1235 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1240 /* search for an existing record for this association before inserting */
1241 KAUTH_GROUPS_LOCK();
1242 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1243 if ((el
->el_uid
== gm
->gm_uid
) &&
1244 (el
->el_gid
== gm
->gm_gid
)) {
1245 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1246 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1248 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1250 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1251 kauth_groups_lru(gm
);
1255 KAUTH_GROUPS_UNLOCK();
1257 /* if we found an entry to update, stop here */
1261 /* allocate a new record */
1262 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
1264 gm
->gm_uid
= el
->el_uid
;
1265 gm
->gm_gid
= el
->el_gid
;
1266 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1267 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1269 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1271 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1275 * Insert the new entry. Note that it's possible to race ourselves here
1276 * and end up with duplicate entries in the list. Wasteful, but harmless
1277 * since the first into the list will never be looked up, and thus will
1278 * eventually just fall off the end.
1280 KAUTH_GROUPS_LOCK();
1281 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1282 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
1283 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
1284 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1285 kauth_groups_count
--;
1289 KAUTH_GROUPS_UNLOCK();
1291 /* free expired cache entry */
1297 * Group membership KPI
1300 * This function guarantees not to modify resultp when returning an error.
1303 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
1305 struct kauth_group_membership
*gm
;
1306 struct kauth_identity_extlookup el
;
1310 * Check the per-credential list of override groups.
1312 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1313 * the cache should be used for that case.
1315 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
1316 if (gid
== cred
->cr_groups
[i
]) {
1323 * If we don't have a UID for group membership checks, the in-cred list
1324 * was authoritative and we can stop here.
1326 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
1333 * If the resolver hasn't checked in yet, we are early in the boot phase and
1334 * the local group list is complete and authoritative.
1336 if (!kauth_resolver_registered
) {
1342 /* XXX check supplementary groups */
1343 /* XXX check whiteout groups */
1344 /* XXX nesting of supplementary/whiteout groups? */
1347 * Check the group cache.
1349 KAUTH_GROUPS_LOCK();
1350 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1351 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
1352 kauth_groups_lru(gm
);
1357 /* did we find a membership entry? */
1359 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
1360 KAUTH_GROUPS_UNLOCK();
1362 /* if we did, we can return now */
1366 /* nothing in the cache, need to go to userland */
1367 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
1368 el
.el_uid
= cred
->cr_gmuid
;
1370 error
= kauth_identity_resolve(&el
);
1373 /* save the results from the lookup */
1374 kauth_groups_updatecache(&el
);
1376 /* if we successfully ascertained membership, report */
1377 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
1378 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
1386 * Determine whether the supplied credential is a member of the
1387 * group nominated by GUID.
1390 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
1396 wkg
= kauth_wellknown_guid(guidp
);
1398 case KAUTH_WKG_NOBODY
:
1401 case KAUTH_WKG_EVERYBODY
:
1405 /* translate guid to gid */
1406 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
1408 * If we have no guid -> gid translation, it's not a group and
1409 * thus the cred can't be a member.
1411 if (error
== ENOENT
) {
1416 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
1423 * Fast replacement for issuser()
1426 kauth_cred_issuser(kauth_cred_t cred
)
1428 return(cred
->cr_uid
== 0);
1435 /* lock protecting credential hash table */
1436 static lck_mtx_t
*kauth_cred_hash_mtx
;
1437 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1438 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1441 kauth_cred_init(void)
1445 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1446 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
1448 /*allocate credential hash table */
1449 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
1450 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
1451 M_KAUTH
, M_WAITOK
| M_ZERO
);
1452 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
1453 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
1458 * Return the current thread's effective UID.
1463 return(kauth_cred_get()->cr_uid
);
1467 * Return the current thread's real UID.
1472 return(kauth_cred_get()->cr_ruid
);
1476 * Return the current thread's effective GID.
1481 return(kauth_cred_get()->cr_groups
[0]);
1485 * Return the current thread's real GID.
1490 return(kauth_cred_get()->cr_rgid
);
1494 * Returns a pointer to the current thread's credential, does not take a
1495 * reference (so the caller must not do anything that would let the thread's
1496 * credential change while using the returned value).
1499 kauth_cred_get(void)
1502 struct uthread
*uthread
;
1504 uthread
= get_bsdthread_info(current_thread());
1506 if (uthread
== NULL
)
1507 panic("thread wants credential but has no BSD thread info");
1509 * We can lazy-bind credentials to threads, as long as their processes have them.
1510 * If we later inline this function, the code in this block should probably be
1511 * called out in a function.
1513 if (uthread
->uu_ucred
== NOCRED
) {
1514 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1515 panic("thread wants credential but has no BSD process");
1517 kauth_cred_ref(uthread
->uu_ucred
= p
->p_ucred
);
1520 return(uthread
->uu_ucred
);
1524 * Returns a pointer to the current thread's credential, takes a reference.
1527 kauth_cred_get_with_ref(void)
1530 struct uthread
*uthread
;
1532 uthread
= get_bsdthread_info(current_thread());
1534 if (uthread
== NULL
)
1535 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
1536 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1537 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
1540 * We can lazy-bind credentials to threads, as long as their processes have them.
1541 * If we later inline this function, the code in this block should probably be
1542 * called out in a function.
1545 if (uthread
->uu_ucred
== NOCRED
) {
1546 /* take reference for new cred in thread */
1547 kauth_cred_ref(uthread
->uu_ucred
= proc_ucred(procp
));
1549 /* take a reference for our caller */
1550 kauth_cred_ref(uthread
->uu_ucred
);
1552 return(uthread
->uu_ucred
);
1556 * Returns a pointer to the given process's credential, takes a reference.
1559 kauth_cred_proc_ref(proc_t procp
)
1564 cred
= proc_ucred(procp
);
1565 kauth_cred_ref(cred
);
1571 * Allocates a new credential.
1574 kauth_cred_alloc(void)
1576 kauth_cred_t newcred
;
1578 MALLOC(newcred
, kauth_cred_t
, sizeof(*newcred
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1580 newcred
->cr_ref
= 1;
1581 /* must do this, or cred has same group membership as uid 0 */
1582 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
1585 panic("kauth_cred_alloc: couldn't allocate credential");
1589 #if KAUTH_CRED_HASH_DEBUG
1597 * Looks to see if we already have a known credential and if found bumps the
1598 * reference count and returns it. If there are no credentials that match
1599 * the given credential then we allocate a new credential.
1601 * Note that the gmuid is hard-defaulted to the UID specified. Since we maintain
1602 * this field, we can't expect callers to know how it needs to be set. Callers
1603 * should be prepared for this field to be overwritten.
1606 kauth_cred_create(kauth_cred_t cred
)
1608 kauth_cred_t found_cred
, new_cred
= NULL
;
1610 cred
->cr_gmuid
= cred
->cr_uid
;
1613 KAUTH_CRED_HASH_LOCK();
1614 found_cred
= kauth_cred_find(cred
);
1615 if (found_cred
!= NULL
) {
1616 /* found an existing credential so we'll bump reference count and return */
1617 kauth_cred_ref(found_cred
);
1618 KAUTH_CRED_HASH_UNLOCK();
1621 KAUTH_CRED_HASH_UNLOCK();
1623 /* no existing credential found. create one and add it to our hash table */
1624 new_cred
= kauth_cred_alloc();
1625 if (new_cred
!= NULL
) {
1627 new_cred
->cr_uid
= cred
->cr_uid
;
1628 new_cred
->cr_ruid
= cred
->cr_ruid
;
1629 new_cred
->cr_svuid
= cred
->cr_svuid
;
1630 new_cred
->cr_rgid
= cred
->cr_rgid
;
1631 new_cred
->cr_svgid
= cred
->cr_svgid
;
1632 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
1633 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
1634 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
1635 KAUTH_CRED_HASH_LOCK();
1636 err
= kauth_cred_add(new_cred
);
1637 KAUTH_CRED_HASH_UNLOCK();
1639 /* retry if kauth_cred_add returns non zero value */
1642 FREE(new_cred
, M_KAUTH
);
1651 * Update the given credential using the uid argument. The given uid is used
1652 * set the effective user ID, real user ID, and saved user ID. We only
1653 * allocate a new credential when the given uid actually results in changes to
1654 * the existing credential.
1657 kauth_cred_setuid(kauth_cred_t cred
, uid_t uid
)
1659 struct ucred temp_cred
;
1661 NULLCRED_CHECK(cred
);
1663 /* don't need to do anything if the effective, real and saved user IDs are
1664 * already the same as the user ID passed in
1666 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
) {
1667 /* no change needed */
1671 /* look up in cred hash table to see if we have a matching credential
1674 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1675 temp_cred
.cr_uid
= uid
;
1676 temp_cred
.cr_ruid
= uid
;
1677 temp_cred
.cr_svuid
= uid
;
1678 temp_cred
.cr_gmuid
= uid
;
1680 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1684 * Update the given credential using the euid argument. The given uid is used
1685 * set the effective user ID. We only allocate a new credential when the given
1686 * uid actually results in changes to the existing credential.
1689 kauth_cred_seteuid(kauth_cred_t cred
, uid_t euid
)
1691 struct ucred temp_cred
;
1693 NULLCRED_CHECK(cred
);
1695 /* don't need to do anything if the given effective user ID is already the
1696 * same as the effective user ID in the credential.
1698 if (cred
->cr_uid
== euid
) {
1699 /* no change needed */
1703 /* look up in cred hash table to see if we have a matching credential
1706 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1707 temp_cred
.cr_uid
= euid
;
1709 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1713 * Update the given credential using the gid argument. The given gid is used
1714 * set the effective group ID, real group ID, and saved group ID. We only
1715 * allocate a new credential when the given gid actually results in changes to
1716 * the existing credential.
1719 kauth_cred_setgid(kauth_cred_t cred
, gid_t gid
)
1721 struct ucred temp_cred
;
1723 NULLCRED_CHECK(cred
);
1725 /* don't need to do anything if the given group ID is already the
1726 * same as the group ID in the credential.
1728 if (cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1729 /* no change needed */
1733 /* look up in cred hash table to see if we have a matching credential
1736 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1737 temp_cred
.cr_groups
[0] = gid
;
1738 temp_cred
.cr_rgid
= gid
;
1739 temp_cred
.cr_svgid
= gid
;
1741 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1745 * Update the given credential using the egid argument. The given gid is used
1746 * set the effective user ID. We only allocate a new credential when the given
1747 * gid actually results in changes to the existing credential.
1750 kauth_cred_setegid(kauth_cred_t cred
, gid_t egid
)
1752 struct ucred temp_cred
;
1754 NULLCRED_CHECK(cred
);
1756 /* don't need to do anything if the given group ID is already the
1757 * same as the group Id in the credential.
1759 if (cred
->cr_groups
[0] == egid
) {
1760 /* no change needed */
1764 /* look up in cred hash table to see if we have a matching credential
1767 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1768 temp_cred
.cr_groups
[0] = egid
;
1770 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1774 * Update the given credential with the given groups. We only allocate a new
1775 * credential when the given gid actually results in changes to the existing
1777 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1778 * which will be used for group membership checking.
1781 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
1784 struct ucred temp_cred
;
1786 NULLCRED_CHECK(cred
);
1788 /* don't need to do anything if the given list of groups does not change.
1790 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
1791 for (i
= 0; i
< groupcount
; i
++) {
1792 if (cred
->cr_groups
[i
] != groups
[i
])
1795 if (i
== groupcount
) {
1796 /* no change needed */
1801 /* look up in cred hash table to see if we have a matching credential
1804 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1805 temp_cred
.cr_ngroups
= groupcount
;
1806 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
1807 temp_cred
.cr_gmuid
= gmuid
;
1809 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1813 * Update the given credential using the uid and gid arguments. The given uid
1814 * is used set the effective user ID, real user ID, and saved user ID.
1815 * The given gid is used set the effective group ID, real group ID, and saved
1817 * We only allocate a new credential when the given uid and gid actually results
1818 * in changes to the existing credential.
1821 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1823 struct ucred temp_cred
;
1825 NULLCRED_CHECK(cred
);
1827 /* don't need to do anything if the effective, real and saved user IDs are
1828 * already the same as the user ID passed in
1830 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
1831 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1832 /* no change needed */
1836 /* look up in cred hash table to see if we have a matching credential
1839 bzero(&temp_cred
, sizeof(temp_cred
));
1840 temp_cred
.cr_uid
= uid
;
1841 temp_cred
.cr_ruid
= uid
;
1842 temp_cred
.cr_svuid
= uid
;
1843 temp_cred
.cr_gmuid
= uid
;
1844 temp_cred
.cr_ngroups
= 1;
1845 temp_cred
.cr_groups
[0] = gid
;
1846 temp_cred
.cr_rgid
= gid
;
1847 temp_cred
.cr_svgid
= gid
;
1849 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1853 * Update the given credential using the uid and gid arguments. The given uid
1854 * is used to set the saved user ID. The given gid is used to set the
1856 * We only allocate a new credential when the given uid and gid actually results
1857 * in changes to the existing credential.
1860 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1862 struct ucred temp_cred
;
1864 NULLCRED_CHECK(cred
);
1866 /* don't need to do anything if the effective, real and saved user IDs are
1867 * already the same as the user ID passed in
1869 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
1870 /* no change needed */
1874 /* look up in cred hash table to see if we have a matching credential
1877 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1878 temp_cred
.cr_svuid
= uid
;
1879 temp_cred
.cr_svgid
= gid
;
1881 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1885 * Update the given credential using the given auditinfo_t.
1886 * We only allocate a new credential when the given auditinfo_t actually results
1887 * in changes to the existing credential.
1890 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
1892 struct ucred temp_cred
;
1894 NULLCRED_CHECK(cred
);
1896 /* don't need to do anything if the audit info is already the same as the
1897 * audit info in the credential passed in
1899 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
1900 /* no change needed */
1904 /* look up in cred hash table to see if we have a matching credential
1907 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1908 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
1910 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
1914 * Add a reference to the passed credential.
1917 kauth_cred_ref(kauth_cred_t cred
)
1921 NULLCRED_CHECK(cred
);
1923 old_value
= OSAddAtomic(1, &cred
->cr_ref
);
1926 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1932 * Drop a reference from the passed credential, potentially destroying it.
1935 kauth_cred_rele(kauth_cred_t cred
)
1939 NULLCRED_CHECK(cred
);
1941 KAUTH_CRED_HASH_LOCK();
1942 old_value
= OSAddAtomic(-1, &cred
->cr_ref
);
1946 panic("kauth_cred_rele: dropping a reference on a cred with no references");
1949 if (old_value
< 3) {
1950 /* the last reference is our credential hash table */
1951 kauth_cred_remove(cred
);
1953 KAUTH_CRED_HASH_UNLOCK();
1957 * Duplicate a credential.
1958 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1961 kauth_cred_dup(kauth_cred_t cred
)
1963 kauth_cred_t newcred
;
1966 if (cred
== NOCRED
|| cred
== FSCRED
)
1967 panic("kauth_cred_dup: bad credential");
1969 newcred
= kauth_cred_alloc();
1970 if (newcred
!= NULL
) {
1971 bcopy(cred
, newcred
, sizeof(*newcred
));
1972 newcred
->cr_ref
= 1;
1978 * Returns a credential based on the passed credential but which
1979 * reflects the real rather than effective UID and GID.
1980 * NOTE - we do NOT decrement cred reference count on passed in credential
1983 kauth_cred_copy_real(kauth_cred_t cred
)
1985 kauth_cred_t newcred
= NULL
, found_cred
;
1986 struct ucred temp_cred
;
1988 /* if the credential is already 'real', just take a reference */
1989 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
1990 (cred
->cr_rgid
== cred
->cr_gid
)) {
1991 kauth_cred_ref(cred
);
1995 /* look up in cred hash table to see if we have a matching credential
1998 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1999 temp_cred
.cr_uid
= cred
->cr_ruid
;
2000 temp_cred
.cr_groups
[0] = cred
->cr_rgid
;
2001 /* if the cred is not opted out, make sure we are using the r/euid for group checks */
2002 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
2003 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
2008 KAUTH_CRED_HASH_LOCK();
2009 found_cred
= kauth_cred_find(&temp_cred
);
2010 if (found_cred
== cred
) {
2011 /* same cred so just bail */
2012 KAUTH_CRED_HASH_UNLOCK();
2015 if (found_cred
!= NULL
) {
2016 /* found a match so we bump reference count on new one and decrement
2017 * reference count on the old one.
2019 kauth_cred_ref(found_cred
);
2020 KAUTH_CRED_HASH_UNLOCK();
2024 /* must allocate a new credential, copy in old credential data and update
2025 * with real user and group IDs.
2027 newcred
= kauth_cred_dup(&temp_cred
);
2028 err
= kauth_cred_add(newcred
);
2029 KAUTH_CRED_HASH_UNLOCK();
2031 /* retry if kauth_cred_add returns non zero value */
2034 FREE(newcred
, M_KAUTH
);
2042 * common code to update a credential. model_cred is a temporary, non reference
2043 * counted credential used only for comparison and modeling purposes. old_cred
2044 * is a live reference counted credential that we intend to update using model_cred
2047 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
, boolean_t retain_auditinfo
)
2049 kauth_cred_t found_cred
, new_cred
= NULL
;
2051 /* make sure we carry the auditinfo forward to the new credential unless
2052 * we are actually updating the auditinfo.
2054 if (retain_auditinfo
)
2055 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
2060 KAUTH_CRED_HASH_LOCK();
2061 found_cred
= kauth_cred_find(model_cred
);
2062 if (found_cred
== old_cred
) {
2063 /* same cred so just bail */
2064 KAUTH_CRED_HASH_UNLOCK();
2067 if (found_cred
!= NULL
) {
2068 /* found a match so we bump reference count on new one and decrement
2069 * reference count on the old one.
2071 kauth_cred_ref(found_cred
);
2072 KAUTH_CRED_HASH_UNLOCK();
2073 kauth_cred_rele(old_cred
);
2077 /* must allocate a new credential using the model. also
2078 * adds the new credential to the credential hash table.
2080 new_cred
= kauth_cred_dup(model_cred
);
2081 err
= kauth_cred_add(new_cred
);
2082 KAUTH_CRED_HASH_UNLOCK();
2084 /* retry if kauth_cred_add returns non zero value */
2087 FREE(new_cred
, M_KAUTH
);
2091 kauth_cred_rele(old_cred
);
2096 * Add the given credential to our credential hash table and take an additional
2097 * reference to account for our use of the credential in the hash table.
2098 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2100 static int kauth_cred_add(kauth_cred_t new_cred
)
2104 hash_key
= kauth_cred_get_hashkey(new_cred
);
2105 hash_key
%= kauth_cred_table_size
;
2107 /* race fix - there is a window where another matching credential
2108 * could have been inserted between the time this one was created and we
2109 * got the hash lock. If we find a match return an error and have the
2112 if (kauth_cred_find(new_cred
) != NULL
) {
2116 /* take a reference for our use in credential hash table */
2117 kauth_cred_ref(new_cred
);
2119 /* insert the credential into the hash table */
2120 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
2126 * Remove the given credential from our credential hash table.
2127 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2129 static void kauth_cred_remove(kauth_cred_t cred
)
2132 kauth_cred_t found_cred
;
2134 hash_key
= kauth_cred_get_hashkey(cred
);
2135 hash_key
%= kauth_cred_table_size
;
2138 if (cred
->cr_ref
< 1)
2139 panic("cred reference underflow");
2140 if (cred
->cr_ref
> 1)
2141 return; /* someone else got a ref */
2143 /* find cred in the credential hash table */
2144 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2145 if (found_cred
== cred
) {
2146 /* found a match, remove it from the hash table */
2147 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
2148 FREE(cred
, M_KAUTH
);
2149 #if KAUTH_CRED_HASH_DEBUG
2156 /* did not find a match. this should not happen! */
2157 printf("%s - %d - %s - did not find a match \n", __FILE__
, __LINE__
, __FUNCTION__
);
2162 * Using the given credential data, look for a match in our credential hash
2164 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2166 kauth_cred_t
kauth_cred_find(kauth_cred_t cred
)
2169 kauth_cred_t found_cred
;
2171 #if KAUTH_CRED_HASH_DEBUG
2172 static int test_count
= 0;
2175 if ((test_count
% 200) == 0) {
2176 kauth_cred_hash_print();
2180 hash_key
= kauth_cred_get_hashkey(cred
);
2181 hash_key
%= kauth_cred_table_size
;
2183 /* find cred in the credential hash table */
2184 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2185 if (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
, (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
))) == 0) {
2190 /* no match found */
2195 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2197 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
)
2199 u_long hash_key
= 0;
2201 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
2202 (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
)),
2208 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2210 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
2212 u_long hash_key
= start_key
;
2215 while (data_len
> 0) {
2216 hash_key
= (hash_key
<< 4) + *datap
++;
2217 temp
= hash_key
& 0xF0000000;
2219 hash_key
^= temp
>> 24;
2227 #if KAUTH_CRED_HASH_DEBUG
2228 static void kauth_cred_hash_print(void)
2231 kauth_cred_t found_cred
;
2233 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
2234 /* count slot hits, misses, collisions, and max depth */
2235 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2236 printf("[%02d] ", i
);
2238 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
2243 kauth_cred_print(found_cred
);
2247 printf("NOCRED \n");
2253 static void kauth_cred_print(kauth_cred_t cred
)
2257 printf("0x%02X - refs %d uids %d %d %d ", cred
, cred
->cr_ref
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
);
2258 printf("group count %d gids ", cred
->cr_ngroups
);
2259 for (i
= 0; i
< NGROUPS
; i
++) {
2260 printf("%d ", cred
->cr_groups
[i
]);
2262 printf("%d %d %d ", cred
->cr_rgid
, cred
->cr_svgid
, cred
->cr_gmuid
);
2263 printf("auditinfo %d %d %d %d %d %d ",
2264 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
2265 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);