2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 * Kernel Authorization framework: Management of process/thread credentials and identity information.
34 #include <sys/param.h> /* XXX trim includes */
36 #include <sys/systm.h>
37 #include <sys/ucred.h>
38 #include <sys/proc_internal.h>
40 #include <sys/timeb.h>
41 #include <sys/times.h>
42 #include <sys/malloc.h>
43 #include <sys/kauth.h>
44 #include <sys/kernel.h>
46 #include <bsm/audit_kernel.h>
48 #include <sys/mount.h>
49 #include <sys/sysproto.h>
50 #include <mach/message.h>
51 #include <mach/host_security.h>
53 #include <libkern/OSAtomic.h>
55 #include <kern/task.h>
56 #include <kern/lock.h>
60 #define MACH_ASSERT 1 /* XXX so bogus */
61 #include <kern/assert.h>
63 #define CRED_DIAGNOSTIC 1
65 # define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
68 * Interface to external identity resolver.
70 * The architecture of the interface is simple; the external resolver calls in to
71 * get work, then calls back with completed work. It also calls us to let us know
72 * that it's (re)started, so that we can resubmit work if it times out.
75 static lck_mtx_t
*kauth_resolver_mtx
;
76 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
77 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
79 static volatile pid_t kauth_resolver_identity
;
80 static int kauth_resolver_registered
;
81 static uint32_t kauth_resolver_sequence
;
83 struct kauth_resolver_work
{
84 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
85 struct kauth_identity_extlookup kr_work
;
89 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
90 #define KAUTH_REQUEST_SUBMITTED (1<<1)
91 #define KAUTH_REQUEST_DONE (1<<2)
95 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
96 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
97 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
99 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
100 static int kauth_resolver_complete(user_addr_t message
);
101 static int kauth_resolver_getwork(user_addr_t message
);
103 #define KAUTH_CRED_PRIMES_COUNT 7
104 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = {97, 241, 397, 743, 1499, 3989, 7499};
105 static int kauth_cred_primes_index
= 0;
106 static int kauth_cred_table_size
= 0;
108 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
109 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
111 #define KAUTH_CRED_HASH_DEBUG 0
113 static int kauth_cred_add(kauth_cred_t new_cred
);
114 static void kauth_cred_remove(kauth_cred_t cred
);
115 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
116 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
117 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
119 #if KAUTH_CRED_HASH_DEBUG
120 static int kauth_cred_count
= 0;
121 static void kauth_cred_hash_print(void);
122 static void kauth_cred_print(kauth_cred_t cred
);
126 kauth_resolver_init(void)
128 TAILQ_INIT(&kauth_resolver_unsubmitted
);
129 TAILQ_INIT(&kauth_resolver_submitted
);
130 TAILQ_INIT(&kauth_resolver_done
);
131 kauth_resolver_sequence
= 31337;
132 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
136 * Allocate a work queue entry, submit the work and wait for completion.
138 * XXX do we want an 'interruptible' flag vs. always being interruptible?
141 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
143 struct kauth_resolver_work
*workp
, *killp
;
145 int error
, shouldfree
;
147 /* no point actually blocking if the resolver isn't up yet */
148 if (kauth_resolver_identity
== 0) {
150 * We've already waited an initial 30 seconds with no result.
151 * Sleep on a stack address so no one wakes us before timeout;
152 * we sleep a half a second in case we are a high priority
153 * process, so that memberd doesn't starve while we are in a
154 * tight loop between user and kernel, eating all the CPU.
156 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
157 if (kauth_resolver_identity
== 0) {
159 * if things haven't changed while we were asleep,
160 * tell the caller we couldn't get an authoritative
167 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
171 workp
->kr_work
= *lkp
;
173 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
174 workp
->kr_result
= 0;
177 * We insert the request onto the unsubmitted queue, the call in from the
178 * resolver will it to the submitted thread when appropriate.
180 KAUTH_RESOLVER_LOCK();
181 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
182 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
184 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
185 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
187 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
189 /* we could compute a better timeout here */
192 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
193 /* request has been completed? */
194 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
196 /* woken because the resolver has died? */
197 if (kauth_resolver_identity
== 0) {
205 /* if the request was processed, copy the result */
207 *lkp
= workp
->kr_work
;
210 * If the request timed out and was never collected, the resolver is dead and
211 * probably not coming back anytime soon. In this case we revert to no-resolver
212 * behaviour, and punt all the other sleeping requests to clear the backlog.
214 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
215 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
216 kauth_resolver_identity
= 0;
217 /* kill all the other requestes that are waiting as well */
218 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
220 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
224 /* drop our reference on the work item, and note whether we should free it or not */
225 if (--workp
->kr_refs
<= 0) {
226 /* work out which list we have to remove it from */
227 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
228 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
229 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
230 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
231 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
232 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
234 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
238 /* someone else still has a reference on this request */
241 /* collect request result */
243 error
= workp
->kr_result
;
244 KAUTH_RESOLVER_UNLOCK();
246 * If we dropped the last reference, free the request.
249 FREE(workp
, M_KAUTH
);
251 KAUTH_DEBUG("RESOLVER - returning %d", error
);
256 * System call interface for the external identity resolver.
259 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
261 int opcode
= uap
->opcode
;
262 user_addr_t message
= uap
->message
;
263 struct kauth_resolver_work
*workp
;
268 * New server registering itself.
270 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
271 new_id
= current_proc()->p_pid
;
272 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
273 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
276 KAUTH_RESOLVER_LOCK();
277 if (kauth_resolver_identity
!= new_id
) {
278 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
280 * We have a new server, so assume that all the old requests have been lost.
282 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
283 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
284 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
285 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
286 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
288 kauth_resolver_identity
= new_id
;
289 kauth_resolver_registered
= 1;
290 wakeup(&kauth_resolver_unsubmitted
);
292 KAUTH_RESOLVER_UNLOCK();
297 * Beyond this point, we must be the resolver process.
299 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
300 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
305 * Got a result returning?
307 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
308 if ((error
= kauth_resolver_complete(message
)) != 0)
313 * Caller wants to take more work?
315 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
316 if ((error
= kauth_resolver_getwork(message
)) != 0)
324 * Get work for a caller.
327 kauth_resolver_getwork(user_addr_t message
)
329 struct kauth_resolver_work
*workp
;
332 KAUTH_RESOLVER_LOCK();
334 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
335 error
= msleep(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0);
340 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
341 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
344 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
345 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
346 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
347 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
351 KAUTH_RESOLVER_UNLOCK();
356 * Return a result from userspace.
359 kauth_resolver_complete(user_addr_t message
)
361 struct kauth_identity_extlookup extl
;
362 struct kauth_resolver_work
*workp
;
365 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
366 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
370 KAUTH_RESOLVER_LOCK();
374 switch (extl
.el_result
) {
375 case KAUTH_EXTLOOKUP_INPROG
:
379 /* XXX this should go away once memberd is updated */
381 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
386 case KAUTH_EXTLOOKUP_SUCCESS
:
389 case KAUTH_EXTLOOKUP_FATAL
:
390 /* fatal error means the resolver is dead */
391 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
392 kauth_resolver_identity
= 0;
393 /* XXX should we terminate all outstanding requests? */
396 case KAUTH_EXTLOOKUP_BADRQ
:
397 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
400 case KAUTH_EXTLOOKUP_FAILURE
:
401 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
405 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
411 * In the case of a fatal error, we assume that the resolver will restart
412 * quickly and re-collect all of the outstanding requests. Thus, we don't
413 * complete the request which returned the fatal error status.
415 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
416 /* scan our list for this request */
417 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
419 if (workp
->kr_seqno
== extl
.el_seqno
) {
421 workp
->kr_work
= extl
;
422 /* move onto completed list and wake up requester(s) */
423 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
424 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
425 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
426 workp
->kr_result
= result
;
427 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
434 * Note that it's OK for us not to find anything; if the request has
435 * timed out the work record will be gone.
437 KAUTH_RESOLVER_UNLOCK();
447 struct kauth_identity
{
448 TAILQ_ENTRY(kauth_identity
) ki_link
;
450 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
451 #define KI_VALID_GID (1<<1)
452 #define KI_VALID_GUID (1<<2)
453 #define KI_VALID_NTSID (1<<3)
459 * Expiry times are the earliest time at which we will disregard the cached state and go to
460 * userland. Before then if the valid bit is set, we will return the cached value. If it's
461 * not set, we will not go to userland to resolve, just assume that there is no answer
464 time_t ki_guid_expiry
;
465 time_t ki_ntsid_expiry
;
468 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
469 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
470 static int kauth_identity_count
;
472 static lck_mtx_t
*kauth_identity_mtx
;
473 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
474 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
477 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
478 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
479 static void kauth_identity_register(struct kauth_identity
*kip
);
480 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
481 static void kauth_identity_lru(struct kauth_identity
*kip
);
482 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
483 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
484 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
485 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
486 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
487 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
490 kauth_identity_init(void)
492 TAILQ_INIT(&kauth_identities
);
493 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
497 kauth_identity_resolve(__unused
struct kauth_identity_extlookup
*el
)
499 return(kauth_resolver_submit(el
));
502 static struct kauth_identity
*
503 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
505 struct kauth_identity
*kip
;
507 /* get and fill in a new identity */
508 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
510 if (gid
!= KAUTH_GID_NONE
) {
512 kip
->ki_valid
= KI_VALID_GID
;
514 if (uid
!= KAUTH_UID_NONE
) {
515 if (kip
->ki_valid
& KI_VALID_GID
)
516 panic("can't allocate kauth identity with both uid and gid");
518 kip
->ki_valid
= KI_VALID_UID
;
521 kip
->ki_guid
= *guidp
;
522 kip
->ki_valid
|= KI_VALID_GUID
;
524 kip
->ki_guid_expiry
= guid_expiry
;
525 if (ntsidp
!= NULL
) {
526 kip
->ki_ntsid
= *ntsidp
;
527 kip
->ki_valid
|= KI_VALID_NTSID
;
529 kip
->ki_ntsid_expiry
= ntsid_expiry
;
535 * Register an association between identity tokens.
538 kauth_identity_register(struct kauth_identity
*kip
)
540 struct kauth_identity
*ip
;
543 * We search the cache for the UID listed in the incoming association. If we
544 * already have an entry, the new information is merged.
547 KAUTH_IDENTITY_LOCK();
548 if (kip
->ki_valid
& KI_VALID_UID
) {
549 if (kip
->ki_valid
& KI_VALID_GID
)
550 panic("kauth_identity: can't insert record with both UID and GID as key");
551 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
552 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
554 } else if (kip
->ki_valid
& KI_VALID_GID
) {
555 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
556 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
559 panic("kauth_identity: can't insert record without UID or GID as key");
563 /* we already have an entry, merge/overwrite */
564 if (kip
->ki_valid
& KI_VALID_GUID
) {
565 ip
->ki_guid
= kip
->ki_guid
;
566 ip
->ki_valid
|= KI_VALID_GUID
;
568 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
569 if (kip
->ki_valid
& KI_VALID_NTSID
) {
570 ip
->ki_ntsid
= kip
->ki_ntsid
;
571 ip
->ki_valid
|= KI_VALID_NTSID
;
573 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
574 /* and discard the incoming identity */
578 /* don't have any information on this identity, so just add it */
579 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
580 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
581 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
582 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
583 kauth_identity_count
--;
586 KAUTH_IDENTITY_UNLOCK();
587 /* have to drop lock before freeing expired entry */
593 * Given a lookup result, add any associations that we don't
597 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
600 struct kauth_identity
*kip
;
605 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
606 KAUTH_IDENTITY_LOCK();
607 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
608 /* matching record */
609 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
610 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
611 kip
->ki_guid
= elp
->el_uguid
;
612 kip
->ki_valid
|= KI_VALID_GUID
;
614 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
615 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
616 kip
->ki_ntsid
= elp
->el_usid
;
617 kip
->ki_valid
|= KI_VALID_NTSID
;
619 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
620 kauth_identity_lru(kip
);
623 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
627 KAUTH_IDENTITY_UNLOCK();
628 /* not found in cache, add new record */
630 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
631 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
632 tv
.tv_sec
+ elp
->el_uguid_valid
,
633 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
634 tv
.tv_sec
+ elp
->el_usid_valid
);
638 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
639 kauth_identity_register(kip
);
644 /* group identity? */
645 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
646 KAUTH_IDENTITY_LOCK();
647 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
648 /* matching record */
649 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
650 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
651 kip
->ki_guid
= elp
->el_gguid
;
652 kip
->ki_valid
|= KI_VALID_GUID
;
654 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
655 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
656 kip
->ki_ntsid
= elp
->el_gsid
;
657 kip
->ki_valid
|= KI_VALID_NTSID
;
659 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
660 kauth_identity_lru(kip
);
663 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
667 KAUTH_IDENTITY_UNLOCK();
668 /* not found in cache, add new record */
670 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
671 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
672 tv
.tv_sec
+ elp
->el_gguid_valid
,
673 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
674 tv
.tv_sec
+ elp
->el_gsid_valid
);
678 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
679 kauth_identity_register(kip
);
687 * Promote the entry to the head of the LRU, assumes the cache is locked.
689 * This is called even if the entry has expired; typically an expired entry
690 * that's been looked up is about to be revalidated, and having it closer to
691 * the head of the LRU means finding it quickly again when the revalidation
695 kauth_identity_lru(struct kauth_identity
*kip
)
697 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
698 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
699 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
704 * Handly lazy expiration of translations.
707 kauth_identity_guid_expired(struct kauth_identity
*kip
)
712 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
713 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
717 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
722 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
723 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
727 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
728 * association exists for the UID.
731 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
733 struct kauth_identity
*kip
;
735 KAUTH_IDENTITY_LOCK();
736 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
737 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
738 kauth_identity_lru(kip
);
743 KAUTH_IDENTITY_UNLOCK();
744 return((kip
== NULL
) ? ENOENT
: 0);
749 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
750 * association exists for the GID.
753 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
755 struct kauth_identity
*kip
;
757 KAUTH_IDENTITY_LOCK();
758 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
759 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
760 kauth_identity_lru(kip
);
765 KAUTH_IDENTITY_UNLOCK();
766 return((kip
== NULL
) ? ENOENT
: 0);
771 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
772 * association exists for the GUID. Note that the association may be expired,
773 * in which case the caller may elect to call out to userland to revalidate.
776 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
778 struct kauth_identity
*kip
;
780 KAUTH_IDENTITY_LOCK();
781 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
782 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
783 kauth_identity_lru(kip
);
788 KAUTH_IDENTITY_UNLOCK();
789 return((kip
== NULL
) ? ENOENT
: 0);
793 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
794 * association exists for the SID. Note that the association may be expired,
795 * in which case the caller may elect to call out to userland to revalidate.
798 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
800 struct kauth_identity
*kip
;
802 KAUTH_IDENTITY_LOCK();
803 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
804 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
805 kauth_identity_lru(kip
);
810 KAUTH_IDENTITY_UNLOCK();
811 return((kip
== NULL
) ? ENOENT
: 0);
817 guid_t kauth_null_guid
;
820 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
822 return(!bcmp(guid1
, guid2
, sizeof(*guid1
)));
826 * Look for well-known GUIDs.
829 kauth_wellknown_guid(guid_t
*guid
)
831 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
834 * All WKGs begin with the same 12 bytes.
836 if (!bcmp((void *)guid
, fingerprint
, 12)) {
838 * The final 4 bytes are our code.
840 code
= *(u_int32_t
*)&guid
->g_guid
[12];
843 return(KAUTH_WKG_EVERYBODY
);
845 return(KAUTH_WKG_NOBODY
);
847 return(KAUTH_WKG_OWNER
);
849 return(KAUTH_WKG_GROUP
);
852 return(KAUTH_WKG_NOT
);
857 * NT Security Identifier handling.
860 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
862 /* check sizes for equality, also sanity-check size while we're at it */
863 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
864 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
865 !bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)))
873 * We support four tokens representing identity:
874 * - Credential reference
877 * - NT security identifier
879 * Of these, the UID is the ubiquitous identifier; cross-referencing should
883 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
886 * Fetch UID from credential.
889 kauth_cred_getuid(kauth_cred_t cred
)
891 NULLCRED_CHECK(cred
);
892 return(cred
->cr_uid
);
896 * Fetch GID from credential.
899 kauth_cred_getgid(kauth_cred_t cred
)
901 NULLCRED_CHECK(cred
);
902 return(cred
->cr_gid
);
906 * Fetch UID from GUID.
909 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
911 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
915 * Fetch GID from GUID.
918 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
920 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
924 * Fetch UID from NT SID.
927 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
929 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
933 * Fetch GID from NT SID.
936 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
938 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
942 * Fetch GUID from NT SID.
945 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
947 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
951 * Fetch GUID from UID.
954 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
956 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
960 * Fetch user GUID from credential.
963 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
965 NULLCRED_CHECK(cred
);
966 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
970 * Fetch GUID from GID.
973 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
975 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
979 * Fetch NT SID from UID.
982 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
984 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
988 * Fetch NT SID from credential.
991 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
993 NULLCRED_CHECK(cred
);
994 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
998 * Fetch NT SID from GID.
1001 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1003 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1007 * Fetch NT SID from GUID.
1010 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1012 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1018 * Lookup a translation in the cache.
1021 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1023 struct kauth_identity ki
;
1024 struct kauth_identity_extlookup el
;
1026 int (* expired
)(struct kauth_identity
*kip
);
1028 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1031 * Look for an existing cache entry for this association.
1032 * If the entry has not expired, return the cached information.
1037 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1040 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1043 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1045 case KI_VALID_NTSID
:
1046 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1051 /* lookup failure or error */
1053 /* any other error is fatal */
1054 if (error
!= ENOENT
) {
1055 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1059 /* do we have a translation? */
1060 if (ki
.ki_valid
& to
) {
1061 /* found a valid cached entry, check expiry */
1064 expired
= kauth_identity_guid_expired
;
1066 case KI_VALID_NTSID
:
1067 expired
= kauth_identity_ntsid_expired
;
1072 expired
= kauth_identity_guid_expired
;
1074 case KI_VALID_NTSID
:
1075 expired
= kauth_identity_ntsid_expired
;
1081 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1083 * If no expiry function, or not expired, we have found
1087 KAUTH_DEBUG("CACHE - no expiry function");
1090 if (!expired(&ki
)) {
1091 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1095 * We leave ki_valid set here; it contains a translation but the TTL has
1096 * expired. If we can't get a result from the resolver, we will
1097 * use it as a better-than nothing alternative.
1099 KAUTH_DEBUG("CACHE - expired entry found");
1104 * Call the resolver. We ask for as much data as we can get.
1108 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1109 el
.el_uid
= *(uid_t
*)src
;
1112 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1113 el
.el_gid
= *(gid_t
*)src
;
1116 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1117 el
.el_uguid
= *(guid_t
*)src
;
1118 el
.el_gguid
= *(guid_t
*)src
;
1120 case KI_VALID_NTSID
:
1121 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1122 el
.el_usid
= *(ntsid_t
*)src
;
1123 el
.el_gsid
= *(ntsid_t
*)src
;
1129 * Here we ask for everything all at once, to avoid having to work
1130 * out what we really want now, or might want soon.
1132 * Asking for SID translations when we don't know we need them right
1133 * now is going to cause excess work to be done if we're connected
1134 * to a network that thinks it can translate them. This list needs
1135 * to get smaller/smarter.
1137 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1138 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1139 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1140 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1141 error
= kauth_identity_resolve(&el
);
1142 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1143 /* was the lookup successful? */
1146 * Save the results from the lookup - may have other information even if we didn't
1149 kauth_identity_updatecache(&el
, &ki
);
1152 * Check to see if we have a valid result.
1154 if (!error
&& !(ki
.ki_valid
& to
))
1161 *(uid_t
*)dst
= ki
.ki_uid
;
1164 *(gid_t
*)dst
= ki
.ki_gid
;
1167 *(guid_t
*)dst
= ki
.ki_guid
;
1169 case KI_VALID_NTSID
:
1170 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1175 KAUTH_DEBUG("CACHE - returned successfully");
1181 * Group membership cache.
1183 * XXX the linked-list implementation here needs to be optimized.
1186 struct kauth_group_membership
{
1187 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1188 uid_t gm_uid
; /* the identity whose membership we're recording */
1189 gid_t gm_gid
; /* group of which they are a member */
1190 time_t gm_expiry
; /* TTL for the membership */
1192 #define KAUTH_GROUP_ISMEMBER (1<<0)
1195 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1196 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1197 static int kauth_groups_count
;
1199 static lck_mtx_t
*kauth_groups_mtx
;
1200 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1201 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1203 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1204 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1205 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1208 kauth_groups_init(void)
1210 TAILQ_INIT(&kauth_groups
);
1211 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1215 kauth_groups_expired(struct kauth_group_membership
*gm
)
1220 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1224 kauth_groups_lru(struct kauth_group_membership
*gm
)
1226 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1227 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1228 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1233 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1235 struct kauth_group_membership
*gm
;
1238 /* need a valid response if we are to cache anything */
1240 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1241 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1246 /* search for an existing record for this association before inserting */
1247 KAUTH_GROUPS_LOCK();
1248 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1249 if ((el
->el_uid
== gm
->gm_uid
) &&
1250 (el
->el_gid
== gm
->gm_gid
)) {
1251 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1252 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1254 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1256 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1257 kauth_groups_lru(gm
);
1261 KAUTH_GROUPS_UNLOCK();
1263 /* if we found an entry to update, stop here */
1267 /* allocate a new record */
1268 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
1270 gm
->gm_uid
= el
->el_uid
;
1271 gm
->gm_gid
= el
->el_gid
;
1272 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1273 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1275 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1277 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1281 * Insert the new entry. Note that it's possible to race ourselves here
1282 * and end up with duplicate entries in the list. Wasteful, but harmless
1283 * since the first into the list will never be looked up, and thus will
1284 * eventually just fall off the end.
1286 KAUTH_GROUPS_LOCK();
1287 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1288 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
1289 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
1290 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1291 kauth_groups_count
--;
1295 KAUTH_GROUPS_UNLOCK();
1297 /* free expired cache entry */
1303 * Group membership KPI
1306 * This function guarantees not to modify resultp when returning an error.
1309 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
1311 struct kauth_group_membership
*gm
;
1312 struct kauth_identity_extlookup el
;
1316 * Check the per-credential list of override groups.
1318 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1319 * the cache should be used for that case.
1321 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
1322 if (gid
== cred
->cr_groups
[i
]) {
1329 * If we don't have a UID for group membership checks, the in-cred list
1330 * was authoritative and we can stop here.
1332 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
1339 * If the resolver hasn't checked in yet, we are early in the boot phase and
1340 * the local group list is complete and authoritative.
1342 if (!kauth_resolver_registered
) {
1348 /* XXX check supplementary groups */
1349 /* XXX check whiteout groups */
1350 /* XXX nesting of supplementary/whiteout groups? */
1353 * Check the group cache.
1355 KAUTH_GROUPS_LOCK();
1356 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1357 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
1358 kauth_groups_lru(gm
);
1363 /* did we find a membership entry? */
1365 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
1366 KAUTH_GROUPS_UNLOCK();
1368 /* if we did, we can return now */
1372 /* nothing in the cache, need to go to userland */
1373 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
1374 el
.el_uid
= cred
->cr_gmuid
;
1376 error
= kauth_identity_resolve(&el
);
1379 /* save the results from the lookup */
1380 kauth_groups_updatecache(&el
);
1382 /* if we successfully ascertained membership, report */
1383 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
1384 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
1392 * Determine whether the supplied credential is a member of the
1393 * group nominated by GUID.
1396 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
1402 wkg
= kauth_wellknown_guid(guidp
);
1404 case KAUTH_WKG_NOBODY
:
1407 case KAUTH_WKG_EVERYBODY
:
1411 /* translate guid to gid */
1412 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
1414 * If we have no guid -> gid translation, it's not a group and
1415 * thus the cred can't be a member.
1417 if (error
== ENOENT
) {
1422 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
1429 * Fast replacement for issuser()
1432 kauth_cred_issuser(kauth_cred_t cred
)
1434 return(cred
->cr_uid
== 0);
1441 /* lock protecting credential hash table */
1442 static lck_mtx_t
*kauth_cred_hash_mtx
;
1443 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1444 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1447 kauth_cred_init(void)
1451 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1452 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
1454 /*allocate credential hash table */
1455 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
1456 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
1457 M_KAUTH
, M_WAITOK
| M_ZERO
);
1458 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
1459 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
1464 * Return the current thread's effective UID.
1469 return(kauth_cred_get()->cr_uid
);
1473 * Return the current thread's real UID.
1478 return(kauth_cred_get()->cr_ruid
);
1482 * Return the current thread's effective GID.
1487 return(kauth_cred_get()->cr_groups
[0]);
1491 * Return the current thread's real GID.
1496 return(kauth_cred_get()->cr_rgid
);
1500 * Returns a pointer to the current thread's credential, does not take a
1501 * reference (so the caller must not do anything that would let the thread's
1502 * credential change while using the returned value).
1505 kauth_cred_get(void)
1508 struct uthread
*uthread
;
1510 uthread
= get_bsdthread_info(current_thread());
1512 if (uthread
== NULL
)
1513 panic("thread wants credential but has no BSD thread info");
1515 * We can lazy-bind credentials to threads, as long as their processes have them.
1516 * If we later inline this function, the code in this block should probably be
1517 * called out in a function.
1519 if (uthread
->uu_ucred
== NOCRED
) {
1520 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1521 panic("thread wants credential but has no BSD process");
1523 kauth_cred_ref(uthread
->uu_ucred
= p
->p_ucred
);
1526 return(uthread
->uu_ucred
);
1530 * Returns a pointer to the current thread's credential, takes a reference.
1533 kauth_cred_get_with_ref(void)
1536 struct uthread
*uthread
;
1538 uthread
= get_bsdthread_info(current_thread());
1540 if (uthread
== NULL
)
1541 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
1542 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1543 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
1546 * We can lazy-bind credentials to threads, as long as their processes have them.
1547 * If we later inline this function, the code in this block should probably be
1548 * called out in a function.
1551 if (uthread
->uu_ucred
== NOCRED
) {
1552 /* take reference for new cred in thread */
1553 kauth_cred_ref(uthread
->uu_ucred
= proc_ucred(procp
));
1555 /* take a reference for our caller */
1556 kauth_cred_ref(uthread
->uu_ucred
);
1558 return(uthread
->uu_ucred
);
1562 * Returns a pointer to the given process's credential, takes a reference.
1565 kauth_cred_proc_ref(proc_t procp
)
1570 cred
= proc_ucred(procp
);
1571 kauth_cred_ref(cred
);
1577 * Allocates a new credential.
1580 kauth_cred_alloc(void)
1582 kauth_cred_t newcred
;
1584 MALLOC(newcred
, kauth_cred_t
, sizeof(*newcred
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1586 newcred
->cr_ref
= 1;
1587 /* must do this, or cred has same group membership as uid 0 */
1588 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
1591 panic("kauth_cred_alloc: couldn't allocate credential");
1595 #if KAUTH_CRED_HASH_DEBUG
1603 * Looks to see if we already have a known credential and if found bumps the
1604 * reference count and returns it. If there are no credentials that match
1605 * the given credential then we allocate a new credential.
1607 * Note that the gmuid is hard-defaulted to the UID specified. Since we maintain
1608 * this field, we can't expect callers to know how it needs to be set. Callers
1609 * should be prepared for this field to be overwritten.
1612 kauth_cred_create(kauth_cred_t cred
)
1614 kauth_cred_t found_cred
, new_cred
= NULL
;
1616 cred
->cr_gmuid
= cred
->cr_uid
;
1619 KAUTH_CRED_HASH_LOCK();
1620 found_cred
= kauth_cred_find(cred
);
1621 if (found_cred
!= NULL
) {
1622 /* found an existing credential so we'll bump reference count and return */
1623 kauth_cred_ref(found_cred
);
1624 KAUTH_CRED_HASH_UNLOCK();
1627 KAUTH_CRED_HASH_UNLOCK();
1629 /* no existing credential found. create one and add it to our hash table */
1630 new_cred
= kauth_cred_alloc();
1631 if (new_cred
!= NULL
) {
1633 new_cred
->cr_uid
= cred
->cr_uid
;
1634 new_cred
->cr_ruid
= cred
->cr_ruid
;
1635 new_cred
->cr_svuid
= cred
->cr_svuid
;
1636 new_cred
->cr_rgid
= cred
->cr_rgid
;
1637 new_cred
->cr_svgid
= cred
->cr_svgid
;
1638 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
1639 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
1640 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
1641 KAUTH_CRED_HASH_LOCK();
1642 err
= kauth_cred_add(new_cred
);
1643 KAUTH_CRED_HASH_UNLOCK();
1645 /* retry if kauth_cred_add returns non zero value */
1648 FREE(new_cred
, M_KAUTH
);
1657 * Update the given credential using the uid argument. The given uid is used
1658 * set the effective user ID, real user ID, and saved user ID. We only
1659 * allocate a new credential when the given uid actually results in changes to
1660 * the existing credential.
1663 kauth_cred_setuid(kauth_cred_t cred
, uid_t uid
)
1665 struct ucred temp_cred
;
1667 NULLCRED_CHECK(cred
);
1669 /* don't need to do anything if the effective, real and saved user IDs are
1670 * already the same as the user ID passed in
1672 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
) {
1673 /* no change needed */
1677 /* look up in cred hash table to see if we have a matching credential
1680 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1681 temp_cred
.cr_uid
= uid
;
1682 temp_cred
.cr_ruid
= uid
;
1683 temp_cred
.cr_svuid
= uid
;
1684 temp_cred
.cr_gmuid
= uid
;
1686 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1690 * Update the given credential using the euid argument. The given uid is used
1691 * set the effective user ID. We only allocate a new credential when the given
1692 * uid actually results in changes to the existing credential.
1695 kauth_cred_seteuid(kauth_cred_t cred
, uid_t euid
)
1697 struct ucred temp_cred
;
1699 NULLCRED_CHECK(cred
);
1701 /* don't need to do anything if the given effective user ID is already the
1702 * same as the effective user ID in the credential.
1704 if (cred
->cr_uid
== euid
) {
1705 /* no change needed */
1709 /* look up in cred hash table to see if we have a matching credential
1712 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1713 temp_cred
.cr_uid
= euid
;
1715 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1719 * Update the given credential using the gid argument. The given gid is used
1720 * set the effective group ID, real group ID, and saved group ID. We only
1721 * allocate a new credential when the given gid actually results in changes to
1722 * the existing credential.
1725 kauth_cred_setgid(kauth_cred_t cred
, gid_t gid
)
1727 struct ucred temp_cred
;
1729 NULLCRED_CHECK(cred
);
1731 /* don't need to do anything if the given group ID is already the
1732 * same as the group ID in the credential.
1734 if (cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1735 /* no change needed */
1739 /* look up in cred hash table to see if we have a matching credential
1742 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1743 temp_cred
.cr_groups
[0] = gid
;
1744 temp_cred
.cr_rgid
= gid
;
1745 temp_cred
.cr_svgid
= gid
;
1747 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1751 * Update the given credential using the egid argument. The given gid is used
1752 * set the effective user ID. We only allocate a new credential when the given
1753 * gid actually results in changes to the existing credential.
1756 kauth_cred_setegid(kauth_cred_t cred
, gid_t egid
)
1758 struct ucred temp_cred
;
1760 NULLCRED_CHECK(cred
);
1762 /* don't need to do anything if the given group ID is already the
1763 * same as the group Id in the credential.
1765 if (cred
->cr_groups
[0] == egid
) {
1766 /* no change needed */
1770 /* look up in cred hash table to see if we have a matching credential
1773 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1774 temp_cred
.cr_groups
[0] = egid
;
1776 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1780 * Update the given credential with the given groups. We only allocate a new
1781 * credential when the given gid actually results in changes to the existing
1783 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1784 * which will be used for group membership checking.
1787 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
1790 struct ucred temp_cred
;
1792 NULLCRED_CHECK(cred
);
1794 /* don't need to do anything if the given list of groups does not change.
1796 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
1797 for (i
= 0; i
< groupcount
; i
++) {
1798 if (cred
->cr_groups
[i
] != groups
[i
])
1801 if (i
== groupcount
) {
1802 /* no change needed */
1807 /* look up in cred hash table to see if we have a matching credential
1810 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1811 temp_cred
.cr_ngroups
= groupcount
;
1812 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
1813 temp_cred
.cr_gmuid
= gmuid
;
1815 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1819 * Update the given credential using the uid and gid arguments. The given uid
1820 * is used set the effective user ID, real user ID, and saved user ID.
1821 * The given gid is used set the effective group ID, real group ID, and saved
1823 * We only allocate a new credential when the given uid and gid actually results
1824 * in changes to the existing credential.
1827 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1829 struct ucred temp_cred
;
1831 NULLCRED_CHECK(cred
);
1833 /* don't need to do anything if the effective, real and saved user IDs are
1834 * already the same as the user ID passed in
1836 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
1837 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1838 /* no change needed */
1842 /* look up in cred hash table to see if we have a matching credential
1845 bzero(&temp_cred
, sizeof(temp_cred
));
1846 temp_cred
.cr_uid
= uid
;
1847 temp_cred
.cr_ruid
= uid
;
1848 temp_cred
.cr_svuid
= uid
;
1849 temp_cred
.cr_gmuid
= uid
;
1850 temp_cred
.cr_ngroups
= 1;
1851 temp_cred
.cr_groups
[0] = gid
;
1852 temp_cred
.cr_rgid
= gid
;
1853 temp_cred
.cr_svgid
= gid
;
1855 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1859 * Update the given credential using the uid and gid arguments. The given uid
1860 * is used to set the saved user ID. The given gid is used to set the
1862 * We only allocate a new credential when the given uid and gid actually results
1863 * in changes to the existing credential.
1866 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1868 struct ucred temp_cred
;
1870 NULLCRED_CHECK(cred
);
1872 /* don't need to do anything if the effective, real and saved user IDs are
1873 * already the same as the user ID passed in
1875 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
1876 /* no change needed */
1880 /* look up in cred hash table to see if we have a matching credential
1883 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1884 temp_cred
.cr_svuid
= uid
;
1885 temp_cred
.cr_svgid
= gid
;
1887 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1891 * Update the given credential using the given auditinfo_t.
1892 * We only allocate a new credential when the given auditinfo_t actually results
1893 * in changes to the existing credential.
1896 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
1898 struct ucred temp_cred
;
1900 NULLCRED_CHECK(cred
);
1902 /* don't need to do anything if the audit info is already the same as the
1903 * audit info in the credential passed in
1905 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
1906 /* no change needed */
1910 /* look up in cred hash table to see if we have a matching credential
1913 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1914 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
1916 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
1920 * Add a reference to the passed credential.
1923 kauth_cred_ref(kauth_cred_t cred
)
1927 NULLCRED_CHECK(cred
);
1929 old_value
= OSAddAtomic(1, &cred
->cr_ref
);
1932 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1938 * Drop a reference from the passed credential, potentially destroying it.
1941 kauth_cred_rele(kauth_cred_t cred
)
1945 NULLCRED_CHECK(cred
);
1947 KAUTH_CRED_HASH_LOCK();
1948 old_value
= OSAddAtomic(-1, &cred
->cr_ref
);
1952 panic("kauth_cred_rele: dropping a reference on a cred with no references");
1955 if (old_value
< 3) {
1956 /* the last reference is our credential hash table */
1957 kauth_cred_remove(cred
);
1959 KAUTH_CRED_HASH_UNLOCK();
1963 * Duplicate a credential.
1964 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1967 kauth_cred_dup(kauth_cred_t cred
)
1969 kauth_cred_t newcred
;
1972 if (cred
== NOCRED
|| cred
== FSCRED
)
1973 panic("kauth_cred_dup: bad credential");
1975 newcred
= kauth_cred_alloc();
1976 if (newcred
!= NULL
) {
1977 bcopy(cred
, newcred
, sizeof(*newcred
));
1978 newcred
->cr_ref
= 1;
1984 * Returns a credential based on the passed credential but which
1985 * reflects the real rather than effective UID and GID.
1986 * NOTE - we do NOT decrement cred reference count on passed in credential
1989 kauth_cred_copy_real(kauth_cred_t cred
)
1991 kauth_cred_t newcred
= NULL
, found_cred
;
1992 struct ucred temp_cred
;
1994 /* if the credential is already 'real', just take a reference */
1995 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
1996 (cred
->cr_rgid
== cred
->cr_gid
)) {
1997 kauth_cred_ref(cred
);
2001 /* look up in cred hash table to see if we have a matching credential
2004 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2005 temp_cred
.cr_uid
= cred
->cr_ruid
;
2006 temp_cred
.cr_groups
[0] = cred
->cr_rgid
;
2007 /* if the cred is not opted out, make sure we are using the r/euid for group checks */
2008 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
2009 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
2014 KAUTH_CRED_HASH_LOCK();
2015 found_cred
= kauth_cred_find(&temp_cred
);
2016 if (found_cred
== cred
) {
2017 /* same cred so just bail */
2018 KAUTH_CRED_HASH_UNLOCK();
2021 if (found_cred
!= NULL
) {
2022 /* found a match so we bump reference count on new one and decrement
2023 * reference count on the old one.
2025 kauth_cred_ref(found_cred
);
2026 KAUTH_CRED_HASH_UNLOCK();
2030 /* must allocate a new credential, copy in old credential data and update
2031 * with real user and group IDs.
2033 newcred
= kauth_cred_dup(&temp_cred
);
2034 err
= kauth_cred_add(newcred
);
2035 KAUTH_CRED_HASH_UNLOCK();
2037 /* retry if kauth_cred_add returns non zero value */
2040 FREE(newcred
, M_KAUTH
);
2048 * common code to update a credential. model_cred is a temporary, non reference
2049 * counted credential used only for comparison and modeling purposes. old_cred
2050 * is a live reference counted credential that we intend to update using model_cred
2053 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
, boolean_t retain_auditinfo
)
2055 kauth_cred_t found_cred
, new_cred
= NULL
;
2057 /* make sure we carry the auditinfo forward to the new credential unless
2058 * we are actually updating the auditinfo.
2060 if (retain_auditinfo
)
2061 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
2066 KAUTH_CRED_HASH_LOCK();
2067 found_cred
= kauth_cred_find(model_cred
);
2068 if (found_cred
== old_cred
) {
2069 /* same cred so just bail */
2070 KAUTH_CRED_HASH_UNLOCK();
2073 if (found_cred
!= NULL
) {
2074 /* found a match so we bump reference count on new one and decrement
2075 * reference count on the old one.
2077 kauth_cred_ref(found_cred
);
2078 KAUTH_CRED_HASH_UNLOCK();
2079 kauth_cred_rele(old_cred
);
2083 /* must allocate a new credential using the model. also
2084 * adds the new credential to the credential hash table.
2086 new_cred
= kauth_cred_dup(model_cred
);
2087 err
= kauth_cred_add(new_cred
);
2088 KAUTH_CRED_HASH_UNLOCK();
2090 /* retry if kauth_cred_add returns non zero value */
2093 FREE(new_cred
, M_KAUTH
);
2097 kauth_cred_rele(old_cred
);
2102 * Add the given credential to our credential hash table and take an additional
2103 * reference to account for our use of the credential in the hash table.
2104 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2106 static int kauth_cred_add(kauth_cred_t new_cred
)
2110 hash_key
= kauth_cred_get_hashkey(new_cred
);
2111 hash_key
%= kauth_cred_table_size
;
2113 /* race fix - there is a window where another matching credential
2114 * could have been inserted between the time this one was created and we
2115 * got the hash lock. If we find a match return an error and have the
2118 if (kauth_cred_find(new_cred
) != NULL
) {
2122 /* take a reference for our use in credential hash table */
2123 kauth_cred_ref(new_cred
);
2125 /* insert the credential into the hash table */
2126 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
2132 * Remove the given credential from our credential hash table.
2133 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2135 static void kauth_cred_remove(kauth_cred_t cred
)
2138 kauth_cred_t found_cred
;
2140 hash_key
= kauth_cred_get_hashkey(cred
);
2141 hash_key
%= kauth_cred_table_size
;
2144 if (cred
->cr_ref
< 1)
2145 panic("cred reference underflow");
2146 if (cred
->cr_ref
> 1)
2147 return; /* someone else got a ref */
2149 /* find cred in the credential hash table */
2150 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2151 if (found_cred
== cred
) {
2152 /* found a match, remove it from the hash table */
2153 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
2154 FREE(cred
, M_KAUTH
);
2155 #if KAUTH_CRED_HASH_DEBUG
2162 /* did not find a match. this should not happen! */
2163 printf("%s - %d - %s - did not find a match \n", __FILE__
, __LINE__
, __FUNCTION__
);
2168 * Using the given credential data, look for a match in our credential hash
2170 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2172 kauth_cred_t
kauth_cred_find(kauth_cred_t cred
)
2175 kauth_cred_t found_cred
;
2177 #if KAUTH_CRED_HASH_DEBUG
2178 static int test_count
= 0;
2181 if ((test_count
% 200) == 0) {
2182 kauth_cred_hash_print();
2186 hash_key
= kauth_cred_get_hashkey(cred
);
2187 hash_key
%= kauth_cred_table_size
;
2189 /* find cred in the credential hash table */
2190 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2191 if (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
, (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
))) == 0) {
2196 /* no match found */
2201 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2203 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
)
2205 u_long hash_key
= 0;
2207 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
2208 (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
)),
2214 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2216 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
2218 u_long hash_key
= start_key
;
2221 while (data_len
> 0) {
2222 hash_key
= (hash_key
<< 4) + *datap
++;
2223 temp
= hash_key
& 0xF0000000;
2225 hash_key
^= temp
>> 24;
2233 #if KAUTH_CRED_HASH_DEBUG
2234 static void kauth_cred_hash_print(void)
2237 kauth_cred_t found_cred
;
2239 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
2240 /* count slot hits, misses, collisions, and max depth */
2241 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2242 printf("[%02d] ", i
);
2244 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
2249 kauth_cred_print(found_cred
);
2253 printf("NOCRED \n");
2259 static void kauth_cred_print(kauth_cred_t cred
)
2263 printf("0x%02X - refs %d uids %d %d %d ", cred
, cred
->cr_ref
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
);
2264 printf("group count %d gids ", cred
->cr_ngroups
);
2265 for (i
= 0; i
< NGROUPS
; i
++) {
2266 printf("%d ", cred
->cr_groups
[i
]);
2268 printf("%d %d %d ", cred
->cr_rgid
, cred
->cr_svgid
, cred
->cr_gmuid
);
2269 printf("auditinfo %d %d %d %d %d %d ",
2270 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
2271 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);