2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Kernel Authorization framework: Management of process/thread credentials and identity information.
29 #include <sys/param.h> /* XXX trim includes */
31 #include <sys/systm.h>
32 #include <sys/ucred.h>
33 #include <sys/proc_internal.h>
35 #include <sys/timeb.h>
36 #include <sys/times.h>
37 #include <sys/malloc.h>
38 #include <sys/kauth.h>
39 #include <sys/kernel.h>
41 #include <bsm/audit_kernel.h>
43 #include <sys/mount.h>
44 #include <sys/sysproto.h>
45 #include <mach/message.h>
46 #include <mach/host_security.h>
48 #include <libkern/OSAtomic.h>
50 #include <kern/task.h>
51 #include <kern/lock.h>
55 #define MACH_ASSERT 1 /* XXX so bogus */
56 #include <kern/assert.h>
58 #define CRED_DIAGNOSTIC 1
60 # define NULLCRED_CHECK(_c) do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
63 * Interface to external identity resolver.
65 * The architecture of the interface is simple; the external resolver calls in to
66 * get work, then calls back with completed work. It also calls us to let us know
67 * that it's (re)started, so that we can resubmit work if it times out.
70 static lck_mtx_t
*kauth_resolver_mtx
;
71 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
72 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
74 static volatile pid_t kauth_resolver_identity
;
75 static int kauth_resolver_registered
;
76 static uint32_t kauth_resolver_sequence
;
78 struct kauth_resolver_work
{
79 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
80 struct kauth_identity_extlookup kr_work
;
84 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
85 #define KAUTH_REQUEST_SUBMITTED (1<<1)
86 #define KAUTH_REQUEST_DONE (1<<2)
90 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
91 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
92 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
94 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
95 static int kauth_resolver_complete(user_addr_t message
);
96 static int kauth_resolver_getwork(user_addr_t message
);
98 #define KAUTH_CRED_PRIMES_COUNT 7
99 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = {97, 241, 397, 743, 1499, 3989, 7499};
100 static int kauth_cred_primes_index
= 0;
101 static int kauth_cred_table_size
= 0;
103 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
104 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
106 #define KAUTH_CRED_HASH_DEBUG 0
108 static int kauth_cred_add(kauth_cred_t new_cred
);
109 static void kauth_cred_remove(kauth_cred_t cred
);
110 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
111 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
112 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
114 #if KAUTH_CRED_HASH_DEBUG
115 static int kauth_cred_count
= 0;
116 static void kauth_cred_hash_print(void);
117 static void kauth_cred_print(kauth_cred_t cred
);
121 kauth_resolver_init(void)
123 TAILQ_INIT(&kauth_resolver_unsubmitted
);
124 TAILQ_INIT(&kauth_resolver_submitted
);
125 TAILQ_INIT(&kauth_resolver_done
);
126 kauth_resolver_sequence
= 31337;
127 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
131 * Allocate a work queue entry, submit the work and wait for completion.
133 * XXX do we want an 'interruptible' flag vs. always being interruptible?
136 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
138 struct kauth_resolver_work
*workp
, *killp
;
140 int error
, shouldfree
;
142 /* no point actually blocking if the resolver isn't up yet */
143 if (kauth_resolver_identity
== 0) {
145 * We've already waited an initial 30 seconds with no result.
146 * Sleep on a stack address so no one wakes us before timeout;
147 * we sleep a half a second in case we are a high priority
148 * process, so that memberd doesn't starve while we are in a
149 * tight loop between user and kernel, eating all the CPU.
151 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
152 if (kauth_resolver_identity
== 0) {
154 * if things haven't changed while we were asleep,
155 * tell the caller we couldn't get an authoritative
162 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
166 workp
->kr_work
= *lkp
;
168 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
169 workp
->kr_result
= 0;
172 * We insert the request onto the unsubmitted queue, the call in from the
173 * resolver will it to the submitted thread when appropriate.
175 KAUTH_RESOLVER_LOCK();
176 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
177 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
179 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
180 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
182 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
184 /* we could compute a better timeout here */
187 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
188 /* request has been completed? */
189 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
191 /* woken because the resolver has died? */
192 if (kauth_resolver_identity
== 0) {
200 /* if the request was processed, copy the result */
202 *lkp
= workp
->kr_work
;
205 * If the request timed out and was never collected, the resolver is dead and
206 * probably not coming back anytime soon. In this case we revert to no-resolver
207 * behaviour, and punt all the other sleeping requests to clear the backlog.
209 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
210 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
211 kauth_resolver_identity
= 0;
212 /* kill all the other requestes that are waiting as well */
213 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
215 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
219 /* drop our reference on the work item, and note whether we should free it or not */
220 if (--workp
->kr_refs
<= 0) {
221 /* work out which list we have to remove it from */
222 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
223 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
224 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
225 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
226 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
227 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
229 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
233 /* someone else still has a reference on this request */
236 /* collect request result */
238 error
= workp
->kr_result
;
239 KAUTH_RESOLVER_UNLOCK();
241 * If we dropped the last reference, free the request.
244 FREE(workp
, M_KAUTH
);
246 KAUTH_DEBUG("RESOLVER - returning %d", error
);
251 * System call interface for the external identity resolver.
254 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
256 int opcode
= uap
->opcode
;
257 user_addr_t message
= uap
->message
;
258 struct kauth_resolver_work
*workp
;
263 * New server registering itself.
265 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
266 new_id
= current_proc()->p_pid
;
267 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
268 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
271 KAUTH_RESOLVER_LOCK();
272 if (kauth_resolver_identity
!= new_id
) {
273 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
275 * We have a new server, so assume that all the old requests have been lost.
277 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
278 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
279 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
280 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
281 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
283 kauth_resolver_identity
= new_id
;
284 kauth_resolver_registered
= 1;
285 wakeup(&kauth_resolver_unsubmitted
);
287 KAUTH_RESOLVER_UNLOCK();
292 * Beyond this point, we must be the resolver process.
294 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
295 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
300 * Got a result returning?
302 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
303 if ((error
= kauth_resolver_complete(message
)) != 0)
308 * Caller wants to take more work?
310 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
311 if ((error
= kauth_resolver_getwork(message
)) != 0)
319 * Get work for a caller.
322 kauth_resolver_getwork(user_addr_t message
)
324 struct kauth_resolver_work
*workp
;
327 KAUTH_RESOLVER_LOCK();
329 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
330 error
= msleep(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0);
335 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
336 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
339 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
340 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
341 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
342 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
346 KAUTH_RESOLVER_UNLOCK();
351 * Return a result from userspace.
354 kauth_resolver_complete(user_addr_t message
)
356 struct kauth_identity_extlookup extl
;
357 struct kauth_resolver_work
*workp
;
360 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
361 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
365 KAUTH_RESOLVER_LOCK();
369 switch (extl
.el_result
) {
370 case KAUTH_EXTLOOKUP_INPROG
:
374 /* XXX this should go away once memberd is updated */
376 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
381 case KAUTH_EXTLOOKUP_SUCCESS
:
384 case KAUTH_EXTLOOKUP_FATAL
:
385 /* fatal error means the resolver is dead */
386 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
387 kauth_resolver_identity
= 0;
388 /* XXX should we terminate all outstanding requests? */
391 case KAUTH_EXTLOOKUP_BADRQ
:
392 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
395 case KAUTH_EXTLOOKUP_FAILURE
:
396 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
400 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
406 * In the case of a fatal error, we assume that the resolver will restart
407 * quickly and re-collect all of the outstanding requests. Thus, we don't
408 * complete the request which returned the fatal error status.
410 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
411 /* scan our list for this request */
412 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
414 if (workp
->kr_seqno
== extl
.el_seqno
) {
416 workp
->kr_work
= extl
;
417 /* move onto completed list and wake up requester(s) */
418 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
419 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
420 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
421 workp
->kr_result
= result
;
422 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
429 * Note that it's OK for us not to find anything; if the request has
430 * timed out the work record will be gone.
432 KAUTH_RESOLVER_UNLOCK();
442 struct kauth_identity
{
443 TAILQ_ENTRY(kauth_identity
) ki_link
;
445 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
446 #define KI_VALID_GID (1<<1)
447 #define KI_VALID_GUID (1<<2)
448 #define KI_VALID_NTSID (1<<3)
454 * Expiry times are the earliest time at which we will disregard the cached state and go to
455 * userland. Before then if the valid bit is set, we will return the cached value. If it's
456 * not set, we will not go to userland to resolve, just assume that there is no answer
459 time_t ki_guid_expiry
;
460 time_t ki_ntsid_expiry
;
463 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
464 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
465 static int kauth_identity_count
;
467 static lck_mtx_t
*kauth_identity_mtx
;
468 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
469 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
472 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
473 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
474 static void kauth_identity_register(struct kauth_identity
*kip
);
475 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
476 static void kauth_identity_lru(struct kauth_identity
*kip
);
477 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
478 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
479 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
480 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
481 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
482 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
485 kauth_identity_init(void)
487 TAILQ_INIT(&kauth_identities
);
488 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
492 kauth_identity_resolve(__unused
struct kauth_identity_extlookup
*el
)
494 return(kauth_resolver_submit(el
));
497 static struct kauth_identity
*
498 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
500 struct kauth_identity
*kip
;
502 /* get and fill in a new identity */
503 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
505 if (gid
!= KAUTH_GID_NONE
) {
507 kip
->ki_valid
= KI_VALID_GID
;
509 if (uid
!= KAUTH_UID_NONE
) {
510 if (kip
->ki_valid
& KI_VALID_GID
)
511 panic("can't allocate kauth identity with both uid and gid");
513 kip
->ki_valid
= KI_VALID_UID
;
516 kip
->ki_guid
= *guidp
;
517 kip
->ki_valid
|= KI_VALID_GUID
;
519 kip
->ki_guid_expiry
= guid_expiry
;
520 if (ntsidp
!= NULL
) {
521 kip
->ki_ntsid
= *ntsidp
;
522 kip
->ki_valid
|= KI_VALID_NTSID
;
524 kip
->ki_ntsid_expiry
= ntsid_expiry
;
530 * Register an association between identity tokens.
533 kauth_identity_register(struct kauth_identity
*kip
)
535 struct kauth_identity
*ip
;
538 * We search the cache for the UID listed in the incoming association. If we
539 * already have an entry, the new information is merged.
542 KAUTH_IDENTITY_LOCK();
543 if (kip
->ki_valid
& KI_VALID_UID
) {
544 if (kip
->ki_valid
& KI_VALID_GID
)
545 panic("kauth_identity: can't insert record with both UID and GID as key");
546 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
547 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
549 } else if (kip
->ki_valid
& KI_VALID_GID
) {
550 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
551 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
554 panic("kauth_identity: can't insert record without UID or GID as key");
558 /* we already have an entry, merge/overwrite */
559 if (kip
->ki_valid
& KI_VALID_GUID
) {
560 ip
->ki_guid
= kip
->ki_guid
;
561 ip
->ki_valid
|= KI_VALID_GUID
;
563 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
564 if (kip
->ki_valid
& KI_VALID_NTSID
) {
565 ip
->ki_ntsid
= kip
->ki_ntsid
;
566 ip
->ki_valid
|= KI_VALID_NTSID
;
568 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
569 /* and discard the incoming identity */
573 /* don't have any information on this identity, so just add it */
574 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
575 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
576 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
577 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
578 kauth_identity_count
--;
581 KAUTH_IDENTITY_UNLOCK();
582 /* have to drop lock before freeing expired entry */
588 * Given a lookup result, add any associations that we don't
592 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
595 struct kauth_identity
*kip
;
600 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
601 KAUTH_IDENTITY_LOCK();
602 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
603 /* matching record */
604 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
605 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
606 kip
->ki_guid
= elp
->el_uguid
;
607 kip
->ki_valid
|= KI_VALID_GUID
;
609 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
610 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
611 kip
->ki_ntsid
= elp
->el_usid
;
612 kip
->ki_valid
|= KI_VALID_NTSID
;
614 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
615 kauth_identity_lru(kip
);
618 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
622 KAUTH_IDENTITY_UNLOCK();
623 /* not found in cache, add new record */
625 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
626 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
627 tv
.tv_sec
+ elp
->el_uguid_valid
,
628 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
629 tv
.tv_sec
+ elp
->el_usid_valid
);
633 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
634 kauth_identity_register(kip
);
639 /* group identity? */
640 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
641 KAUTH_IDENTITY_LOCK();
642 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
643 /* matching record */
644 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
645 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
646 kip
->ki_guid
= elp
->el_gguid
;
647 kip
->ki_valid
|= KI_VALID_GUID
;
649 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
650 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
651 kip
->ki_ntsid
= elp
->el_gsid
;
652 kip
->ki_valid
|= KI_VALID_NTSID
;
654 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
655 kauth_identity_lru(kip
);
658 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
662 KAUTH_IDENTITY_UNLOCK();
663 /* not found in cache, add new record */
665 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
666 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
667 tv
.tv_sec
+ elp
->el_gguid_valid
,
668 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
669 tv
.tv_sec
+ elp
->el_gsid_valid
);
673 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
674 kauth_identity_register(kip
);
682 * Promote the entry to the head of the LRU, assumes the cache is locked.
684 * This is called even if the entry has expired; typically an expired entry
685 * that's been looked up is about to be revalidated, and having it closer to
686 * the head of the LRU means finding it quickly again when the revalidation
690 kauth_identity_lru(struct kauth_identity
*kip
)
692 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
693 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
694 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
699 * Handly lazy expiration of translations.
702 kauth_identity_guid_expired(struct kauth_identity
*kip
)
707 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
708 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
712 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
717 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
718 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
722 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
723 * association exists for the UID.
726 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
728 struct kauth_identity
*kip
;
730 KAUTH_IDENTITY_LOCK();
731 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
732 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
733 kauth_identity_lru(kip
);
738 KAUTH_IDENTITY_UNLOCK();
739 return((kip
== NULL
) ? ENOENT
: 0);
744 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
745 * association exists for the GID.
748 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
750 struct kauth_identity
*kip
;
752 KAUTH_IDENTITY_LOCK();
753 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
754 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
755 kauth_identity_lru(kip
);
760 KAUTH_IDENTITY_UNLOCK();
761 return((kip
== NULL
) ? ENOENT
: 0);
766 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
767 * association exists for the GUID. Note that the association may be expired,
768 * in which case the caller may elect to call out to userland to revalidate.
771 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
773 struct kauth_identity
*kip
;
775 KAUTH_IDENTITY_LOCK();
776 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
777 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
778 kauth_identity_lru(kip
);
783 KAUTH_IDENTITY_UNLOCK();
784 return((kip
== NULL
) ? ENOENT
: 0);
788 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
789 * association exists for the SID. Note that the association may be expired,
790 * in which case the caller may elect to call out to userland to revalidate.
793 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
795 struct kauth_identity
*kip
;
797 KAUTH_IDENTITY_LOCK();
798 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
799 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
800 kauth_identity_lru(kip
);
805 KAUTH_IDENTITY_UNLOCK();
806 return((kip
== NULL
) ? ENOENT
: 0);
812 guid_t kauth_null_guid
;
815 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
817 return(!bcmp(guid1
, guid2
, sizeof(*guid1
)));
821 * Look for well-known GUIDs.
824 kauth_wellknown_guid(guid_t
*guid
)
826 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
829 * All WKGs begin with the same 12 bytes.
831 if (!bcmp((void *)guid
, fingerprint
, 12)) {
833 * The final 4 bytes are our code.
835 code
= *(u_int32_t
*)&guid
->g_guid
[12];
838 return(KAUTH_WKG_EVERYBODY
);
840 return(KAUTH_WKG_NOBODY
);
842 return(KAUTH_WKG_OWNER
);
844 return(KAUTH_WKG_GROUP
);
847 return(KAUTH_WKG_NOT
);
852 * NT Security Identifier handling.
855 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
857 /* check sizes for equality, also sanity-check size while we're at it */
858 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
859 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
860 !bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)))
868 * We support four tokens representing identity:
869 * - Credential reference
872 * - NT security identifier
874 * Of these, the UID is the ubiquitous identifier; cross-referencing should
878 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
881 * Fetch UID from credential.
884 kauth_cred_getuid(kauth_cred_t cred
)
886 NULLCRED_CHECK(cred
);
887 return(cred
->cr_uid
);
891 * Fetch GID from credential.
894 kauth_cred_getgid(kauth_cred_t cred
)
896 NULLCRED_CHECK(cred
);
897 return(cred
->cr_gid
);
901 * Fetch UID from GUID.
904 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
906 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
910 * Fetch GID from GUID.
913 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
915 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
919 * Fetch UID from NT SID.
922 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
924 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
928 * Fetch GID from NT SID.
931 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
933 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
937 * Fetch GUID from NT SID.
940 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
942 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
946 * Fetch GUID from UID.
949 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
951 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
955 * Fetch user GUID from credential.
958 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
960 NULLCRED_CHECK(cred
);
961 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
965 * Fetch GUID from GID.
968 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
970 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
974 * Fetch NT SID from UID.
977 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
979 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
983 * Fetch NT SID from credential.
986 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
988 NULLCRED_CHECK(cred
);
989 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
993 * Fetch NT SID from GID.
996 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
998 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1002 * Fetch NT SID from GUID.
1005 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1007 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1013 * Lookup a translation in the cache.
1016 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1018 struct kauth_identity ki
;
1019 struct kauth_identity_extlookup el
;
1021 int (* expired
)(struct kauth_identity
*kip
);
1023 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1026 * Look for an existing cache entry for this association.
1027 * If the entry has not expired, return the cached information.
1032 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1035 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1038 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1040 case KI_VALID_NTSID
:
1041 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1046 /* lookup failure or error */
1048 /* any other error is fatal */
1049 if (error
!= ENOENT
) {
1050 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1054 /* do we have a translation? */
1055 if (ki
.ki_valid
& to
) {
1056 /* found a valid cached entry, check expiry */
1059 expired
= kauth_identity_guid_expired
;
1061 case KI_VALID_NTSID
:
1062 expired
= kauth_identity_ntsid_expired
;
1067 expired
= kauth_identity_guid_expired
;
1069 case KI_VALID_NTSID
:
1070 expired
= kauth_identity_ntsid_expired
;
1076 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1078 * If no expiry function, or not expired, we have found
1082 KAUTH_DEBUG("CACHE - no expiry function");
1085 if (!expired(&ki
)) {
1086 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1090 * We leave ki_valid set here; it contains a translation but the TTL has
1091 * expired. If we can't get a result from the resolver, we will
1092 * use it as a better-than nothing alternative.
1094 KAUTH_DEBUG("CACHE - expired entry found");
1099 * Call the resolver. We ask for as much data as we can get.
1103 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1104 el
.el_uid
= *(uid_t
*)src
;
1107 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1108 el
.el_gid
= *(gid_t
*)src
;
1111 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1112 el
.el_uguid
= *(guid_t
*)src
;
1113 el
.el_gguid
= *(guid_t
*)src
;
1115 case KI_VALID_NTSID
:
1116 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1117 el
.el_usid
= *(ntsid_t
*)src
;
1118 el
.el_gsid
= *(ntsid_t
*)src
;
1124 * Here we ask for everything all at once, to avoid having to work
1125 * out what we really want now, or might want soon.
1127 * Asking for SID translations when we don't know we need them right
1128 * now is going to cause excess work to be done if we're connected
1129 * to a network that thinks it can translate them. This list needs
1130 * to get smaller/smarter.
1132 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1133 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1134 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1135 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1136 error
= kauth_identity_resolve(&el
);
1137 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1138 /* was the lookup successful? */
1141 * Save the results from the lookup - may have other information even if we didn't
1144 kauth_identity_updatecache(&el
, &ki
);
1147 * Check to see if we have a valid result.
1149 if (!error
&& !(ki
.ki_valid
& to
))
1156 *(uid_t
*)dst
= ki
.ki_uid
;
1159 *(gid_t
*)dst
= ki
.ki_gid
;
1162 *(guid_t
*)dst
= ki
.ki_guid
;
1164 case KI_VALID_NTSID
:
1165 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1170 KAUTH_DEBUG("CACHE - returned successfully");
1176 * Group membership cache.
1178 * XXX the linked-list implementation here needs to be optimized.
1181 struct kauth_group_membership
{
1182 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1183 uid_t gm_uid
; /* the identity whose membership we're recording */
1184 gid_t gm_gid
; /* group of which they are a member */
1185 time_t gm_expiry
; /* TTL for the membership */
1187 #define KAUTH_GROUP_ISMEMBER (1<<0)
1190 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1191 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1192 static int kauth_groups_count
;
1194 static lck_mtx_t
*kauth_groups_mtx
;
1195 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1196 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1198 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1199 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1200 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1203 kauth_groups_init(void)
1205 TAILQ_INIT(&kauth_groups
);
1206 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1210 kauth_groups_expired(struct kauth_group_membership
*gm
)
1215 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1219 kauth_groups_lru(struct kauth_group_membership
*gm
)
1221 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1222 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1223 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1228 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1230 struct kauth_group_membership
*gm
;
1233 /* need a valid response if we are to cache anything */
1235 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1236 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1241 /* search for an existing record for this association before inserting */
1242 KAUTH_GROUPS_LOCK();
1243 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1244 if ((el
->el_uid
== gm
->gm_uid
) &&
1245 (el
->el_gid
== gm
->gm_gid
)) {
1246 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1247 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1249 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1251 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1252 kauth_groups_lru(gm
);
1256 KAUTH_GROUPS_UNLOCK();
1258 /* if we found an entry to update, stop here */
1262 /* allocate a new record */
1263 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
1265 gm
->gm_uid
= el
->el_uid
;
1266 gm
->gm_gid
= el
->el_gid
;
1267 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1268 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1270 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1272 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1276 * Insert the new entry. Note that it's possible to race ourselves here
1277 * and end up with duplicate entries in the list. Wasteful, but harmless
1278 * since the first into the list will never be looked up, and thus will
1279 * eventually just fall off the end.
1281 KAUTH_GROUPS_LOCK();
1282 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1283 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
1284 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
1285 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1286 kauth_groups_count
--;
1290 KAUTH_GROUPS_UNLOCK();
1292 /* free expired cache entry */
1298 * Group membership KPI
1301 * This function guarantees not to modify resultp when returning an error.
1304 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
1306 struct kauth_group_membership
*gm
;
1307 struct kauth_identity_extlookup el
;
1311 * Check the per-credential list of override groups.
1313 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1314 * the cache should be used for that case.
1316 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
1317 if (gid
== cred
->cr_groups
[i
]) {
1324 * If we don't have a UID for group membership checks, the in-cred list
1325 * was authoritative and we can stop here.
1327 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
1334 * If the resolver hasn't checked in yet, we are early in the boot phase and
1335 * the local group list is complete and authoritative.
1337 if (!kauth_resolver_registered
) {
1343 /* XXX check supplementary groups */
1344 /* XXX check whiteout groups */
1345 /* XXX nesting of supplementary/whiteout groups? */
1348 * Check the group cache.
1350 KAUTH_GROUPS_LOCK();
1351 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1352 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
1353 kauth_groups_lru(gm
);
1358 /* did we find a membership entry? */
1360 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
1361 KAUTH_GROUPS_UNLOCK();
1363 /* if we did, we can return now */
1367 /* nothing in the cache, need to go to userland */
1368 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
1369 el
.el_uid
= cred
->cr_gmuid
;
1371 error
= kauth_identity_resolve(&el
);
1374 /* save the results from the lookup */
1375 kauth_groups_updatecache(&el
);
1377 /* if we successfully ascertained membership, report */
1378 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
1379 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
1387 * Determine whether the supplied credential is a member of the
1388 * group nominated by GUID.
1391 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
1397 wkg
= kauth_wellknown_guid(guidp
);
1399 case KAUTH_WKG_NOBODY
:
1402 case KAUTH_WKG_EVERYBODY
:
1406 /* translate guid to gid */
1407 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
1409 * If we have no guid -> gid translation, it's not a group and
1410 * thus the cred can't be a member.
1412 if (error
== ENOENT
) {
1417 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
1424 * Fast replacement for issuser()
1427 kauth_cred_issuser(kauth_cred_t cred
)
1429 return(cred
->cr_uid
== 0);
1436 /* lock protecting credential hash table */
1437 static lck_mtx_t
*kauth_cred_hash_mtx
;
1438 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1439 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1442 kauth_cred_init(void)
1446 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1447 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
1449 /*allocate credential hash table */
1450 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
1451 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
1452 M_KAUTH
, M_WAITOK
| M_ZERO
);
1453 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
1454 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
1459 * Return the current thread's effective UID.
1464 return(kauth_cred_get()->cr_uid
);
1468 * Return the current thread's real UID.
1473 return(kauth_cred_get()->cr_ruid
);
1477 * Return the current thread's effective GID.
1482 return(kauth_cred_get()->cr_groups
[0]);
1486 * Return the current thread's real GID.
1491 return(kauth_cred_get()->cr_rgid
);
1495 * Returns a pointer to the current thread's credential, does not take a
1496 * reference (so the caller must not do anything that would let the thread's
1497 * credential change while using the returned value).
1500 kauth_cred_get(void)
1503 struct uthread
*uthread
;
1505 uthread
= get_bsdthread_info(current_thread());
1507 if (uthread
== NULL
)
1508 panic("thread wants credential but has no BSD thread info");
1510 * We can lazy-bind credentials to threads, as long as their processes have them.
1511 * If we later inline this function, the code in this block should probably be
1512 * called out in a function.
1514 if (uthread
->uu_ucred
== NOCRED
) {
1515 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1516 panic("thread wants credential but has no BSD process");
1518 kauth_cred_ref(uthread
->uu_ucred
= p
->p_ucred
);
1521 return(uthread
->uu_ucred
);
1525 * Returns a pointer to the current thread's credential, takes a reference.
1528 kauth_cred_get_with_ref(void)
1531 struct uthread
*uthread
;
1533 uthread
= get_bsdthread_info(current_thread());
1535 if (uthread
== NULL
)
1536 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
1537 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
1538 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
1541 * We can lazy-bind credentials to threads, as long as their processes have them.
1542 * If we later inline this function, the code in this block should probably be
1543 * called out in a function.
1546 if (uthread
->uu_ucred
== NOCRED
) {
1547 /* take reference for new cred in thread */
1548 kauth_cred_ref(uthread
->uu_ucred
= proc_ucred(procp
));
1550 /* take a reference for our caller */
1551 kauth_cred_ref(uthread
->uu_ucred
);
1553 return(uthread
->uu_ucred
);
1557 * Returns a pointer to the given process's credential, takes a reference.
1560 kauth_cred_proc_ref(proc_t procp
)
1565 cred
= proc_ucred(procp
);
1566 kauth_cred_ref(cred
);
1572 * Allocates a new credential.
1575 kauth_cred_alloc(void)
1577 kauth_cred_t newcred
;
1579 MALLOC(newcred
, kauth_cred_t
, sizeof(*newcred
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1581 newcred
->cr_ref
= 1;
1582 /* must do this, or cred has same group membership as uid 0 */
1583 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
1586 panic("kauth_cred_alloc: couldn't allocate credential");
1590 #if KAUTH_CRED_HASH_DEBUG
1598 * Looks to see if we already have a known credential and if found bumps the
1599 * reference count and returns it. If there are no credentials that match
1600 * the given credential then we allocate a new credential.
1602 * Note that the gmuid is hard-defaulted to the UID specified. Since we maintain
1603 * this field, we can't expect callers to know how it needs to be set. Callers
1604 * should be prepared for this field to be overwritten.
1607 kauth_cred_create(kauth_cred_t cred
)
1609 kauth_cred_t found_cred
, new_cred
= NULL
;
1611 cred
->cr_gmuid
= cred
->cr_uid
;
1614 KAUTH_CRED_HASH_LOCK();
1615 found_cred
= kauth_cred_find(cred
);
1616 if (found_cred
!= NULL
) {
1617 /* found an existing credential so we'll bump reference count and return */
1618 kauth_cred_ref(found_cred
);
1619 KAUTH_CRED_HASH_UNLOCK();
1622 KAUTH_CRED_HASH_UNLOCK();
1624 /* no existing credential found. create one and add it to our hash table */
1625 new_cred
= kauth_cred_alloc();
1626 if (new_cred
!= NULL
) {
1628 new_cred
->cr_uid
= cred
->cr_uid
;
1629 new_cred
->cr_ruid
= cred
->cr_ruid
;
1630 new_cred
->cr_svuid
= cred
->cr_svuid
;
1631 new_cred
->cr_rgid
= cred
->cr_rgid
;
1632 new_cred
->cr_svgid
= cred
->cr_svgid
;
1633 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
1634 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
1635 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
1636 KAUTH_CRED_HASH_LOCK();
1637 err
= kauth_cred_add(new_cred
);
1638 KAUTH_CRED_HASH_UNLOCK();
1640 /* retry if kauth_cred_add returns non zero value */
1643 FREE(new_cred
, M_KAUTH
);
1652 * Update the given credential using the uid argument. The given uid is used
1653 * set the effective user ID, real user ID, and saved user ID. We only
1654 * allocate a new credential when the given uid actually results in changes to
1655 * the existing credential.
1658 kauth_cred_setuid(kauth_cred_t cred
, uid_t uid
)
1660 struct ucred temp_cred
;
1662 NULLCRED_CHECK(cred
);
1664 /* don't need to do anything if the effective, real and saved user IDs are
1665 * already the same as the user ID passed in
1667 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
) {
1668 /* no change needed */
1672 /* look up in cred hash table to see if we have a matching credential
1675 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1676 temp_cred
.cr_uid
= uid
;
1677 temp_cred
.cr_ruid
= uid
;
1678 temp_cred
.cr_svuid
= uid
;
1679 temp_cred
.cr_gmuid
= uid
;
1681 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1685 * Update the given credential using the euid argument. The given uid is used
1686 * set the effective user ID. We only allocate a new credential when the given
1687 * uid actually results in changes to the existing credential.
1690 kauth_cred_seteuid(kauth_cred_t cred
, uid_t euid
)
1692 struct ucred temp_cred
;
1694 NULLCRED_CHECK(cred
);
1696 /* don't need to do anything if the given effective user ID is already the
1697 * same as the effective user ID in the credential.
1699 if (cred
->cr_uid
== euid
) {
1700 /* no change needed */
1704 /* look up in cred hash table to see if we have a matching credential
1707 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1708 temp_cred
.cr_uid
= euid
;
1710 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1714 * Update the given credential using the gid argument. The given gid is used
1715 * set the effective group ID, real group ID, and saved group ID. We only
1716 * allocate a new credential when the given gid actually results in changes to
1717 * the existing credential.
1720 kauth_cred_setgid(kauth_cred_t cred
, gid_t gid
)
1722 struct ucred temp_cred
;
1724 NULLCRED_CHECK(cred
);
1726 /* don't need to do anything if the given group ID is already the
1727 * same as the group ID in the credential.
1729 if (cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1730 /* no change needed */
1734 /* look up in cred hash table to see if we have a matching credential
1737 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1738 temp_cred
.cr_groups
[0] = gid
;
1739 temp_cred
.cr_rgid
= gid
;
1740 temp_cred
.cr_svgid
= gid
;
1742 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1746 * Update the given credential using the egid argument. The given gid is used
1747 * set the effective user ID. We only allocate a new credential when the given
1748 * gid actually results in changes to the existing credential.
1751 kauth_cred_setegid(kauth_cred_t cred
, gid_t egid
)
1753 struct ucred temp_cred
;
1755 NULLCRED_CHECK(cred
);
1757 /* don't need to do anything if the given group ID is already the
1758 * same as the group Id in the credential.
1760 if (cred
->cr_groups
[0] == egid
) {
1761 /* no change needed */
1765 /* look up in cred hash table to see if we have a matching credential
1768 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1769 temp_cred
.cr_groups
[0] = egid
;
1771 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1775 * Update the given credential with the given groups. We only allocate a new
1776 * credential when the given gid actually results in changes to the existing
1778 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1779 * which will be used for group membership checking.
1782 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
1785 struct ucred temp_cred
;
1787 NULLCRED_CHECK(cred
);
1789 /* don't need to do anything if the given list of groups does not change.
1791 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
1792 for (i
= 0; i
< groupcount
; i
++) {
1793 if (cred
->cr_groups
[i
] != groups
[i
])
1796 if (i
== groupcount
) {
1797 /* no change needed */
1802 /* look up in cred hash table to see if we have a matching credential
1805 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1806 temp_cred
.cr_ngroups
= groupcount
;
1807 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
1808 temp_cred
.cr_gmuid
= gmuid
;
1810 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1814 * Update the given credential using the uid and gid arguments. The given uid
1815 * is used set the effective user ID, real user ID, and saved user ID.
1816 * The given gid is used set the effective group ID, real group ID, and saved
1818 * We only allocate a new credential when the given uid and gid actually results
1819 * in changes to the existing credential.
1822 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1824 struct ucred temp_cred
;
1826 NULLCRED_CHECK(cred
);
1828 /* don't need to do anything if the effective, real and saved user IDs are
1829 * already the same as the user ID passed in
1831 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
1832 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
1833 /* no change needed */
1837 /* look up in cred hash table to see if we have a matching credential
1840 bzero(&temp_cred
, sizeof(temp_cred
));
1841 temp_cred
.cr_uid
= uid
;
1842 temp_cred
.cr_ruid
= uid
;
1843 temp_cred
.cr_svuid
= uid
;
1844 temp_cred
.cr_gmuid
= uid
;
1845 temp_cred
.cr_ngroups
= 1;
1846 temp_cred
.cr_groups
[0] = gid
;
1847 temp_cred
.cr_rgid
= gid
;
1848 temp_cred
.cr_svgid
= gid
;
1850 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1854 * Update the given credential using the uid and gid arguments. The given uid
1855 * is used to set the saved user ID. The given gid is used to set the
1857 * We only allocate a new credential when the given uid and gid actually results
1858 * in changes to the existing credential.
1861 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
1863 struct ucred temp_cred
;
1865 NULLCRED_CHECK(cred
);
1867 /* don't need to do anything if the effective, real and saved user IDs are
1868 * already the same as the user ID passed in
1870 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
1871 /* no change needed */
1875 /* look up in cred hash table to see if we have a matching credential
1878 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1879 temp_cred
.cr_svuid
= uid
;
1880 temp_cred
.cr_svgid
= gid
;
1882 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
1886 * Update the given credential using the given auditinfo_t.
1887 * We only allocate a new credential when the given auditinfo_t actually results
1888 * in changes to the existing credential.
1891 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
1893 struct ucred temp_cred
;
1895 NULLCRED_CHECK(cred
);
1897 /* don't need to do anything if the audit info is already the same as the
1898 * audit info in the credential passed in
1900 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
1901 /* no change needed */
1905 /* look up in cred hash table to see if we have a matching credential
1908 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
1909 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
1911 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
1915 * Add a reference to the passed credential.
1918 kauth_cred_ref(kauth_cred_t cred
)
1922 NULLCRED_CHECK(cred
);
1924 old_value
= OSAddAtomic(1, &cred
->cr_ref
);
1927 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1933 * Drop a reference from the passed credential, potentially destroying it.
1936 kauth_cred_rele(kauth_cred_t cred
)
1940 NULLCRED_CHECK(cred
);
1942 KAUTH_CRED_HASH_LOCK();
1943 old_value
= OSAddAtomic(-1, &cred
->cr_ref
);
1947 panic("kauth_cred_rele: dropping a reference on a cred with no references");
1950 if (old_value
< 3) {
1951 /* the last reference is our credential hash table */
1952 kauth_cred_remove(cred
);
1954 KAUTH_CRED_HASH_UNLOCK();
1958 * Duplicate a credential.
1959 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1962 kauth_cred_dup(kauth_cred_t cred
)
1964 kauth_cred_t newcred
;
1967 if (cred
== NOCRED
|| cred
== FSCRED
)
1968 panic("kauth_cred_dup: bad credential");
1970 newcred
= kauth_cred_alloc();
1971 if (newcred
!= NULL
) {
1972 bcopy(cred
, newcred
, sizeof(*newcred
));
1973 newcred
->cr_ref
= 1;
1979 * Returns a credential based on the passed credential but which
1980 * reflects the real rather than effective UID and GID.
1981 * NOTE - we do NOT decrement cred reference count on passed in credential
1984 kauth_cred_copy_real(kauth_cred_t cred
)
1986 kauth_cred_t newcred
= NULL
, found_cred
;
1987 struct ucred temp_cred
;
1989 /* if the credential is already 'real', just take a reference */
1990 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
1991 (cred
->cr_rgid
== cred
->cr_gid
)) {
1992 kauth_cred_ref(cred
);
1996 /* look up in cred hash table to see if we have a matching credential
1999 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2000 temp_cred
.cr_uid
= cred
->cr_ruid
;
2001 temp_cred
.cr_groups
[0] = cred
->cr_rgid
;
2002 /* if the cred is not opted out, make sure we are using the r/euid for group checks */
2003 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
2004 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
2009 KAUTH_CRED_HASH_LOCK();
2010 found_cred
= kauth_cred_find(&temp_cred
);
2011 if (found_cred
== cred
) {
2012 /* same cred so just bail */
2013 KAUTH_CRED_HASH_UNLOCK();
2016 if (found_cred
!= NULL
) {
2017 /* found a match so we bump reference count on new one and decrement
2018 * reference count on the old one.
2020 kauth_cred_ref(found_cred
);
2021 KAUTH_CRED_HASH_UNLOCK();
2025 /* must allocate a new credential, copy in old credential data and update
2026 * with real user and group IDs.
2028 newcred
= kauth_cred_dup(&temp_cred
);
2029 err
= kauth_cred_add(newcred
);
2030 KAUTH_CRED_HASH_UNLOCK();
2032 /* retry if kauth_cred_add returns non zero value */
2035 FREE(newcred
, M_KAUTH
);
2043 * common code to update a credential. model_cred is a temporary, non reference
2044 * counted credential used only for comparison and modeling purposes. old_cred
2045 * is a live reference counted credential that we intend to update using model_cred
2048 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
, boolean_t retain_auditinfo
)
2050 kauth_cred_t found_cred
, new_cred
= NULL
;
2052 /* make sure we carry the auditinfo forward to the new credential unless
2053 * we are actually updating the auditinfo.
2055 if (retain_auditinfo
)
2056 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
2061 KAUTH_CRED_HASH_LOCK();
2062 found_cred
= kauth_cred_find(model_cred
);
2063 if (found_cred
== old_cred
) {
2064 /* same cred so just bail */
2065 KAUTH_CRED_HASH_UNLOCK();
2068 if (found_cred
!= NULL
) {
2069 /* found a match so we bump reference count on new one and decrement
2070 * reference count on the old one.
2072 kauth_cred_ref(found_cred
);
2073 KAUTH_CRED_HASH_UNLOCK();
2074 kauth_cred_rele(old_cred
);
2078 /* must allocate a new credential using the model. also
2079 * adds the new credential to the credential hash table.
2081 new_cred
= kauth_cred_dup(model_cred
);
2082 err
= kauth_cred_add(new_cred
);
2083 KAUTH_CRED_HASH_UNLOCK();
2085 /* retry if kauth_cred_add returns non zero value */
2088 FREE(new_cred
, M_KAUTH
);
2092 kauth_cred_rele(old_cred
);
2097 * Add the given credential to our credential hash table and take an additional
2098 * reference to account for our use of the credential in the hash table.
2099 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2101 static int kauth_cred_add(kauth_cred_t new_cred
)
2105 hash_key
= kauth_cred_get_hashkey(new_cred
);
2106 hash_key
%= kauth_cred_table_size
;
2108 /* race fix - there is a window where another matching credential
2109 * could have been inserted between the time this one was created and we
2110 * got the hash lock. If we find a match return an error and have the
2113 if (kauth_cred_find(new_cred
) != NULL
) {
2117 /* take a reference for our use in credential hash table */
2118 kauth_cred_ref(new_cred
);
2120 /* insert the credential into the hash table */
2121 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
2127 * Remove the given credential from our credential hash table.
2128 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2130 static void kauth_cred_remove(kauth_cred_t cred
)
2133 kauth_cred_t found_cred
;
2135 hash_key
= kauth_cred_get_hashkey(cred
);
2136 hash_key
%= kauth_cred_table_size
;
2139 if (cred
->cr_ref
< 1)
2140 panic("cred reference underflow");
2141 if (cred
->cr_ref
> 1)
2142 return; /* someone else got a ref */
2144 /* find cred in the credential hash table */
2145 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2146 if (found_cred
== cred
) {
2147 /* found a match, remove it from the hash table */
2148 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
2149 FREE(cred
, M_KAUTH
);
2150 #if KAUTH_CRED_HASH_DEBUG
2157 /* did not find a match. this should not happen! */
2158 printf("%s - %d - %s - did not find a match \n", __FILE__
, __LINE__
, __FUNCTION__
);
2163 * Using the given credential data, look for a match in our credential hash
2165 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2167 kauth_cred_t
kauth_cred_find(kauth_cred_t cred
)
2170 kauth_cred_t found_cred
;
2172 #if KAUTH_CRED_HASH_DEBUG
2173 static int test_count
= 0;
2176 if ((test_count
% 200) == 0) {
2177 kauth_cred_hash_print();
2181 hash_key
= kauth_cred_get_hashkey(cred
);
2182 hash_key
%= kauth_cred_table_size
;
2184 /* find cred in the credential hash table */
2185 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
2186 if (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
, (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
))) == 0) {
2191 /* no match found */
2196 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2198 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
)
2200 u_long hash_key
= 0;
2202 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
2203 (sizeof(struct ucred
) - offsetof(struct ucred
, cr_uid
)),
2209 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2211 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
2213 u_long hash_key
= start_key
;
2216 while (data_len
> 0) {
2217 hash_key
= (hash_key
<< 4) + *datap
++;
2218 temp
= hash_key
& 0xF0000000;
2220 hash_key
^= temp
>> 24;
2228 #if KAUTH_CRED_HASH_DEBUG
2229 static void kauth_cred_hash_print(void)
2232 kauth_cred_t found_cred
;
2234 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
2235 /* count slot hits, misses, collisions, and max depth */
2236 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2237 printf("[%02d] ", i
);
2239 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
2244 kauth_cred_print(found_cred
);
2248 printf("NOCRED \n");
2254 static void kauth_cred_print(kauth_cred_t cred
)
2258 printf("0x%02X - refs %d uids %d %d %d ", cred
, cred
->cr_ref
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
);
2259 printf("group count %d gids ", cred
->cr_ngroups
);
2260 for (i
= 0; i
< NGROUPS
; i
++) {
2261 printf("%d ", cred
->cr_groups
[i
]);
2263 printf("%d %d %d ", cred
->cr_rgid
, cred
->cr_svgid
, cred
->cr_gmuid
);
2264 printf("auditinfo %d %d %d %d %d %d ",
2265 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
2266 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);