2 * Copyright (c) 2004-2007 Apple 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@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
36 * Kernel Authorization framework: Management of process/thread credentials
37 * and identity information.
41 #include <sys/param.h> /* XXX trim includes */
43 #include <sys/systm.h>
44 #include <sys/ucred.h>
45 #include <sys/proc_internal.h>
47 #include <sys/timeb.h>
48 #include <sys/times.h>
49 #include <sys/malloc.h>
50 #include <sys/kauth.h>
51 #include <sys/kernel.h>
53 #include <bsm/audit_kernel.h>
55 #include <sys/mount.h>
56 #include <sys/sysproto.h>
57 #include <mach/message.h>
58 #include <mach/host_security.h>
60 #include <libkern/OSAtomic.h>
62 #include <kern/task.h>
63 #include <kern/lock.h>
67 #define MACH_ASSERT 1 /* XXX so bogus */
68 #include <kern/assert.h>
71 #include <security/mac.h>
72 #include <security/mac_framework.h>
75 #define CRED_DIAGNOSTIC 0
77 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
80 * Credential debugging; we can track entry into a function that might
81 * change a credential, and we can track actual credential changes that
84 * Note: Does *NOT* currently include per-thread credential changes
88 #define DEBUG_CRED_ENTER printf
89 #define DEBUG_CRED_CHANGE printf
90 extern void kauth_cred_print(kauth_cred_t cred
);
92 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
94 int is_target_cred( kauth_cred_t the_cred
);
95 void get_backtrace( void );
97 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
98 __unused
int arg2
, struct sysctl_req
*req
);
100 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
101 __unused
int arg2
, struct sysctl_req
*req
);
103 #define MAX_STACK_DEPTH 8
104 struct cred_backtrace
{
106 void * stack
[ MAX_STACK_DEPTH
];
108 typedef struct cred_backtrace cred_backtrace
;
110 #define MAX_CRED_BUFFER_SLOTS 200
111 struct cred_debug_buffer
{
113 cred_backtrace stack_buffer
[ MAX_CRED_BUFFER_SLOTS
];
115 typedef struct cred_debug_buffer cred_debug_buffer
;
116 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
118 #else /* !DEBUG_CRED */
120 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
121 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
123 #endif /* !DEBUG_CRED */
126 * Interface to external identity resolver.
128 * The architecture of the interface is simple; the external resolver calls
129 * in to get work, then calls back with completed work. It also calls us
130 * to let us know that it's (re)started, so that we can resubmit work if it
134 static lck_mtx_t
*kauth_resolver_mtx
;
135 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
136 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
138 static volatile pid_t kauth_resolver_identity
;
139 static int kauth_resolver_registered
;
140 static uint32_t kauth_resolver_sequence
;
142 struct kauth_resolver_work
{
143 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
144 struct kauth_identity_extlookup kr_work
;
148 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
149 #define KAUTH_REQUEST_SUBMITTED (1<<1)
150 #define KAUTH_REQUEST_DONE (1<<2)
154 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
155 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
156 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
158 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
159 static int kauth_resolver_complete(user_addr_t message
);
160 static int kauth_resolver_getwork(user_addr_t message
);
161 static int kauth_resolver_getwork2(user_addr_t message
);
163 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
164 static int kauth_cred_primes_index
= 0;
165 static int kauth_cred_table_size
= 0;
167 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
168 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
170 #define KAUTH_CRED_HASH_DEBUG 0
172 static int kauth_cred_add(kauth_cred_t new_cred
);
173 static void kauth_cred_remove(kauth_cred_t cred
);
174 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
175 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
176 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
177 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
179 #if KAUTH_CRED_HASH_DEBUG
180 static int kauth_cred_count
= 0;
181 static void kauth_cred_hash_print(void);
182 static void kauth_cred_print(kauth_cred_t cred
);
187 * kauth_resolver_init
189 * Description: Initialize the daemon side of the credential identity resolver
195 * Notes: Intialize the credential identity resolver for use; the
196 * credential identity resolver is the KPI used by the user
197 * space credential identity resolver daemon to communicate
198 * with the kernel via the identitysvc() system call..
200 * This is how membership in more than 16 groups (1 effective
201 * and 15 supplementary) is supported, and also how UID's,
202 * UUID's, and so on, are translated to/from POSIX credential
205 * The credential identity resolver operates by attempting to
206 * determine identity first from the credential, then from
207 * the kernel credential identity cache, and finally by
208 * enqueueing a request to a user space daemon.
210 * This function is called from kauth_init() in the file
211 * kern_authorization.c.
214 kauth_resolver_init(void)
216 TAILQ_INIT(&kauth_resolver_unsubmitted
);
217 TAILQ_INIT(&kauth_resolver_submitted
);
218 TAILQ_INIT(&kauth_resolver_done
);
219 kauth_resolver_sequence
= 31337;
220 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
225 * kauth_resolver_submit
227 * Description: Submit an external credential identity resolution request to
228 * the user space daemon.
230 * Parameters: lkp A pointer to an external
234 * EWOULDBLOCK No resolver registered
235 * EINTR Operation interrupted (e.g. by
237 * ENOMEM Could not allocate work item
238 * ??? An error from the user space
241 * Notes: Allocate a work queue entry, submit the work and wait for
242 * the operation to either complete or time out. Outstanding
243 * operations may also be cancelled.
246 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
248 struct kauth_resolver_work
*workp
, *killp
;
250 int error
, shouldfree
;
252 /* no point actually blocking if the resolver isn't up yet */
253 if (kauth_resolver_identity
== 0) {
255 * We've already waited an initial 30 seconds with no result.
256 * Sleep on a stack address so no one wakes us before timeout;
257 * we sleep a half a second in case we are a high priority
258 * process, so that memberd doesn't starve while we are in a
259 * tight loop between user and kernel, eating all the CPU.
261 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
262 if (kauth_resolver_identity
== 0) {
264 * if things haven't changed while we were asleep,
265 * tell the caller we couldn't get an authoritative
272 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
276 workp
->kr_work
= *lkp
;
278 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
279 workp
->kr_result
= 0;
282 * We insert the request onto the unsubmitted queue, the call in from
283 * the resolver will it to the submitted thread when appropriate.
285 KAUTH_RESOLVER_LOCK();
286 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
287 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
290 * XXX As an optimisation, we could check the queue for identical
291 * XXX items and coalesce them
293 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
295 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
297 /* we could compute a better timeout here */
300 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
301 /* request has been completed? */
302 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
304 /* woken because the resolver has died? */
305 if (kauth_resolver_identity
== 0) {
313 /* if the request was processed, copy the result */
315 *lkp
= workp
->kr_work
;
318 * If the request timed out and was never collected, the resolver
319 * is dead and probably not coming back anytime soon. In this
320 * case we revert to no-resolver behaviour, and punt all the other
321 * sleeping requests to clear the backlog.
323 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
324 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
325 kauth_resolver_identity
= 0;
326 /* kill all the other requestes that are waiting as well */
327 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
329 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
334 * drop our reference on the work item, and note whether we should
337 if (--workp
->kr_refs
<= 0) {
338 /* work out which list we have to remove it from */
339 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
340 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
341 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
342 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
343 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
344 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
346 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
350 /* someone else still has a reference on this request */
353 /* collect request result */
355 error
= workp
->kr_result
;
356 KAUTH_RESOLVER_UNLOCK();
358 * If we dropped the last reference, free the request.
361 FREE(workp
, M_KAUTH
);
363 KAUTH_DEBUG("RESOLVER - returning %d", error
);
371 * Description: System call interface for the external identity resolver.
373 * Parameters: uap->message Message from daemon to kernel
375 * Returns: 0 Successfully became resolver
376 * EPERM Not the resolver process
377 * kauth_authorize_generic:EPERM Not root user
378 * kauth_resolver_complete:EIO
379 * kauth_resolver_complete:EFAULT
380 * kauth_resolver_getwork:EINTR
381 * kauth_resolver_getwork:EFAULT
383 * Notes: This system call blocks until there is work enqueued, at
384 * which time the kernel wakes it up, and a message from the
385 * kernel is copied out to the identity resolution daemon, which
386 * proceed to attempt to resolve it. When the resolution has
387 * completed (successfully or not), the daemon called back into
388 * this system call to give the result to the kernel, and wait
389 * for the next request.
392 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused register_t
*retval
)
394 int opcode
= uap
->opcode
;
395 user_addr_t message
= uap
->message
;
396 struct kauth_resolver_work
*workp
;
401 * New server registering itself.
403 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
404 new_id
= current_proc()->p_pid
;
405 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
406 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
409 KAUTH_RESOLVER_LOCK();
410 if (kauth_resolver_identity
!= new_id
) {
411 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
413 * We have a new server, so assume that all the old requests have been lost.
415 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
416 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
417 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
418 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
419 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
421 kauth_resolver_identity
= new_id
;
422 kauth_resolver_registered
= 1;
423 wakeup(&kauth_resolver_unsubmitted
);
425 KAUTH_RESOLVER_UNLOCK();
430 * Beyond this point, we must be the resolver process.
432 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
433 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
438 * Got a result returning?
440 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
441 if ((error
= kauth_resolver_complete(message
)) != 0)
446 * Caller wants to take more work?
448 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
449 if ((error
= kauth_resolver_getwork(message
)) != 0)
458 * kauth_resolver_getwork_continue
460 * Description: Continuation for kauth_resolver_getwork
462 * Parameters: result Error code or 0 for the sleep
463 * that got us to this function
466 * EINTR Interrupted (e.g. by signal)
467 * kauth_resolver_getwork2:EFAULT
469 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
473 kauth_resolver_getwork_continue(int result
)
480 KAUTH_RESOLVER_UNLOCK();
485 * If we lost a race with another thread/memberd restarting, then we
486 * need to go back to sleep to look for more work. If it was memberd
487 * restarting, then the msleep0() will error out here, as our thread
488 * will already be "dead".
490 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
493 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
494 KAUTH_RESOLVER_UNLOCK();
498 thread
= current_thread();
499 ut
= get_bsdthread_info(thread
);
500 message
= ut
->uu_kauth
.message
;
501 return(kauth_resolver_getwork2(message
));
506 * kauth_resolver_getwork2
508 * Decription: Common utility function to copy out a identity resolver work
509 * item from the kernel to user space as part of the user space
510 * identity resolver requesting work.
512 * Parameters: message message to user space
515 * EFAULT Bad user space message address
517 * Notes: This common function exists to permit the use of continuations
518 * in the identity resoultion process. This frees up the stack
519 * while we are waiting for the user space resolver to complete
520 * a request. This is specifically used so that our per thread
521 * cost can be small, and we will therefore be willing to run a
522 * larger number of threads in the user space identity resolver.
525 kauth_resolver_getwork2(user_addr_t message
)
527 struct kauth_resolver_work
*workp
;
531 * Note: We depend on the caller protecting us from a NULL work item
532 * queue, since we must have the kauth resolver lock on entry to this
535 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
537 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
538 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
541 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
542 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
543 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
544 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
547 KAUTH_RESOLVER_UNLOCK();
553 * kauth_resolver_getwork
555 * Description: Get a work item from the enqueued requests from the kernel and
556 * give it to the user space daemon.
558 * Parameters: message message to user space
561 * EINTR Interrupted (e.g. by signal)
562 * kauth_resolver_getwork2:EFAULT
564 * Notes: This function blocks in a continuation if there are no work
565 * items available for processing at the time the user space
566 * identity resolution daemon makes a request for work. This
567 * permits a large number of threads to be used by the daemon,
568 * without using a lot of wired kernel memory when there are no
569 * acctual request outstanding.
572 kauth_resolver_getwork(user_addr_t message
)
574 struct kauth_resolver_work
*workp
;
577 KAUTH_RESOLVER_LOCK();
579 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
580 thread_t thread
= current_thread();
581 struct uthread
*ut
= get_bsdthread_info(thread
);
583 ut
->uu_kauth
.message
= message
;
584 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
585 KAUTH_RESOLVER_UNLOCK();
588 return kauth_resolver_getwork2(message
);
593 * kauth_resolver_complete
595 * Description: Return a result from userspace.
597 * Parameters: message message from user space
600 * EIO The resolver is dead
601 * copyin:EFAULT Bad message from user space
604 kauth_resolver_complete(user_addr_t message
)
606 struct kauth_identity_extlookup extl
;
607 struct kauth_resolver_work
*workp
;
610 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
611 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
615 KAUTH_RESOLVER_LOCK();
619 switch (extl
.el_result
) {
620 case KAUTH_EXTLOOKUP_INPROG
:
624 /* XXX this should go away once memberd is updated */
626 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
631 case KAUTH_EXTLOOKUP_SUCCESS
:
634 case KAUTH_EXTLOOKUP_FATAL
:
635 /* fatal error means the resolver is dead */
636 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
637 kauth_resolver_identity
= 0;
638 /* XXX should we terminate all outstanding requests? */
641 case KAUTH_EXTLOOKUP_BADRQ
:
642 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
645 case KAUTH_EXTLOOKUP_FAILURE
:
646 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
650 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
656 * In the case of a fatal error, we assume that the resolver will restart
657 * quickly and re-collect all of the outstanding requests. Thus, we don't
658 * complete the request which returned the fatal error status.
660 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
661 /* scan our list for this request */
662 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
664 if (workp
->kr_seqno
== extl
.el_seqno
) {
666 workp
->kr_work
= extl
;
667 /* move onto completed list and wake up requester(s) */
668 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
669 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
670 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
671 workp
->kr_result
= result
;
672 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
679 * Note that it's OK for us not to find anything; if the request has
680 * timed out the work record will be gone.
682 KAUTH_RESOLVER_UNLOCK();
692 struct kauth_identity
{
693 TAILQ_ENTRY(kauth_identity
) ki_link
;
695 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
696 #define KI_VALID_GID (1<<1)
697 #define KI_VALID_GUID (1<<2)
698 #define KI_VALID_NTSID (1<<3)
704 * Expiry times are the earliest time at which we will disregard the cached state and go to
705 * userland. Before then if the valid bit is set, we will return the cached value. If it's
706 * not set, we will not go to userland to resolve, just assume that there is no answer
709 time_t ki_guid_expiry
;
710 time_t ki_ntsid_expiry
;
713 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
714 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
715 static int kauth_identity_count
;
717 static lck_mtx_t
*kauth_identity_mtx
;
718 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
719 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
722 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
723 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
724 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
725 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
726 static void kauth_identity_lru(struct kauth_identity
*kip
);
727 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
728 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
729 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
730 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
731 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
732 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
736 * kauth_identity_init
738 * Description: Initialize the kernel side of the credential identity resolver
744 * Notes: Intialize the credential identity resolver for use; the
745 * credential identity resolver is the KPI used to communicate
746 * with a user space credential identity resolver daemon.
748 * This function is called from kauth_init() in the file
749 * kern_authorization.c.
752 kauth_identity_init(void)
754 TAILQ_INIT(&kauth_identities
);
755 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
760 * kauth_identity_alloc
762 * Description: Allocate and fill out a kauth_identity structure for
763 * translation between {UID|GID}/GUID/NTSID
767 * Returns: NULL Insufficient memory to satisfy
769 * !NULL A pointer to the applocated
770 * structure, filled in
772 * Notes: It is illegal to translate between UID and GID; any given UUID
773 * or NTSID can oly refer to an NTSIDE or UUID (respectively),
774 * and *either* a UID *or* a GID, but not both.
776 static struct kauth_identity
*
777 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
779 struct kauth_identity
*kip
;
781 /* get and fill in a new identity */
782 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
784 if (gid
!= KAUTH_GID_NONE
) {
786 kip
->ki_valid
= KI_VALID_GID
;
788 if (uid
!= KAUTH_UID_NONE
) {
789 if (kip
->ki_valid
& KI_VALID_GID
)
790 panic("can't allocate kauth identity with both uid and gid");
792 kip
->ki_valid
= KI_VALID_UID
;
795 kip
->ki_guid
= *guidp
;
796 kip
->ki_valid
|= KI_VALID_GUID
;
798 kip
->ki_guid_expiry
= guid_expiry
;
799 if (ntsidp
!= NULL
) {
800 kip
->ki_ntsid
= *ntsidp
;
801 kip
->ki_valid
|= KI_VALID_NTSID
;
803 kip
->ki_ntsid_expiry
= ntsid_expiry
;
810 * kauth_identity_register_and_free
812 * Description: Register an association between identity tokens. The passed
813 * 'kip' is freed by this function.
815 * Parameters: kip Pointer to kauth_identity
816 * structure to register
820 * Notes: The memory pointer to by 'kip' is assumed to have been
821 * previously allocated via kauth_identity_alloc().
824 kauth_identity_register_and_free(struct kauth_identity
*kip
)
826 struct kauth_identity
*ip
;
829 * We search the cache for the UID listed in the incoming association.
830 * If we already have an entry, the new information is merged.
833 KAUTH_IDENTITY_LOCK();
834 if (kip
->ki_valid
& KI_VALID_UID
) {
835 if (kip
->ki_valid
& KI_VALID_GID
)
836 panic("kauth_identity: can't insert record with both UID and GID as key");
837 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
838 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
840 } else if (kip
->ki_valid
& KI_VALID_GID
) {
841 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
842 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
845 panic("kauth_identity: can't insert record without UID or GID as key");
849 /* we already have an entry, merge/overwrite */
850 if (kip
->ki_valid
& KI_VALID_GUID
) {
851 ip
->ki_guid
= kip
->ki_guid
;
852 ip
->ki_valid
|= KI_VALID_GUID
;
854 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
855 if (kip
->ki_valid
& KI_VALID_NTSID
) {
856 ip
->ki_ntsid
= kip
->ki_ntsid
;
857 ip
->ki_valid
|= KI_VALID_NTSID
;
859 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
860 /* and discard the incoming identity */
864 /* don't have any information on this identity, so just add it */
865 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
866 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
867 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
868 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
869 kauth_identity_count
--;
872 KAUTH_IDENTITY_UNLOCK();
873 /* have to drop lock before freeing expired entry */
880 * kauth_identity_updatecache
882 * Description: Given a lookup result, add any associations that we don't
885 * Parameters: elp External lookup result from
886 * user space daemon to kernel
887 * rkip pointer to returned kauth
893 * *rkip Modified (if non-NULL)
896 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
899 struct kauth_identity
*kip
;
904 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
905 KAUTH_IDENTITY_LOCK();
906 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
907 /* matching record */
908 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
909 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
910 kip
->ki_guid
= elp
->el_uguid
;
911 kip
->ki_valid
|= KI_VALID_GUID
;
913 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
914 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
915 kip
->ki_ntsid
= elp
->el_usid
;
916 kip
->ki_valid
|= KI_VALID_NTSID
;
918 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
919 kauth_identity_lru(kip
);
922 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
926 KAUTH_IDENTITY_UNLOCK();
927 /* not found in cache, add new record */
929 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
930 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
931 tv
.tv_sec
+ elp
->el_uguid_valid
,
932 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
933 tv
.tv_sec
+ elp
->el_usid_valid
);
937 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
938 kauth_identity_register_and_free(kip
);
943 /* group identity? */
944 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
945 KAUTH_IDENTITY_LOCK();
946 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
947 /* matching record */
948 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
949 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
950 kip
->ki_guid
= elp
->el_gguid
;
951 kip
->ki_valid
|= KI_VALID_GUID
;
953 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
954 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
955 kip
->ki_ntsid
= elp
->el_gsid
;
956 kip
->ki_valid
|= KI_VALID_NTSID
;
958 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
959 kauth_identity_lru(kip
);
962 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
966 KAUTH_IDENTITY_UNLOCK();
967 /* not found in cache, add new record */
969 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
970 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
971 tv
.tv_sec
+ elp
->el_gguid_valid
,
972 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
973 tv
.tv_sec
+ elp
->el_gsid_valid
);
977 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
978 kauth_identity_register_and_free(kip
);
989 * Description: Promote the entry to the head of the LRU, assumes the cache
992 * Parameters: kip kauth identity to move to the
993 * head of the LRU list, if it's
998 * Notes: This is called even if the entry has expired; typically an
999 * expired entry that's been looked up is about to be revalidated,
1000 * and having it closer to the head of the LRU means finding it
1001 * quickly again when the revalidation comes through.
1004 kauth_identity_lru(struct kauth_identity
*kip
)
1006 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1007 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1008 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1014 * kauth_identity_guid_expired
1016 * Description: Handle lazy expiration of GUID translations.
1018 * Parameters: kip kauth identity to check for
1021 * Returns: 1 Expired
1025 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1030 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
1031 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1036 * kauth_identity_ntsid_expired
1038 * Description: Handle lazy expiration of NTSID translations.
1040 * Parameters: kip kauth identity to check for
1043 * Returns: 1 Expired
1047 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1052 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1053 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1058 * kauth_identity_find_uid
1060 * Description: Search for an entry by UID
1062 * Parameters: uid UID to find
1063 * kir Pointer to return area
1069 * *klr Modified, if found
1072 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
1074 struct kauth_identity
*kip
;
1076 KAUTH_IDENTITY_LOCK();
1077 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1078 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1079 kauth_identity_lru(kip
);
1080 /* Copy via structure assignment */
1085 KAUTH_IDENTITY_UNLOCK();
1086 return((kip
== NULL
) ? ENOENT
: 0);
1091 * kauth_identity_find_uid
1093 * Description: Search for an entry by GID
1095 * Parameters: gid GID to find
1096 * kir Pointer to return area
1102 * *klr Modified, if found
1105 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
1107 struct kauth_identity
*kip
;
1109 KAUTH_IDENTITY_LOCK();
1110 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1111 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1112 kauth_identity_lru(kip
);
1113 /* Copy via structure assignment */
1118 KAUTH_IDENTITY_UNLOCK();
1119 return((kip
== NULL
) ? ENOENT
: 0);
1124 * kauth_identity_find_guid
1126 * Description: Search for an entry by GUID
1128 * Parameters: guidp Pointer to GUID to find
1129 * kir Pointer to return area
1135 * *klr Modified, if found
1137 * Note: The association may be expired, in which case the caller
1138 * may elect to call out to userland to revalidate.
1141 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
1143 struct kauth_identity
*kip
;
1145 KAUTH_IDENTITY_LOCK();
1146 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1147 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1148 kauth_identity_lru(kip
);
1149 /* Copy via structure assignment */
1154 KAUTH_IDENTITY_UNLOCK();
1155 return((kip
== NULL
) ? ENOENT
: 0);
1160 * kauth_identity_find_ntsid
1162 * Description: Search for an entry by NTSID
1164 * Parameters: ntsid Pointer to NTSID to find
1165 * kir Pointer to return area
1171 * *klr Modified, if found
1173 * Note: The association may be expired, in which case the caller
1174 * may elect to call out to userland to revalidate.
1177 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
1179 struct kauth_identity
*kip
;
1181 KAUTH_IDENTITY_LOCK();
1182 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1183 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1184 kauth_identity_lru(kip
);
1185 /* Copy via structure assignment */
1190 KAUTH_IDENTITY_UNLOCK();
1191 return((kip
== NULL
) ? ENOENT
: 0);
1198 guid_t kauth_null_guid
;
1204 * Description: Determine the equality of two GUIDs
1206 * Parameters: guid1 Pointer to first GUID
1207 * guid2 Pointer to second GUID
1209 * Returns: 0 If GUIDs are inequal
1210 * !0 If GUIDs are equal
1213 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1215 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1220 * kauth_wellknown_guid
1222 * Description: Determine if a GUID is a well-known GUID
1224 * Parameters: guid Pointer to GUID to check
1226 * Returns: KAUTH_WKG_NOT Not a wel known GUID
1227 * KAUTH_WKG_EVERYBODY "Everybody"
1228 * KAUTH_WKG_NOBODY "Nobody"
1229 * KAUTH_WKG_OWNER "Other"
1230 * KAUTH_WKG_GROUP "Group"
1233 kauth_wellknown_guid(guid_t
*guid
)
1235 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1238 * All WKGs begin with the same 12 bytes.
1240 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1242 * The final 4 bytes are our code (in network byte order).
1244 code
= OSSwapHostToBigInt32(*(u_int32_t
*)&guid
->g_guid
[12]);
1247 return(KAUTH_WKG_EVERYBODY
);
1249 return(KAUTH_WKG_NOBODY
);
1251 return(KAUTH_WKG_OWNER
);
1253 return(KAUTH_WKG_GROUP
);
1256 return(KAUTH_WKG_NOT
);
1263 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1265 * Paramters: sid1 Pointer to first NTSID
1266 * sid2 Pointer to second NTSID
1268 * Returns: 0 If GUIDs are inequal
1269 * !0 If GUIDs are equal
1272 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1274 /* check sizes for equality, also sanity-check size while we're at it */
1275 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1276 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1277 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1286 * We support four tokens representing identity:
1287 * - Credential reference
1290 * - NT security identifier
1292 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1296 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
1300 * kauth_cred_change_egid
1302 * Description: Set EGID by changing the first element of cr_groups for the
1303 * passed credential; if the new EGID exists in the list of
1304 * groups already, then rotate the old EGID into its position,
1305 * otherwise replace it
1307 * Parameters: cred Pointer to the credential to modify
1308 * new_egid The new EGID to set
1310 * Returns: 0 The egid did not displace a member of
1311 * the supplementary group list
1312 * 1 The egid being set displaced a member
1313 * of the supplementary groups list
1315 * Note: Utility function; internal use only because of locking.
1317 * This function operates on the credential passed; the caller
1318 * must operate either on a newly allocated credential (one for
1319 * which there is no hash cache reference and no externally
1320 * visible pointer reference), or a template credential.
1323 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1329 #endif /* radar_4600026 */
1330 gid_t old_egid
= cred
->cr_groups
[0];
1332 /* Ignoring the first entry, scan for a match for the new egid */
1333 for (i
= 1; i
< cred
->cr_ngroups
; i
++) {
1335 * If we find a match, swap them so we don't lose overall
1338 if (cred
->cr_groups
[i
] == new_egid
) {
1339 cred
->cr_groups
[i
] = old_egid
;
1340 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1347 #error Fix radar 4600026 first!!!
1350 This is correct for memberd behaviour, but incorrect for POSIX; to address
1351 this, we would need to automatically opt-out any SUID/SGID binary, and force
1352 it to use initgroups to opt back in. We take the approach of considering it
1353 opt'ed out in any group of 16 displacement instead, since it's a much more
1354 conservative approach (i.e. less likely to cause things to break).
1358 * If we displaced a member of the supplementary groups list of the
1359 * credential, and we have not opted out of memberd, then if memberd
1360 * says that the credential is a member of the group, then it has not
1361 * actually been displaced.
1363 * NB: This is typically a cold code path.
1365 if (displaced
&& !(cred
->cr_flags
& CRF_NOMEMBERD
) &&
1366 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1369 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1371 #endif /* radar_4600026 */
1373 /* set the new EGID into the old spot */
1374 cred
->cr_groups
[0] = new_egid
;
1383 * Description: Fetch UID from credential
1385 * Parameters: cred Credential to examine
1387 * Returns: (uid_t) UID associated with credential
1390 kauth_cred_getuid(kauth_cred_t cred
)
1392 NULLCRED_CHECK(cred
);
1393 return(cred
->cr_uid
);
1400 * Description: Fetch GID from credential
1402 * Parameters: cred Credential to examine
1404 * Returns: (gid_t) GID associated with credential
1407 kauth_cred_getgid(kauth_cred_t cred
)
1409 NULLCRED_CHECK(cred
);
1410 return(cred
->cr_gid
);
1415 * kauth_cred_guid2uid
1417 * Description: Fetch UID from GUID
1419 * Parameters: guidp Pointer to GUID to examine
1420 * uidp Pointer to buffer for UID
1422 * Returns: 0 Success
1423 * kauth_cred_cache_lookup:EINVAL
1426 * *uidp Modified, if successful
1429 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
1431 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
1436 * kauth_cred_guid2gid
1438 * Description: Fetch GID from GUID
1440 * Parameters: guidp Pointer to GUID to examine
1441 * gidp Pointer to buffer for GID
1443 * Returns: 0 Success
1444 * kauth_cred_cache_lookup:EINVAL
1447 * *gidp Modified, if successful
1450 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
1452 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
1457 * kauth_cred_ntsid2uid
1459 * Description: Fetch UID from NTSID
1461 * Parameters: sidp Pointer to NTSID to examine
1462 * uidp Pointer to buffer for UID
1464 * Returns: 0 Success
1465 * kauth_cred_cache_lookup:EINVAL
1468 * *uidp Modified, if successful
1471 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
1473 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
1478 * kauth_cred_ntsid2gid
1480 * Description: Fetch GID from NTSID
1482 * Parameters: sidp Pointer to NTSID to examine
1483 * gidp Pointer to buffer for GID
1485 * Returns: 0 Success
1486 * kauth_cred_cache_lookup:EINVAL
1489 * *gidp Modified, if successful
1492 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
1494 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
1499 * kauth_cred_ntsid2guid
1501 * Description: Fetch GUID from NTSID
1503 * Parameters: sidp Pointer to NTSID to examine
1504 * guidp Pointer to buffer for GUID
1506 * Returns: 0 Success
1507 * kauth_cred_cache_lookup:EINVAL
1510 * *guidp Modified, if successful
1513 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
1515 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
1520 * kauth_cred_uid2guid
1522 * Description: Fetch GUID from UID
1524 * Parameters: uid UID to examine
1525 * guidp Pointer to buffer for GUID
1527 * Returns: 0 Success
1528 * kauth_cred_cache_lookup:EINVAL
1531 * *guidp Modified, if successful
1534 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
1536 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
1541 * kauth_cred_getguid
1543 * Description: Fetch GUID from credential
1545 * Parameters: cred Credential to examine
1546 * guidp Pointer to buffer for GUID
1548 * Returns: 0 Success
1549 * kauth_cred_cache_lookup:EINVAL
1552 * *guidp Modified, if successful
1555 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
1557 NULLCRED_CHECK(cred
);
1558 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
1563 * kauth_cred_getguid
1565 * Description: Fetch GUID from GID
1567 * Parameters: gid GID to examine
1568 * guidp Pointer to buffer for GUID
1570 * Returns: 0 Success
1571 * kauth_cred_cache_lookup:EINVAL
1574 * *guidp Modified, if successful
1577 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
1579 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
1584 * kauth_cred_uid2ntsid
1586 * Description: Fetch NTSID from UID
1588 * Parameters: uid UID to examine
1589 * sidp Pointer to buffer for NTSID
1591 * Returns: 0 Success
1592 * kauth_cred_cache_lookup:EINVAL
1595 * *sidp Modified, if successful
1598 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
1600 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
1605 * kauth_cred_getntsid
1607 * Description: Fetch NTSID from credential
1609 * Parameters: cred Credential to examine
1610 * sidp Pointer to buffer for NTSID
1612 * Returns: 0 Success
1613 * kauth_cred_cache_lookup:EINVAL
1616 * *sidp Modified, if successful
1619 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
1621 NULLCRED_CHECK(cred
);
1622 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
1627 * kauth_cred_gid2ntsid
1629 * Description: Fetch NTSID from GID
1631 * Parameters: gid GID to examine
1632 * sidp Pointer to buffer for NTSID
1634 * Returns: 0 Success
1635 * kauth_cred_cache_lookup:EINVAL
1638 * *sidp Modified, if successful
1641 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1643 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1648 * kauth_cred_guid2ntsid
1650 * Description: Fetch NTSID from GUID
1652 * Parameters: guidp Pointer to GUID to examine
1653 * sidp Pointer to buffer for NTSID
1655 * Returns: 0 Success
1656 * kauth_cred_cache_lookup:EINVAL
1659 * *sidp Modified, if successful
1662 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1664 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1669 * kauth_cred_cache_lookup
1671 * Description: Lookup a translation in the cache; if one is not found, and
1672 * the attempt was not fatal, submit the request to the resolver
1673 * instead, and wait for it to complete or be aborted.
1675 * Parameters: from Identity information we have
1676 * to Identity information we want
1677 * src Pointer to buffer containing
1678 * the source identity
1679 * dst Pointer to buffer to receive
1680 * the target identity
1682 * Returns: 0 Success
1683 * EINVAL Unknown source identity type
1686 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1688 struct kauth_identity ki
;
1689 struct kauth_identity_extlookup el
;
1691 int (* expired
)(struct kauth_identity
*kip
);
1693 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1696 * Look for an existing cache entry for this association.
1697 * If the entry has not expired, return the cached information.
1702 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1705 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1708 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1710 case KI_VALID_NTSID
:
1711 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1716 /* lookup failure or error */
1718 /* any other error is fatal */
1719 if (error
!= ENOENT
) {
1720 /* XXX bogus check - this is not possible */
1721 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1725 /* do we have a translation? */
1726 if (ki
.ki_valid
& to
) {
1727 /* found a valid cached entry, check expiry */
1730 expired
= kauth_identity_guid_expired
;
1732 case KI_VALID_NTSID
:
1733 expired
= kauth_identity_ntsid_expired
;
1738 expired
= kauth_identity_guid_expired
;
1740 case KI_VALID_NTSID
:
1741 expired
= kauth_identity_ntsid_expired
;
1747 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1749 * If no expiry function, or not expired, we have found
1753 KAUTH_DEBUG("CACHE - no expiry function");
1756 if (!expired(&ki
)) {
1757 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1761 * We leave ki_valid set here; it contains a
1762 * translation but the TTL has expired. If we can't
1763 * get a result from the resolver, we will use it as
1764 * a better-than nothing alternative.
1766 KAUTH_DEBUG("CACHE - expired entry found");
1771 * We failed to find a cache entry; call the resolver.
1773 * Note: We ask for as much data as we can get.
1777 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1778 el
.el_uid
= *(uid_t
*)src
;
1781 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1782 el
.el_gid
= *(gid_t
*)src
;
1785 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1786 el
.el_uguid
= *(guid_t
*)src
;
1787 el
.el_gguid
= *(guid_t
*)src
;
1789 case KI_VALID_NTSID
:
1790 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1791 el
.el_usid
= *(ntsid_t
*)src
;
1792 el
.el_gsid
= *(ntsid_t
*)src
;
1798 * Here we ask for everything all at once, to avoid having to work
1799 * out what we really want now, or might want soon.
1801 * Asking for SID translations when we don't know we need them right
1802 * now is going to cause excess work to be done if we're connected
1803 * to a network that thinks it can translate them. This list needs
1804 * to get smaller/smarter.
1806 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1807 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1808 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1809 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1810 error
= kauth_resolver_submit(&el
);
1811 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1812 /* was the lookup successful? */
1815 * Save the results from the lookup - may have other
1816 * information even if we didn't get a guid.
1818 kauth_identity_updatecache(&el
, &ki
);
1821 * Check to see if we have a valid result.
1823 if (!error
&& !(ki
.ki_valid
& to
))
1830 *(uid_t
*)dst
= ki
.ki_uid
;
1833 *(gid_t
*)dst
= ki
.ki_gid
;
1836 *(guid_t
*)dst
= ki
.ki_guid
;
1838 case KI_VALID_NTSID
:
1839 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1844 KAUTH_DEBUG("CACHE - returned successfully");
1850 * Group membership cache.
1852 * XXX the linked-list implementation here needs to be optimized.
1855 struct kauth_group_membership
{
1856 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1857 uid_t gm_uid
; /* the identity whose membership we're recording */
1858 gid_t gm_gid
; /* group of which they are a member */
1859 time_t gm_expiry
; /* TTL for the membership */
1861 #define KAUTH_GROUP_ISMEMBER (1<<0)
1864 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1865 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1866 static int kauth_groups_count
;
1868 static lck_mtx_t
*kauth_groups_mtx
;
1869 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1870 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1872 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1873 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1874 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1880 * Description: Initialize the groups cache
1882 * Parameters: (void)
1886 * Notes: Intialize the groups cache for use; the group cache is used
1887 * to avoid unnecessary calls out to user space.
1889 * This function is called from kauth_init() in the file
1890 * kern_authorization.c.
1893 kauth_groups_init(void)
1895 TAILQ_INIT(&kauth_groups
);
1896 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1901 * kauth_groups_expired
1903 * Description: Handle lazy expiration of group membership cache entries
1905 * Parameters: gm group membership entry to
1906 * check for expiration
1908 * Returns: 1 Expired
1912 kauth_groups_expired(struct kauth_group_membership
*gm
)
1917 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
1924 * Description: Promote the entry to the head of the LRU, assumes the cache
1927 * Parameters: kip group membership entry to move
1928 * to the head of the LRU list,
1929 * if it's not already there
1933 * Notes: This is called even if the entry has expired; typically an
1934 * expired entry that's been looked up is about to be revalidated,
1935 * and having it closer to the head of the LRU means finding it
1936 * quickly again when the revalidation comes through.
1939 kauth_groups_lru(struct kauth_group_membership
*gm
)
1941 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
1942 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
1943 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
1949 * kauth_groups_updatecache
1951 * Description: Given a lookup result, add any group cache associations that
1952 * we don't currently have.
1954 * Parameters: elp External lookup result from
1955 * user space daemon to kernel
1956 * rkip pointer to returned kauth
1962 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
1964 struct kauth_group_membership
*gm
;
1967 /* need a valid response if we are to cache anything */
1969 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
1970 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
1976 * Search for an existing record for this association before inserting
1977 * a new one; if we find one, update it instead of creating a new one
1979 KAUTH_GROUPS_LOCK();
1980 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
1981 if ((el
->el_uid
== gm
->gm_uid
) &&
1982 (el
->el_gid
== gm
->gm_gid
)) {
1983 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
1984 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
1986 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
1988 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
1989 kauth_groups_lru(gm
);
1993 KAUTH_GROUPS_UNLOCK();
1995 /* if we found an entry to update, stop here */
1999 /* allocate a new record */
2000 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2002 gm
->gm_uid
= el
->el_uid
;
2003 gm
->gm_gid
= el
->el_gid
;
2004 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2005 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2007 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2009 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2013 * Insert the new entry. Note that it's possible to race ourselves
2014 * here and end up with duplicate entries in the list. Wasteful, but
2015 * harmless since the first into the list will never be looked up,
2016 * and thus will eventually just fall off the end.
2018 KAUTH_GROUPS_LOCK();
2019 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2020 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
2021 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2022 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2023 kauth_groups_count
--;
2027 KAUTH_GROUPS_UNLOCK();
2029 /* free expired cache entry */
2036 * Group membership KPI
2040 * kauth_cred_ismember_gid
2042 * Description: Given a credential and a GID, determine if the GID is a member
2043 * of one of the supplementary groups associated with the given
2046 * Parameters: cred Credential to check in
2047 * gid GID to check for membership
2048 * resultp Pointer to int to contain the
2049 * result of the call
2051 * Returns: 0 Success
2052 * ENOENT Could not proform lookup
2053 * kauth_resolver_submit:EWOULDBLOCK
2054 * kauth_resolver_submit:EINTR
2055 * kauth_resolver_submit:ENOMEM
2056 * kauth_resolver_submit:??? Unlikely error from user space
2059 * *resultp (modified) 1 Is member
2062 * Notes: This function guarantees not to modify resultp when returning
2065 * This function effectively checkes the EGID as well, since the
2066 * EGID is cr_groups[0] as an implementation detail.
2069 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2071 struct kauth_group_membership
*gm
;
2072 struct kauth_identity_extlookup el
;
2076 * Check the per-credential list of override groups.
2078 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2079 * the cache should be used for that case.
2081 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
2082 if (gid
== cred
->cr_groups
[i
]) {
2089 * If we don't have a UID for group membership checks, the in-cred list
2090 * was authoritative and we can stop here.
2092 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
2099 * If the resolver hasn't checked in yet, we are early in the boot
2100 * phase and the local group list is complete and authoritative.
2102 if (!kauth_resolver_registered
) {
2108 /* XXX check supplementary groups */
2109 /* XXX check whiteout groups */
2110 /* XXX nesting of supplementary/whiteout groups? */
2113 * Check the group cache.
2115 KAUTH_GROUPS_LOCK();
2116 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2117 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2118 kauth_groups_lru(gm
);
2123 /* did we find a membership entry? */
2125 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2126 KAUTH_GROUPS_UNLOCK();
2128 /* if we did, we can return now */
2132 /* nothing in the cache, need to go to userland */
2133 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2134 el
.el_uid
= cred
->cr_gmuid
;
2136 el
.el_member_valid
= 0; /* XXX set by resolver? */
2137 error
= kauth_resolver_submit(&el
);
2140 /* save the results from the lookup */
2141 kauth_groups_updatecache(&el
);
2143 /* if we successfully ascertained membership, report */
2144 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2145 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2154 * kauth_cred_ismember_guid
2156 * Description: Determine whether the supplied credential is a member of the
2157 * group nominated by GUID.
2159 * Parameters: cred Credential to check in
2160 * guidp Pointer to GUID whose group
2161 * we are testing for membership
2162 * resultp Pointer to int to contain the
2163 * result of the call
2165 * Returns: 0 Success
2166 * kauth_cred_guid2gid:EINVAL
2167 * kauth_cred_ismember_gid:ENOENT
2168 * kauth_cred_ismember_gid:EWOULDBLOCK
2169 * kauth_cred_ismember_gid:EINTR
2170 * kauth_cred_ismember_gid:ENOMEM
2171 * kauth_cred_ismember_gid:??? Unlikely error from user space
2174 * *resultp (modified) 1 Is member
2178 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2184 wkg
= kauth_wellknown_guid(guidp
);
2186 case KAUTH_WKG_NOBODY
:
2189 case KAUTH_WKG_EVERYBODY
:
2193 /* translate guid to gid */
2194 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2196 * If we have no guid -> gid translation, it's not a group and
2197 * thus the cred can't be a member.
2199 if (error
== ENOENT
) {
2204 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
2211 * kauth_cred_gid_subset
2213 * Description: Given two credentials, determine if all GIDs associated with
2214 * the first are also associated with the second
2216 * Parameters: cred1 Credential to check for
2217 * cred2 Credential to check in
2218 * resultp Pointer to int to contain the
2219 * result of the call
2221 * Returns: 0 Success
2222 * non-zero See kauth_cred_ismember_gid for
2226 * *resultp (modified) 1 Is subset
2229 * Notes: This function guarantees not to modify resultp when returning
2233 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
2235 int i
, err
, res
= 1;
2238 /* First, check the local list of groups */
2239 for (i
= 0; i
< cred1
->cr_ngroups
; i
++) {
2240 gid
= cred1
->cr_groups
[i
];
2241 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
2245 if (!res
&& gid
!= cred2
->cr_rgid
&& gid
!= cred2
->cr_svgid
) {
2251 /* Check real gid */
2252 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_rgid
, &res
)) != 0) {
2256 if (!res
&& cred1
->cr_rgid
!= cred2
->cr_rgid
&&
2257 cred1
->cr_rgid
!= cred2
->cr_svgid
) {
2262 /* Finally, check saved gid */
2263 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_svgid
, &res
)) != 0){
2267 if (!res
&& cred1
->cr_svgid
!= cred2
->cr_rgid
&&
2268 cred1
->cr_svgid
!= cred2
->cr_svgid
) {
2279 * kauth_cred_issuser
2281 * Description: Fast replacement for issuser()
2283 * Parameters: cred Credential to check for super
2286 * Returns: 0 Not super user
2289 * Notes: This function uses a magic number which is not a manifest
2290 * constant; this is bad practice.
2293 kauth_cred_issuser(kauth_cred_t cred
)
2295 return(cred
->cr_uid
== 0);
2303 /* lock protecting credential hash table */
2304 static lck_mtx_t
*kauth_cred_hash_mtx
;
2305 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2306 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2307 #if KAUTH_CRED_HASH_DEBUG
2308 #define KAUTH_CRED_HASH_LOCK_ASSERT() _mutex_assert(kauth_cred_hash_mtx, MA_OWNED)
2309 #else /* !KAUTH_CRED_HASH_DEBUG */
2310 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2311 #endif /* !KAUTH_CRED_HASH_DEBUG */
2317 * Description: Initialize the credential hash cache
2319 * Parameters: (void)
2323 * Notes: Intialize the credential hash cache for use; the credential
2324 * hash cache is used convert duplicate credentials into a
2325 * single reference counted credential in order to save wired
2326 * kernel memory. In practice, this generally means a desktop
2327 * system runs with a few tens of credentials, instead of one
2328 * per process, one per thread, one per vnode cache entry, and
2329 * so on. This generally results in savings of 200K or more
2330 * (potentially much more on server systems).
2332 * The hash cache internally has a reference on the credential
2333 * for itself as a means of avoiding a reclaim race for a
2334 * credential in the process of having it's last non-hash
2335 * reference released. This would otherwise result in the
2336 * possibility of a freed credential that was still in uses due
2337 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
2339 * On final release, the hash reference is droped, and the
2340 * credential is freed back to the system.
2342 * This function is called from kauth_init() in the file
2343 * kern_authorization.c.
2346 kauth_cred_init(void)
2350 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2351 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
2353 /*allocate credential hash table */
2354 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
2355 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
2356 M_KAUTH
, M_WAITOK
| M_ZERO
);
2357 if (kauth_cred_table_anchor
== NULL
)
2358 panic("startup: kauth_cred_init");
2359 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2360 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
2368 * Description: Get the current thread's effective UID.
2370 * Parameters: (void)
2372 * Returns: (uid_t) The effective UID of the
2378 return(kauth_cred_get()->cr_uid
);
2385 * Description: Get the current thread's real UID.
2387 * Parameters: (void)
2389 * Returns: (uid_t) The real UID of the current
2395 return(kauth_cred_get()->cr_ruid
);
2402 * Description: Get the current thread's effective GID.
2404 * Parameters: (void)
2406 * Returns: (gid_t) The effective GID of the
2412 return(kauth_cred_get()->cr_groups
[0]);
2419 * Description: Get the current thread's real GID.
2421 * Parameters: (void)
2423 * Returns: (gid_t) The real GID of the current
2429 return(kauth_cred_get()->cr_rgid
);
2436 * Description: Returns a pointer to the current thread's credential
2438 * Parameters: (void)
2440 * Returns: (kauth_cred_t) Pointer to the current thread's
2443 * Notes: This function does not take a reference; because of this, the
2444 * caller MUST NOT do anything that would let the thread's
2445 * credential change while using the returned value, without
2446 * first explicitly taking their own reference.
2448 * If a caller intends to take a reference on the resulting
2449 * credential pointer from calling this function, it is strongly
2450 * recommended that the caller use kauth_cred_get_with_ref()
2451 * instead, to protect against any future changes to the cred
2452 * locking protocols; such changes could otherwise potentially
2453 * introduce race windows in the callers code.
2456 kauth_cred_get(void)
2459 struct uthread
*uthread
;
2461 uthread
= get_bsdthread_info(current_thread());
2463 if (uthread
== NULL
)
2464 panic("thread wants credential but has no BSD thread info");
2466 * We can lazy-bind credentials to threads, as long as their processes
2469 * XXX If we later inline this function, the code in this block
2470 * XXX should probably be called out in a function.
2472 if (uthread
->uu_ucred
== NOCRED
) {
2473 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2474 panic("thread wants credential but has no BSD process");
2475 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
2477 return(uthread
->uu_ucred
);
2482 * kauth_cred_uthread_update
2484 * Description: Given a uthread, a proc, and whether or not the proc is locked,
2485 * late-bind the uthread cred to the proc cred.
2487 * Parameters: uthread_t The uthread to update
2488 * proc_t The process to update to
2492 * Notes: This code is common code called from system call or trap entry
2493 * in the case that the process thread may have been changed
2494 * since the last time the thread entered the kernel. It is
2495 * generally only called with the current uthread and process as
2499 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
2501 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
2502 (uthread
->uu_flag
& UT_SETUID
) == 0) {
2503 kauth_cred_t old
= uthread
->uu_ucred
;
2504 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
2505 if (IS_VALID_CRED(old
))
2506 kauth_cred_unref(&old
);
2512 * kauth_cred_get_with_ref
2514 * Description: Takes a reference on the current thread's credential, and then
2515 * returns a pointer to it to the caller.
2517 * Parameters: (void)
2519 * Returns: (kauth_cred_t) Pointer to the current thread's
2520 * newly referenced credential
2522 * Notes: This function takes a reference on the credential before
2523 * returning it to the caller.
2525 * It is the responsibility of the calling code to release this
2526 * reference when the credential is no longer in use.
2528 * Since the returned reference may be a persistent reference
2529 * (e.g. one cached in another data structure with a lifetime
2530 * longer than the calling function), this release may be delayed
2531 * until such time as the persistent reference is to be destroyed.
2532 * An example of this would be the per vnode credential cache used
2533 * to accelerate lookup operations.
2536 kauth_cred_get_with_ref(void)
2539 struct uthread
*uthread
;
2541 uthread
= get_bsdthread_info(current_thread());
2543 if (uthread
== NULL
)
2544 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
2545 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2546 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
2549 * We can lazy-bind credentials to threads, as long as their processes
2552 * XXX If we later inline this function, the code in this block
2553 * XXX should probably be called out in a function.
2555 if (uthread
->uu_ucred
== NOCRED
) {
2556 /* take reference for new cred in thread */
2557 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
2559 /* take a reference for our caller */
2560 kauth_cred_ref(uthread
->uu_ucred
);
2561 return(uthread
->uu_ucred
);
2566 * kauth_cred_proc_ref
2568 * Description: Takes a reference on the current process's credential, and
2569 * then returns a pointer to it to the caller.
2571 * Parameters: procp Process whose credential we
2572 * intend to take a reference on
2574 * Returns: (kauth_cred_t) Pointer to the process's
2575 * newly referenced credential
2577 * Locks: PROC_LOCK is held before taking the reference and released
2578 * after the refeence is taken to protect the p_ucred field of
2579 * the process referred to by procp.
2581 * Notes: This function takes a reference on the credential before
2582 * returning it to the caller.
2584 * It is the responsibility of the calling code to release this
2585 * reference when the credential is no longer in use.
2587 * Since the returned reference may be a persistent reference
2588 * (e.g. one cached in another data structure with a lifetime
2589 * longer than the calling function), this release may be delayed
2590 * until such time as the persistent reference is to be destroyed.
2591 * An example of this would be the per vnode credential cache used
2592 * to accelerate lookup operations.
2595 kauth_cred_proc_ref(proc_t procp
)
2600 cred
= proc_ucred(procp
);
2601 kauth_cred_ref(cred
);
2610 * Description: Allocate a new credential
2612 * Parameters: (void)
2614 * Returns: !NULL Newly allocated credential
2615 * NULL Insufficient memory
2617 * Notes: The newly allocated credential is zero'ed as part of the
2618 * allocation process, with the exception of the reference
2619 * count, which is set to 1 to indicate a single reference
2620 * held by the caller.
2622 * Since newly allocated credentials have no external pointers
2623 * referencing them, prior to making them visible in an externally
2624 * visible pointer (e.g. by adding them to the credential hash
2625 * cache) is the only legal time in which an existing credential
2626 * can be safely iinitialized or modified directly.
2628 * After initialization, the caller is expected to call the
2629 * function kauth_cred_add() to add the credential to the hash
2630 * cache, after which time it's frozen and becomes publically
2633 * The release protocol depends on kauth_hash_add() being called
2634 * before kauth_cred_rele() (there is a diagnostic panic which
2635 * will trigger if this protocol is not observed).
2637 * XXX: This function really ought to be static, rather than being
2638 * exported as KPI, since a failure of kauth_cred_add() can only
2639 * be handled by an explicit free of the credential; such frees
2640 * depend on knowlegdge of the allocation method used, which is
2641 * permitted to change between kernel revisions.
2643 * XXX: In the insufficient resource case, this code panic's rather
2644 * than returning a NULL pointer; the code that calls this
2645 * function needs to be audited before this can be changed.
2648 kauth_cred_alloc(void)
2650 kauth_cred_t newcred
;
2652 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
2654 bzero(newcred
, sizeof(*newcred
));
2655 newcred
->cr_ref
= 1;
2656 /* must do this, or cred has same group membership as uid 0 */
2657 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
2660 panic("kauth_cred_alloc: couldn't allocate credential");
2664 #if KAUTH_CRED_HASH_DEBUG
2669 mac_cred_label_init(newcred
);
2679 * Description: Look to see if we already have a known credential in the hash
2680 * cache; if one is found, bump the reference count and return
2681 * it. If there are no credentials that match the given
2682 * credential, then allocate a new credential.
2684 * Parameters: cred Template for credential to
2687 * Returns: (kauth_cred_t) The credential that was found
2688 * in the hash or created
2689 * NULL kauth_cred_add() failed, or
2690 * there was not an egid specified
2692 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
2693 * maintain this field, we can't expect callers to know how it
2694 * needs to be set. Callers should be prepared for this field
2695 * to be overwritten.
2697 * XXX: This code will tight-loop if memory for a new credential is
2698 * persistently unavailable; this is perhaps not the wisest way
2699 * to handle this condition, but current callers do not expect
2703 kauth_cred_create(kauth_cred_t cred
)
2705 kauth_cred_t found_cred
, new_cred
= NULL
;
2707 KAUTH_CRED_HASH_LOCK_ASSERT();
2709 if (cred
->cr_flags
& CRF_NOMEMBERD
)
2710 cred
->cr_gmuid
= KAUTH_UID_NONE
;
2712 cred
->cr_gmuid
= cred
->cr_uid
;
2714 /* Caller *must* specify at least the egid in cr_groups[0] */
2715 if (cred
->cr_ngroups
< 1)
2719 KAUTH_CRED_HASH_LOCK();
2720 found_cred
= kauth_cred_find(cred
);
2721 if (found_cred
!= NULL
) {
2723 * Found an existing credential so we'll bump
2724 * reference count and return
2726 kauth_cred_ref(found_cred
);
2727 KAUTH_CRED_HASH_UNLOCK();
2730 KAUTH_CRED_HASH_UNLOCK();
2733 * No existing credential found. Create one and add it to
2736 new_cred
= kauth_cred_alloc();
2737 if (new_cred
!= NULL
) {
2739 new_cred
->cr_uid
= cred
->cr_uid
;
2740 new_cred
->cr_ruid
= cred
->cr_ruid
;
2741 new_cred
->cr_svuid
= cred
->cr_svuid
;
2742 new_cred
->cr_rgid
= cred
->cr_rgid
;
2743 new_cred
->cr_svgid
= cred
->cr_svgid
;
2744 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
2745 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
2746 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
2747 new_cred
->cr_flags
= cred
->cr_flags
;
2749 KAUTH_CRED_HASH_LOCK();
2750 err
= kauth_cred_add(new_cred
);
2751 KAUTH_CRED_HASH_UNLOCK();
2753 /* Retry if kauth_cred_add returns non zero value */
2757 mac_cred_label_destroy(new_cred
);
2759 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
2769 * kauth_cred_setresuid
2771 * Description: Update the given credential using the UID arguments. The given
2772 * UIDs are used to set the effective UID, real UID, saved UID,
2773 * and GMUID (used for group membership checking).
2775 * Parameters: cred The original credential
2776 * ruid The new real UID
2777 * euid The new effective UID
2778 * svuid The new saved UID
2779 * gmuid KAUTH_UID_NONE -or- the new
2780 * group membership UID
2782 * Returns: (kauth_cred_t) The updated credential
2784 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
2785 * setting, so if you don't want it to change, pass it the
2786 * previous value, explicitly.
2788 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
2789 * if it returns a credential other than the one it is passed,
2790 * will have dropped the reference on the passed credential. All
2791 * callers should be aware of this, and treat this function as an
2792 * unref + ref, potentially on different credentials.
2794 * Because of this, the caller is expected to take its own
2795 * reference on the credential passed as the first parameter,
2796 * and be prepared to release the reference on the credential
2797 * that is returned to them, if it is not intended to be a
2798 * persistent reference.
2801 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
2803 struct ucred temp_cred
;
2805 NULLCRED_CHECK(cred
);
2808 * We don't need to do anything if the UIDs we are changing are
2809 * already the same as the UIDs passed in
2811 if ((euid
== KAUTH_UID_NONE
|| cred
->cr_uid
== euid
) &&
2812 (ruid
== KAUTH_UID_NONE
|| cred
->cr_ruid
== ruid
) &&
2813 (svuid
== KAUTH_UID_NONE
|| cred
->cr_svuid
== svuid
) &&
2814 (cred
->cr_gmuid
== gmuid
)) {
2815 /* no change needed */
2820 * Look up in cred hash table to see if we have a matching credential
2821 * with the new values; this is done by calling kauth_cred_update().
2823 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2824 if (euid
!= KAUTH_UID_NONE
) {
2825 temp_cred
.cr_uid
= euid
;
2827 if (ruid
!= KAUTH_UID_NONE
) {
2828 temp_cred
.cr_ruid
= ruid
;
2830 if (svuid
!= KAUTH_UID_NONE
) {
2831 temp_cred
.cr_svuid
= svuid
;
2835 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
2836 * opt out of participation in external group resolution, unless we
2837 * unless we explicitly opt back in later.
2839 if ((temp_cred
.cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
2840 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
2843 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
2848 * kauth_cred_setresgid
2850 * Description: Update the given credential using the GID arguments. The given
2851 * GIDs are used to set the effective GID, real GID, and saved
2854 * Parameters: cred The original credential
2855 * rgid The new real GID
2856 * egid The new effective GID
2857 * svgid The new saved GID
2859 * Returns: (kauth_cred_t) The updated credential
2861 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
2862 * if it returns a credential other than the one it is passed,
2863 * will have dropped the reference on the passed credential. All
2864 * callers should be aware of this, and treat this function as an
2865 * unref + ref, potentially on different credentials.
2867 * Because of this, the caller is expected to take its own
2868 * reference on the credential passed as the first parameter,
2869 * and be prepared to release the reference on the credential
2870 * that is returned to them, if it is not intended to be a
2871 * persistent reference.
2874 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
2876 struct ucred temp_cred
;
2878 NULLCRED_CHECK(cred
);
2879 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
2882 * We don't need to do anything if the given GID are already the
2883 * same as the GIDs in the credential.
2885 if (cred
->cr_groups
[0] == egid
&&
2886 cred
->cr_rgid
== rgid
&&
2887 cred
->cr_svgid
== svgid
) {
2888 /* no change needed */
2893 * Look up in cred hash table to see if we have a matching credential
2894 * with the new values; this is done by calling kauth_cred_update().
2896 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2897 if (egid
!= KAUTH_GID_NONE
) {
2898 /* displacing a supplementary group opts us out of memberd */
2899 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
2900 DEBUG_CRED_CHANGE("displaced!\n");
2901 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
2902 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
2904 DEBUG_CRED_CHANGE("not displaced\n");
2907 if (rgid
!= KAUTH_GID_NONE
) {
2908 temp_cred
.cr_rgid
= rgid
;
2910 if (svgid
!= KAUTH_GID_NONE
) {
2911 temp_cred
.cr_svgid
= svgid
;
2914 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
2919 * Update the given credential with the given groups. We only allocate a new
2920 * credential when the given gid actually results in changes to the existing
2922 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
2923 * which will be used for group membership checking.
2926 * kauth_cred_setgroups
2928 * Description: Update the given credential using the provide supplementary
2929 * group list and group membership UID
2931 * Parameters: cred The original credential
2932 * groups Pointer to gid_t array which
2933 * contains the new group list
2934 * groupcount The cound of valid groups which
2935 * are contained in 'groups'
2936 * gmuid KAUTH_UID_NONE -or- the new
2937 * group membership UID
2939 * Returns: (kauth_cred_t) The updated credential
2941 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
2942 * setting, so if you don't want it to change, pass it the
2943 * previous value, explicitly.
2945 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
2946 * if it returns a credential other than the one it is passed,
2947 * will have dropped the reference on the passed credential. All
2948 * callers should be aware of this, and treat this function as an
2949 * unref + ref, potentially on different credentials.
2951 * Because of this, the caller is expected to take its own
2952 * reference on the credential passed as the first parameter,
2953 * and be prepared to release the reference on the credential
2954 * that is returned to them, if it is not intended to be a
2955 * persistent reference.
2957 * XXX: Changes are determined in ordinal order - if the caller pasess
2958 * in the same groups list that is already present in the
2959 * credential, but the members are in a different order, even if
2960 * the EGID is not modified (i.e. cr_groups[0] is the same), it
2961 * is considered a modification to the credential, and a new
2962 * credential is created.
2964 * This should perhaps be better optimized, but it is considered
2965 * to be the caller's problem.
2968 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
2971 struct ucred temp_cred
;
2973 NULLCRED_CHECK(cred
);
2976 * We don't need to do anything if the given list of groups does not
2979 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
2980 for (i
= 0; i
< groupcount
; i
++) {
2981 if (cred
->cr_groups
[i
] != groups
[i
])
2984 if (i
== groupcount
) {
2985 /* no change needed */
2991 * Look up in cred hash table to see if we have a matching credential
2992 * with new values. If we are setting or clearing the gmuid, then
2993 * update the cr_flags, since clearing it is sticky. This permits an
2994 * opt-out of memberd processing using setgroups(), and an opt-in
2995 * using initgroups(). This is required for POSIX conformance.
2997 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2998 temp_cred
.cr_ngroups
= groupcount
;
2999 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
3000 temp_cred
.cr_gmuid
= gmuid
;
3001 if (gmuid
== KAUTH_UID_NONE
)
3002 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3004 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3006 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3011 * kauth_cred_setuidgid
3013 * Description: Update the given credential using the UID and GID arguments.
3014 * The given UID is used to set the effective UID, real UID, and
3015 * saved UID. The given GID is used to set the effective GID,
3016 * real GID, and saved GID.
3018 * Parameters: cred The original credential
3019 * uid The new UID to use
3020 * gid The new GID to use
3022 * Returns: (kauth_cred_t) The updated credential
3024 * Notes: We set the gmuid to uid if the credential we are inheriting
3025 * from has not opted out of memberd participation; otherwise
3026 * we set it to KAUTH_UID_NONE
3028 * This code is only ever called from the per-thread credential
3029 * code path in the "set per thread credential" case; and in
3030 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3033 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3034 * if it returns a credential other than the one it is passed,
3035 * will have dropped the reference on the passed credential. All
3036 * callers should be aware of this, and treat this function as an
3037 * unref + ref, potentially on different credentials.
3039 * Because of this, the caller is expected to take its own
3040 * reference on the credential passed as the first parameter,
3041 * and be prepared to release the reference on the credential
3042 * that is returned to them, if it is not intended to be a
3043 * persistent reference.
3046 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3048 struct ucred temp_cred
;
3050 NULLCRED_CHECK(cred
);
3053 * We don't need to do anything if the effective, real and saved
3054 * user IDs are already the same as the user ID passed into us.
3056 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
3057 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
3058 /* no change needed */
3063 * Look up in cred hash table to see if we have a matching credential
3064 * with the new values.
3066 bzero(&temp_cred
, sizeof(temp_cred
));
3067 temp_cred
.cr_uid
= uid
;
3068 temp_cred
.cr_ruid
= uid
;
3069 temp_cred
.cr_svuid
= uid
;
3070 /* inherit the opt-out of memberd */
3071 if (cred
->cr_flags
& CRF_NOMEMBERD
) {
3072 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3073 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3075 temp_cred
.cr_gmuid
= uid
;
3076 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3078 temp_cred
.cr_ngroups
= 1;
3079 /* displacing a supplementary group opts us out of memberd */
3080 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3081 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3082 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3084 temp_cred
.cr_rgid
= gid
;
3085 temp_cred
.cr_svgid
= gid
;
3087 temp_cred
.cr_label
= cred
->cr_label
;
3090 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3095 * kauth_cred_setsvuidgid
3097 * Description: Function used by execve to set the saved uid and gid values
3098 * for suid/sgid programs
3100 * Parameters: cred The credential to update
3101 * uid The saved uid to set
3102 * gid The saved gid to set
3104 * Returns: (kauth_cred_t) The updated credential
3106 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3107 * if it returns a credential other than the one it is passed,
3108 * will have dropped the reference on the passed credential. All
3109 * callers should be aware of this, and treat this function as an
3110 * unref + ref, potentially on different credentials.
3112 * Because of this, the caller is expected to take its own
3113 * reference on the credential passed as the first parameter,
3114 * and be prepared to release the reference on the credential
3115 * that is returned to them, if it is not intended to be a
3116 * persistent reference.
3119 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3121 struct ucred temp_cred
;
3123 NULLCRED_CHECK(cred
);
3124 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
3127 * We don't need to do anything if the effective, real and saved
3128 * uids are already the same as the uid provided. This check is
3129 * likely insufficient.
3131 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
3132 /* no change needed */
3135 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3137 /* look up in cred hash table to see if we have a matching credential
3140 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3141 temp_cred
.cr_svuid
= uid
;
3142 temp_cred
.cr_svgid
= gid
;
3144 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3149 * kauth_cred_setauditinfo
3151 * Description: Update the given credential using the given auditinfo_t.
3153 * Parameters: cred The original credential
3154 * auditinfo_p Pointer to ne audit information
3156 * Returns: (kauth_cred_t) The updated credential
3158 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3159 * if it returns a credential other than the one it is passed,
3160 * will have dropped the reference on the passed credential. All
3161 * callers should be aware of this, and treat this function as an
3162 * unref + ref, potentially on different credentials.
3164 * Because of this, the caller is expected to take its own
3165 * reference on the credential passed as the first parameter,
3166 * and be prepared to release the reference on the credential
3167 * that is returned to them, if it is not intended to be a
3168 * persistent reference.
3171 kauth_cred_setauditinfo(kauth_cred_t cred
, auditinfo_t
*auditinfo_p
)
3173 struct ucred temp_cred
;
3175 NULLCRED_CHECK(cred
);
3178 * We don't need to do anything if the audit info is already the
3179 * same as the audit info in the credential provided.
3181 if (bcmp(&cred
->cr_au
, auditinfo_p
, sizeof(cred
->cr_au
)) == 0) {
3182 /* no change needed */
3186 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3187 bcopy(auditinfo_p
, &temp_cred
.cr_au
, sizeof(temp_cred
.cr_au
));
3189 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
3194 * kauth_cred_label_update
3196 * Description: Update the MAC label associated with a credential
3198 * Parameters: cred The original credential
3199 * label The MAC label to set
3201 * Returns: (kauth_cred_t) The updated credential
3203 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3204 * if it returns a credential other than the one it is passed,
3205 * will have dropped the reference on the passed credential. All
3206 * callers should be aware of this, and treat this function as an
3207 * unref + ref, potentially on different credentials.
3209 * Because of this, the caller is expected to take its own
3210 * reference on the credential passed as the first parameter,
3211 * and be prepared to release the reference on the credential
3212 * that is returned to them, if it is not intended to be a
3213 * persistent reference.
3216 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
3218 kauth_cred_t newcred
;
3219 struct ucred temp_cred
;
3221 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3223 mac_cred_label_init(&temp_cred
);
3224 mac_cred_label_associate(cred
, &temp_cred
);
3225 mac_cred_label_update(&temp_cred
, label
);
3227 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3228 mac_cred_label_destroy(&temp_cred
);
3233 * kauth_cred_label_update_execve
3235 * Description: Update the MAC label associated with a credential as
3238 * Parameters: cred The original credential
3240 * scriptl The script MAC label
3241 * execl The executable MAC label
3242 * disjointp Pointer to flag to set if old
3243 * and returned credentials are
3246 * Returns: (kauth_cred_t) The updated credential
3249 * *disjointp Set to 1 for disjoint creds
3251 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3252 * if it returns a credential other than the one it is passed,
3253 * will have dropped the reference on the passed credential. All
3254 * callers should be aware of this, and treat this function as an
3255 * unref + ref, potentially on different credentials.
3257 * Because of this, the caller is expected to take its own
3258 * reference on the credential passed as the first parameter,
3259 * and be prepared to release the reference on the credential
3260 * that is returned to them, if it is not intended to be a
3261 * persistent reference.
3265 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
3266 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
3269 kauth_cred_t newcred
;
3270 struct ucred temp_cred
;
3272 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3274 mac_cred_label_init(&temp_cred
);
3275 mac_cred_label_associate(cred
, &temp_cred
);
3276 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
3277 vp
, scriptl
, execl
);
3279 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3280 mac_cred_label_destroy(&temp_cred
);
3285 * kauth_proc_label_update
3287 * Description: Update the label inside the credential associated with the process.
3289 * Parameters: p The process to modify
3290 * label The label to place in the process credential
3292 * Notes: The credential associated with the process may change as a result
3293 * of this call. The caller should not assume the process reference to
3294 * the old credential still exists.
3296 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
3298 kauth_cred_t my_cred
, my_new_cred
;
3300 my_cred
= kauth_cred_proc_ref(p
);
3302 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
3304 /* get current credential and take a reference while we muck with it */
3308 * Set the credential with new info. If there is no change,
3309 * we get back the same credential we passed in; if there is
3310 * a change, we drop the reference on the credential we
3311 * passed in. The subsequent compare is safe, because it is
3312 * a pointer compare rather than a contents compare.
3314 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
3315 if (my_cred
!= my_new_cred
) {
3317 DEBUG_CRED_CHANGE("kauth_proc_setlabel_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p
->p_pid
, my_cred
, my_cred
->cr_flags
, my_new_cred
, my_new_cred
->cr_flags
);
3321 * We need to protect for a race where another thread
3322 * also changed the credential after we took our
3323 * reference. If p_ucred has changed then we should
3324 * restart this again with the new cred.
3326 if (p
->p_ucred
!= my_cred
) {
3328 kauth_cred_unref(&my_new_cred
);
3329 my_cred
= kauth_cred_proc_ref(p
);
3333 p
->p_ucred
= my_new_cred
;
3334 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3339 /* Drop old proc reference or our extra reference */
3340 kauth_cred_unref(&my_cred
);
3346 * kauth_proc_label_update_execve
3348 * Description: Update the label inside the credential associated with the
3349 * process as part of a transitioning execve. The label will
3350 * be updated by the policies as part of this processing, not
3351 * provided up front.
3353 * Parameters: p The process to modify
3354 * ctx The context of the exec
3355 * vp The vnode being exec'ed
3356 * scriptl The script MAC label
3357 * execl The executable MAC label
3359 * Returns: 0 Label update did not make credential
3361 * 1 Label update caused credential to be
3364 * Notes: The credential associated with the process WILL change as a
3365 * result of this call. The caller should not assume the process
3366 * reference to the old credential still exists.
3369 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
3370 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
3372 kauth_cred_t my_cred
, my_new_cred
;
3375 my_cred
= kauth_cred_proc_ref(p
);
3377 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
3379 /* get current credential and take a reference while we muck with it */
3383 * Set the credential with new info. If there is no change,
3384 * we get back the same credential we passed in; if there is
3385 * a change, we drop the reference on the credential we
3386 * passed in. The subsequent compare is safe, because it is
3387 * a pointer compare rather than a contents compare.
3389 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
3390 if (my_cred
!= my_new_cred
) {
3392 DEBUG_CRED_CHANGE("kauth_proc_label_update_execve_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p
->p_pid
, my_cred
, my_cred
->cr_flags
, my_new_cred
, my_new_cred
->cr_flags
);
3396 * We need to protect for a race where another thread
3397 * also changed the credential after we took our
3398 * reference. If p_ucred has changed then we should
3399 * restart this again with the new cred.
3401 if (p
->p_ucred
!= my_cred
) {
3403 kauth_cred_unref(&my_new_cred
);
3404 my_cred
= kauth_cred_proc_ref(p
);
3408 p
->p_ucred
= my_new_cred
;
3409 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3414 /* Drop old proc reference or our extra reference */
3415 kauth_cred_unref(&my_cred
);
3422 * for temporary binary compatibility
3424 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
3426 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
3428 return kauth_cred_label_update(cred
, label
);
3431 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
3433 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
3435 return kauth_proc_label_update(p
, label
);
3441 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
3442 * Since we cannot build our export lists based on the kernel configuration we need
3446 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
3452 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
3459 * for temporary binary compatibility
3461 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
3463 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
3468 int kauth_proc_setlabel(struct proc
*p
, void *label
);
3470 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
3480 * Description: Add a reference to the passed credential
3482 * Parameters: cred The credential to reference
3486 * Notes: This function adds a reference to the provided credential;
3487 * the existing reference on the credential is assumed to be
3488 * held stable over this operation by taking the appropriate
3489 * lock to protect the pointer from which it is being referenced,
3490 * if necessary (e.g. the proc lock is held over the call if the
3491 * credential being referenced is from p_ucred, the vnode lock
3492 * if from the per vnode name cache cred cache, and so on).
3494 * This is safe from the kauth_cred_unref() path, since an atomic
3495 * add is used, and the unref path specifically checks to see that
3496 * the value has not been changed to add a reference between the
3497 * time the credential is unreferenced by another pointer and the
3498 * time it is unreferenced from the cred hash cache.
3501 kauth_cred_ref(kauth_cred_t cred
)
3505 NULLCRED_CHECK(cred
);
3507 // XXX SInt32 not safe for an LP64 kernel
3508 old_value
= OSAddAtomic(1, (SInt32
*)&cred
->cr_ref
);
3511 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
3513 #if 0 // use this to watch a specific credential
3514 if ( is_target_cred( cred
) != 0 ) {
3524 * kauth_cred_unref_hashlocked
3526 * Description: release a credential reference; when the last reference is
3527 * released, the credential will be freed.
3529 * Parameters: credp Pointer to address containing
3530 * credential to be freed
3535 * *credp Set to NOCRED
3537 * Notes: This function assumes the credential hash lock is held.
3539 * This function is internal use only, since the hash lock is
3540 * scoped to this compilation unit.
3542 * This function destroys the contents of the pointer passed by
3543 * the caller to prevent the caller accidently attempting to
3544 * release a given reference twice in error.
3546 * The last reference is considered to be released when a release
3547 * of a credential of a reference count of 2 occurs; this is an
3548 * intended effect, to take into accout the reference held by
3549 * the credential hash, which is released at the same time.
3552 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
3556 KAUTH_CRED_HASH_LOCK_ASSERT();
3557 NULLCRED_CHECK(*credp
);
3559 // XXX SInt32 not safe for an LP64 kernel
3560 old_value
= OSAddAtomic(-1, (SInt32
*)&(*credp
)->cr_ref
);
3564 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
3566 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
3569 #if 0 // use this to watch a specific credential
3570 if ( is_target_cred( *credp
) != 0 ) {
3576 * If the old_value is 2, then we have just released the last external
3577 * reference to this credential
3579 if (old_value
< 3) {
3580 /* The last absolute reference is our credential hash table */
3581 kauth_cred_remove(*credp
);
3590 * Description: Release a credential reference while holding the credential
3591 * hash lock; when the last reference is released, the credential
3594 * Parameters: credp Pointer to address containing
3595 * credential to be freed
3600 * *credp Set to NOCRED
3602 * Notes: See kauth_cred_unref_hashlocked() for more information.
3606 kauth_cred_unref(kauth_cred_t
*credp
)
3608 KAUTH_CRED_HASH_LOCK();
3609 kauth_cred_unref_hashlocked(credp
);
3610 KAUTH_CRED_HASH_UNLOCK();
3617 * Description: release a credential reference; when the last reference is
3618 * released, the credential will be freed
3620 * Parameters: cred Credential to release
3624 * DEPRECATED: This interface is obsolete due to a failure to clear out the
3625 * clear the pointer in the caller to avoid multiple releases of
3626 * the same credential. The currently recommended interface is
3627 * kauth_cred_unref().
3630 kauth_cred_rele(kauth_cred_t cred
)
3632 kauth_cred_unref(&cred
);
3639 * Description: Duplicate a credential via alloc and copy; the new credential
3642 * Parameters: cred The credential to duplicate
3644 * Returns: (kauth_cred_t) The duplicate credential
3646 * Notes: The typical value to calling this routine is if you are going
3647 * to modify an existing credential, and expect to need a new one
3648 * from the hash cache.
3650 * This should probably not be used in the majority of cases;
3651 * if you are using it instead of kauth_cred_create(), you are
3652 * likely making a mistake.
3654 * The newly allocated credential is copied as part of the
3655 * allocation process, with the exception of the reference
3656 * count, which is set to 1 to indicate a single reference
3657 * held by the caller.
3659 * Since newly allocated credentials have no external pointers
3660 * referencing them, prior to making them visible in an externally
3661 * visible pointer (e.g. by adding them to the credential hash
3662 * cache) is the only legal time in which an existing credential
3663 * can be safely iinitialized or modified directly.
3665 * After initialization, the caller is expected to call the
3666 * function kauth_cred_add() to add the credential to the hash
3667 * cache, after which time it's frozen and becomes publically
3670 * The release protocol depends on kauth_hash_add() being called
3671 * before kauth_cred_rele() (there is a diagnostic panic which
3672 * will trigger if this protocol is not observed).
3676 kauth_cred_dup(kauth_cred_t cred
)
3678 kauth_cred_t newcred
;
3680 struct label
*temp_label
;
3684 if (cred
== NOCRED
|| cred
== FSCRED
)
3685 panic("kauth_cred_dup: bad credential");
3687 newcred
= kauth_cred_alloc();
3688 if (newcred
!= NULL
) {
3690 temp_label
= newcred
->cr_label
;
3692 bcopy(cred
, newcred
, sizeof(*newcred
));
3694 newcred
->cr_label
= temp_label
;
3695 mac_cred_label_associate(cred
, newcred
);
3697 newcred
->cr_ref
= 1;
3703 * kauth_cred_copy_real
3705 * Description: Returns a credential based on the passed credential but which
3706 * reflects the real rather than effective UID and GID.
3708 * Parameters: cred The credential from which to
3709 * derive the new credential
3711 * Returns: (kauth_cred_t) The copied credential
3713 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
3714 * result, the caller is responsible for dropping BOTH the
3715 * additional reference on the passed cred (if any), and the
3716 * credential returned by this function. The drop should be
3717 * via the satnadr kauth_cred_unref() KPI.
3720 kauth_cred_copy_real(kauth_cred_t cred
)
3722 kauth_cred_t newcred
= NULL
, found_cred
;
3723 struct ucred temp_cred
;
3725 /* if the credential is already 'real', just take a reference */
3726 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
3727 (cred
->cr_rgid
== cred
->cr_gid
)) {
3728 kauth_cred_ref(cred
);
3733 * Look up in cred hash table to see if we have a matching credential
3734 * with the new values.
3736 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3737 temp_cred
.cr_uid
= cred
->cr_ruid
;
3738 /* displacing a supplementary group opts us out of memberd */
3739 if (kauth_cred_change_egid(&temp_cred
, cred
->cr_rgid
)) {
3740 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3741 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3744 * If the cred is not opted out, make sure we are using the r/euid
3747 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
3748 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
3753 KAUTH_CRED_HASH_LOCK();
3754 found_cred
= kauth_cred_find(&temp_cred
);
3755 if (found_cred
== cred
) {
3756 /* same cred so just bail */
3757 KAUTH_CRED_HASH_UNLOCK();
3760 if (found_cred
!= NULL
) {
3762 * Found a match so we bump reference count on new
3763 * one. We leave the old one alone.
3765 kauth_cred_ref(found_cred
);
3766 KAUTH_CRED_HASH_UNLOCK();
3771 * Must allocate a new credential, copy in old credential
3772 * data and update the real user and group IDs.
3774 newcred
= kauth_cred_dup(&temp_cred
);
3775 err
= kauth_cred_add(newcred
);
3776 KAUTH_CRED_HASH_UNLOCK();
3778 /* Retry if kauth_cred_add() fails */
3782 mac_cred_label_destroy(newcred
);
3784 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
3795 * Description: Common code to update a credential
3797 * Parameters: old_cred Reference counted credential
3799 * model_cred Non-reference counted model
3800 * credential to apply to the
3801 * credential to be updated
3802 * retain_auditinfo Flag as to whether or not the
3803 * audit information should be
3804 * copied from the old_cred into
3807 * Returns: (kauth_cred_t) The updated credential
3809 * IMPORTANT: This function will potentially return a credential other than
3810 * the one it is passed, and if so, it will have dropped the
3811 * reference on the passed credential. All callers should be
3812 * aware of this, and treat this function as an unref + ref,
3813 * potentially on different credentials.
3815 * Because of this, the caller is expected to take its own
3816 * reference on the credential passed as the first parameter,
3817 * and be prepared to release the reference on the credential
3818 * that is returned to them, if it is not intended to be a
3819 * persistent reference.
3822 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
3823 boolean_t retain_auditinfo
)
3825 kauth_cred_t found_cred
, new_cred
= NULL
;
3828 * Make sure we carry the auditinfo forward to the new credential
3829 * unless we are actually updating the auditinfo.
3831 if (retain_auditinfo
)
3832 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
, sizeof(model_cred
->cr_au
));
3837 KAUTH_CRED_HASH_LOCK();
3838 found_cred
= kauth_cred_find(model_cred
);
3839 if (found_cred
== old_cred
) {
3840 /* same cred so just bail */
3841 KAUTH_CRED_HASH_UNLOCK();
3844 if (found_cred
!= NULL
) {
3845 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
3847 * Found a match so we bump reference count on new
3848 * one and decrement reference count on the old one.
3850 kauth_cred_ref(found_cred
);
3851 kauth_cred_unref_hashlocked(&old_cred
);
3852 KAUTH_CRED_HASH_UNLOCK();
3857 * Must allocate a new credential using the model. also
3858 * adds the new credential to the credential hash table.
3860 new_cred
= kauth_cred_dup(model_cred
);
3861 err
= kauth_cred_add(new_cred
);
3862 KAUTH_CRED_HASH_UNLOCK();
3864 /* retry if kauth_cred_add returns non zero value */
3868 mac_cred_label_destroy(new_cred
);
3870 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3874 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
3875 kauth_cred_unref(&old_cred
);
3883 * Description: Add the given credential to our credential hash table and
3884 * take an additional reference to account for our use of the
3885 * credential in the hash table
3887 * Parameters: new_cred Credential to insert into cred
3890 * Returns: 0 Success
3891 * -1 Hash insertion failed: caller
3894 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3896 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
3899 kauth_cred_add(kauth_cred_t new_cred
)
3903 KAUTH_CRED_HASH_LOCK_ASSERT();
3905 hash_key
= kauth_cred_get_hashkey(new_cred
);
3906 hash_key
%= kauth_cred_table_size
;
3908 /* race fix - there is a window where another matching credential
3909 * could have been inserted between the time this one was created and we
3910 * got the hash lock. If we find a match return an error and have the
3913 if (kauth_cred_find(new_cred
) != NULL
) {
3917 /* take a reference for our use in credential hash table */
3918 kauth_cred_ref(new_cred
);
3920 /* insert the credential into the hash table */
3921 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
3930 * Description: Remove the given credential from our credential hash table
3932 * Parameters: cred Credential to remove from cred
3937 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3939 * Notes: The check for the reference increment after entry is generally
3940 * agree to be safe, since we use atomic operations, and the
3941 * following code occurs with the hash lock held; in theory, this
3942 * protects us from the 2->1 reference that gets us here.
3945 kauth_cred_remove(kauth_cred_t cred
)
3948 kauth_cred_t found_cred
;
3950 hash_key
= kauth_cred_get_hashkey(cred
);
3951 hash_key
%= kauth_cred_table_size
;
3954 if (cred
->cr_ref
< 1)
3955 panic("cred reference underflow");
3956 if (cred
->cr_ref
> 1)
3957 return; /* someone else got a ref */
3959 /* Find cred in the credential hash table */
3960 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
3961 if (found_cred
== cred
) {
3962 /* found a match, remove it from the hash table */
3963 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
3965 mac_cred_label_destroy(cred
);
3968 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
3969 #if KAUTH_CRED_HASH_DEBUG
3976 /* Did not find a match... this should not happen! XXX Make panic? */
3977 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
3985 * Description: Using the given credential data, look for a match in our
3986 * credential hash table
3988 * Parameters: cred Credential to lookup in cred
3991 * Returns: NULL Not found
3992 * !NULL Matching cedential already in
3995 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3998 kauth_cred_find(kauth_cred_t cred
)
4001 kauth_cred_t found_cred
;
4003 KAUTH_CRED_HASH_LOCK_ASSERT();
4005 #if KAUTH_CRED_HASH_DEBUG
4006 static int test_count
= 0;
4009 if ((test_count
% 200) == 0) {
4010 kauth_cred_hash_print();
4014 hash_key
= kauth_cred_get_hashkey(cred
);
4015 hash_key
%= kauth_cred_table_size
;
4017 /* Find cred in the credential hash table */
4018 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4022 * don't worry about the label unless the flags in
4023 * either credential tell us to.
4025 if ((found_cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0 ||
4026 (cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) {
4027 /* include the label pointer in the compare */
4028 match
= (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4029 (sizeof(struct ucred
) -
4030 offsetof(struct ucred
, cr_uid
))) == 0);
4032 /* flags have to match, but skip the label in bcmp */
4033 match
= (found_cred
->cr_flags
== cred
->cr_flags
&&
4034 bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4035 (offsetof(struct ucred
, cr_label
) -
4036 offsetof(struct ucred
, cr_uid
))) == 0);
4043 /* No match found */
4052 * Description: Generates a hash key using data that makes up a credential;
4055 * Parameters: datap Pointer to data to hash
4056 * data_len Count of bytes to hash
4057 * start_key Start key value
4059 * Returns: (u_long) Returned hash key
4061 static inline u_long
4062 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4064 u_long hash_key
= start_key
;
4067 while (data_len
> 0) {
4068 hash_key
= (hash_key
<< 4) + *datap
++;
4069 temp
= hash_key
& 0xF0000000;
4071 hash_key
^= temp
>> 24;
4081 * kauth_cred_get_hashkey
4083 * Description: Generate a hash key using data that makes up a credential;
4084 * based on ElfHash. We hash on the entire credential data,
4085 * not including the ref count or the TAILQ, which are mutable;
4086 * everything else isn't.
4088 * We also avoid the label (if the flag is not set saying the
4089 * label is actually enforced).
4091 * Parameters: cred Credential for which hash is
4094 * Returns: (u_long) Returned hash key
4097 kauth_cred_get_hashkey(kauth_cred_t cred
)
4099 u_long hash_key
= 0;
4101 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
4102 ((cred
->cr_flags
& CRF_MAC_ENFORCE
) ?
4103 sizeof(struct ucred
) : offsetof(struct ucred
, cr_label
)) -
4104 offsetof(struct ucred
, cr_uid
),
4110 #if KAUTH_CRED_HASH_DEBUG
4112 * kauth_cred_hash_print
4114 * Description: Print out cred hash cache table information for debugging
4115 * purposes, including the credential contents
4117 * Parameters: (void)
4121 * Implicit returns: Results in console output
4124 kauth_cred_hash_print(void)
4127 kauth_cred_t found_cred
;
4129 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
4130 /* count slot hits, misses, collisions, and max depth */
4131 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4132 printf("[%02d] ", i
);
4134 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4139 kauth_cred_print(found_cred
);
4143 printf("NOCRED \n");
4147 #endif /* KAUTH_CRED_HASH_DEBUG */
4150 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4154 * Description: Print out an individual credential's contents for debugging
4157 * Parameters: cred The credential to print out
4161 * Implicit returns: Results in console output
4164 kauth_cred_print(kauth_cred_t cred
)
4168 printf("%p - refs %lu flags 0x%08x uids e%d r%d sv%d gm%d ", cred
, cred
->cr_ref
, cred
->cr_flags
, cred
->cr_uid
, cred
->cr_ruid
, cred
->cr_svuid
, cred
->cr_gmuid
);
4169 printf("group count %d gids ", cred
->cr_ngroups
);
4170 for (i
= 0; i
< NGROUPS
; i
++) {
4173 printf("%d ", cred
->cr_groups
[i
]);
4175 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
4176 printf("auditinfo %d %d %d %d %d %d\n",
4177 cred
->cr_au
.ai_auid
, cred
->cr_au
.ai_mask
.am_success
, cred
->cr_au
.ai_mask
.am_failure
,
4178 cred
->cr_au
.ai_termid
.port
, cred
->cr_au
.ai_termid
.machine
, cred
->cr_au
.ai_asid
);
4181 int is_target_cred( kauth_cred_t the_cred
)
4183 if ( the_cred
->cr_uid
!= 0 )
4185 if ( the_cred
->cr_ruid
!= 0 )
4187 if ( the_cred
->cr_svuid
!= 0 )
4189 if ( the_cred
->cr_ngroups
!= 11 )
4191 if ( the_cred
->cr_groups
[0] != 11 )
4193 if ( the_cred
->cr_groups
[1] != 81 )
4195 if ( the_cred
->cr_groups
[2] != 63947 )
4197 if ( the_cred
->cr_groups
[3] != 80288 )
4199 if ( the_cred
->cr_groups
[4] != 89006 )
4201 if ( the_cred
->cr_groups
[5] != 52173 )
4203 if ( the_cred
->cr_groups
[6] != 84524 )
4205 if ( the_cred
->cr_groups
[7] != 79 )
4207 if ( the_cred
->cr_groups
[8] != 80292 )
4209 if ( the_cred
->cr_groups
[9] != 80 )
4211 if ( the_cred
->cr_groups
[10] != 90824 )
4213 if ( the_cred
->cr_rgid
!= 11 )
4215 if ( the_cred
->cr_svgid
!= 11 )
4217 if ( the_cred
->cr_gmuid
!= 3475 )
4219 if ( the_cred
->cr_au
.ai_auid
!= 3475 )
4222 if ( the_cred->cr_au.ai_mask.am_success != 0 )
4224 if ( the_cred->cr_au.ai_mask.am_failure != 0 )
4226 if ( the_cred->cr_au.ai_termid.port != 0 )
4228 if ( the_cred->cr_au.ai_termid.machine != 0 )
4230 if ( the_cred->cr_au.ai_asid != 0 )
4232 if ( the_cred->cr_flags != 0 )
4235 return( -1 ); // found target cred
4238 void get_backtrace( void )
4241 void * my_stack
[ MAX_STACK_DEPTH
];
4244 if ( cred_debug_buf_p
== NULL
) {
4245 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
4246 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
4249 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
4250 /* buffer is full */
4254 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
4255 if ( my_depth
== 0 ) {
4256 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
4260 /* fill new backtrace */
4261 my_slot
= cred_debug_buf_p
->next_slot
;
4262 cred_debug_buf_p
->next_slot
++;
4263 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
4264 for ( i
= 0; i
< my_depth
; i
++ ) {
4265 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
4272 /* subset of struct ucred for use in sysctl_dump_creds */
4273 struct debug_ucred
{
4275 u_long cr_ref
; /* reference count */
4276 uid_t cr_uid
; /* effective user id */
4277 uid_t cr_ruid
; /* real user id */
4278 uid_t cr_svuid
; /* saved user id */
4279 short cr_ngroups
; /* number of groups in advisory list */
4280 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
4281 gid_t cr_rgid
; /* real group id */
4282 gid_t cr_svgid
; /* saved group id */
4283 uid_t cr_gmuid
; /* UID for group membership purposes */
4284 struct auditinfo cr_au
; /* user auditing data */
4285 void *cr_label
; /* MACF label */
4286 int cr_flags
; /* flags on credential */
4288 typedef struct debug_ucred debug_ucred
;
4290 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
4291 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
4294 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
4298 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4300 int i
, j
, counter
= 0;
4303 kauth_cred_t found_cred
;
4304 debug_ucred
* cred_listp
;
4305 debug_ucred
* nextp
;
4307 /* This is a readonly node. */
4308 if (req
->newptr
!= USER_ADDR_NULL
)
4311 /* calculate space needed */
4312 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4313 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4318 /* they are querying us so just return the space required. */
4319 if (req
->oldptr
== USER_ADDR_NULL
) {
4320 counter
+= 10; // add in some padding;
4321 req
->oldidx
= counter
* sizeof(debug_ucred
);
4325 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4326 if ( cred_listp
== NULL
) {
4330 /* fill in creds to send back */
4333 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4334 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4335 nextp
->credp
= found_cred
;
4336 nextp
->cr_ref
= found_cred
->cr_ref
;
4337 nextp
->cr_uid
= found_cred
->cr_uid
;
4338 nextp
->cr_ruid
= found_cred
->cr_ruid
;
4339 nextp
->cr_svuid
= found_cred
->cr_svuid
;
4340 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
4341 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
4342 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
4344 nextp
->cr_rgid
= found_cred
->cr_rgid
;
4345 nextp
->cr_svgid
= found_cred
->cr_svgid
;
4346 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
4347 nextp
->cr_au
.ai_auid
= found_cred
->cr_au
.ai_auid
;
4348 nextp
->cr_au
.ai_mask
.am_success
= found_cred
->cr_au
.ai_mask
.am_success
;
4349 nextp
->cr_au
.ai_mask
.am_failure
= found_cred
->cr_au
.ai_mask
.am_failure
;
4350 nextp
->cr_au
.ai_termid
.port
= found_cred
->cr_au
.ai_termid
.port
;
4351 nextp
->cr_au
.ai_termid
.machine
= found_cred
->cr_au
.ai_termid
.machine
;
4352 nextp
->cr_au
.ai_asid
= found_cred
->cr_au
.ai_asid
;
4353 nextp
->cr_label
= found_cred
->cr_label
;
4354 nextp
->cr_flags
= found_cred
->cr_flags
;
4356 space
+= sizeof(debug_ucred
);
4357 if ( space
> req
->oldlen
) {
4358 FREE(cred_listp
, M_TEMP
);
4363 req
->oldlen
= space
;
4364 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
4365 FREE(cred_listp
, M_TEMP
);
4370 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
4371 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
4374 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
4378 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4383 cred_debug_buffer
* bt_bufp
;
4384 cred_backtrace
* nextp
;
4386 /* This is a readonly node. */
4387 if (req
->newptr
!= USER_ADDR_NULL
)
4390 if ( cred_debug_buf_p
== NULL
) {
4394 /* calculate space needed */
4395 space
= sizeof( cred_debug_buf_p
->next_slot
);
4396 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
4398 /* they are querying us so just return the space required. */
4399 if (req
->oldptr
== USER_ADDR_NULL
) {
4400 req
->oldidx
= space
;
4404 if ( space
> req
->oldlen
) {
4408 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4409 if ( bt_bufp
== NULL
) {
4413 /* fill in backtrace info to send back */
4414 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
4415 space
= sizeof(bt_bufp
->next_slot
);
4417 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
4418 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
4419 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
4420 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
4421 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
4423 space
+= sizeof(*nextp
);
4426 req
->oldlen
= space
;
4427 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
4428 FREE(bt_bufp
, M_TEMP
);
4432 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */