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.
40 #include <sys/param.h> /* XXX trim includes */
42 #include <sys/systm.h>
43 #include <sys/ucred.h>
44 #include <sys/proc_internal.h>
46 #include <sys/timeb.h>
47 #include <sys/times.h>
48 #include <sys/malloc.h>
49 #include <sys/kauth.h>
50 #include <sys/kernel.h>
52 #include <security/audit/audit.h>
54 #include <sys/mount.h>
55 #include <sys/sysproto.h>
56 #include <sys/kern_callout.h>
57 #include <mach/message.h>
58 #include <mach/host_security.h>
60 /* mach_absolute_time() */
61 #include <mach/clock_types.h>
62 #include <mach/mach_types.h>
63 #include <mach/mach_time.h>
65 #include <libkern/OSAtomic.h>
67 #include <kern/task.h>
68 #include <kern/lock.h>
72 #define MACH_ASSERT 1 /* XXX so bogus */
73 #include <kern/assert.h>
76 #include <security/mac.h>
77 #include <security/mac_framework.h>
80 void mach_kauth_cred_uthread_update( void );
82 #define CRED_DIAGNOSTIC 0
84 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
87 * Credential debugging; we can track entry into a function that might
88 * change a credential, and we can track actual credential changes that
91 * Note: Does *NOT* currently include per-thread credential changes
95 #define DEBUG_CRED_ENTER printf
96 #define DEBUG_CRED_CHANGE printf
97 extern void kauth_cred_print(kauth_cred_t cred
);
99 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
101 int is_target_cred( kauth_cred_t the_cred
);
102 void get_backtrace( void );
104 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
105 __unused
int arg2
, struct sysctl_req
*req
);
107 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
108 __unused
int arg2
, struct sysctl_req
*req
);
110 #define MAX_STACK_DEPTH 8
111 struct cred_backtrace
{
113 void * stack
[ MAX_STACK_DEPTH
];
115 typedef struct cred_backtrace cred_backtrace
;
117 #define MAX_CRED_BUFFER_SLOTS 200
118 struct cred_debug_buffer
{
120 cred_backtrace stack_buffer
[ MAX_CRED_BUFFER_SLOTS
];
122 typedef struct cred_debug_buffer cred_debug_buffer
;
123 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
125 #else /* !DEBUG_CRED */
127 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
128 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
130 #endif /* !DEBUG_CRED */
133 * Interface to external identity resolver.
135 * The architecture of the interface is simple; the external resolver calls
136 * in to get work, then calls back with completed work. It also calls us
137 * to let us know that it's (re)started, so that we can resubmit work if it
141 static lck_mtx_t
*kauth_resolver_mtx
;
142 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
143 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
145 static volatile pid_t kauth_resolver_identity
;
146 static int kauth_resolver_registered
;
147 static uint32_t kauth_resolver_sequence
;
149 struct kauth_resolver_work
{
150 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
151 struct kauth_identity_extlookup kr_work
;
153 uint64_t kr_subtime
; /* submission time */
156 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
157 #define KAUTH_REQUEST_SUBMITTED (1<<1)
158 #define KAUTH_REQUEST_DONE (1<<2)
162 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
163 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
164 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
166 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
167 static int kauth_resolver_complete(user_addr_t message
);
168 static int kauth_resolver_getwork(user_addr_t message
);
169 static int kauth_resolver_getwork2(user_addr_t message
);
171 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
172 static int kauth_cred_primes_index
= 0;
173 static int kauth_cred_table_size
= 0;
175 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
176 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
178 /* Weighted moving average for resolver response time */
179 static struct kco_moving_average resolver_ma
;
181 #define KAUTH_CRED_HASH_DEBUG 0
183 static int kauth_cred_add(kauth_cred_t new_cred
);
184 static void kauth_cred_remove(kauth_cred_t cred
);
185 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
186 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
187 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
188 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
190 #if KAUTH_CRED_HASH_DEBUG
191 static int kauth_cred_count
= 0;
192 static void kauth_cred_hash_print(void);
193 static void kauth_cred_print(kauth_cred_t cred
);
198 * kauth_resolver_init
200 * Description: Initialize the daemon side of the credential identity resolver
206 * Notes: Intialize the credential identity resolver for use; the
207 * credential identity resolver is the KPI used by the user
208 * space credential identity resolver daemon to communicate
209 * with the kernel via the identitysvc() system call..
211 * This is how membership in more than 16 groups (1 effective
212 * and 15 supplementary) is supported, and also how UID's,
213 * UUID's, and so on, are translated to/from POSIX credential
216 * The credential identity resolver operates by attempting to
217 * determine identity first from the credential, then from
218 * the kernel credential identity cache, and finally by
219 * enqueueing a request to a user space daemon.
221 * This function is called from kauth_init() in the file
222 * kern_authorization.c.
225 kauth_resolver_init(void)
227 TAILQ_INIT(&kauth_resolver_unsubmitted
);
228 TAILQ_INIT(&kauth_resolver_submitted
);
229 TAILQ_INIT(&kauth_resolver_done
);
230 kauth_resolver_sequence
= 31337;
231 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
234 * 110% of average response time is "too long" and should be reported
236 kco_ma_init(&resolver_ma
, 110, KCO_MA_F_WMA
);
241 * kauth_resolver_submit
243 * Description: Submit an external credential identity resolution request to
244 * the user space daemon.
246 * Parameters: lkp A pointer to an external
250 * EWOULDBLOCK No resolver registered
251 * EINTR Operation interrupted (e.g. by
253 * ENOMEM Could not allocate work item
254 * ??? An error from the user space
257 * Notes: Allocate a work queue entry, submit the work and wait for
258 * the operation to either complete or time out. Outstanding
259 * operations may also be cancelled.
262 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
264 struct kauth_resolver_work
*workp
, *killp
;
266 int error
, shouldfree
;
269 /* no point actually blocking if the resolver isn't up yet */
270 if (kauth_resolver_identity
== 0) {
272 * We've already waited an initial 30 seconds with no result.
273 * Sleep on a stack address so no one wakes us before timeout;
274 * we sleep a half a second in case we are a high priority
275 * process, so that memberd doesn't starve while we are in a
276 * tight loop between user and kernel, eating all the CPU.
278 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
279 if (kauth_resolver_identity
== 0) {
281 * if things haven't changed while we were asleep,
282 * tell the caller we couldn't get an authoritative
289 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
293 workp
->kr_work
= *lkp
;
295 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
296 workp
->kr_result
= 0;
299 * We insert the request onto the unsubmitted queue, the call in from
300 * the resolver will it to the submitted thread when appropriate.
302 KAUTH_RESOLVER_LOCK();
303 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
304 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
307 * XXX As an optimisation, we could check the queue for identical
308 * XXX items and coalesce them
310 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
312 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
314 /* we could compute a better timeout here */
317 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
318 /* request has been completed? */
319 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
321 /* woken because the resolver has died? */
322 if (kauth_resolver_identity
== 0) {
332 * Update the moving average of how long it took; if it took longer
333 * than the time threshold, then we complain about it being slow.
335 duration
= mach_absolute_time() - workp
->kr_subtime
;
336 if (kco_ma_addsample(&resolver_ma
, duration
)) {
338 uint64_t old_average
;
342 /* If we can't get information, don't log anything */
343 if (kco_ma_info(&resolver_ma
, KCO_MA_F_WMA
, &average
, &old_average
, &threshold
, &count
)) {
344 char pname
[MAXCOMLEN
+1] = "(NULL)";
345 proc_name(kauth_resolver_identity
, pname
, sizeof(pname
));
346 // <rdar://6276265> printf("kauth_resolver_submit: External resolver pid %d (name %s) response time %lld, average %lld new %lld threshold %d%% actual %d%% count %d\n", kauth_resolver_identity, pname, duration, old_average, average, threshold, (int)((duration * 100) / old_average), count);
350 /* if the request was processed, copy the result */
352 *lkp
= workp
->kr_work
;
355 * If the request timed out and was never collected, the resolver
356 * is dead and probably not coming back anytime soon. In this
357 * case we revert to no-resolver behaviour, and punt all the other
358 * sleeping requests to clear the backlog.
360 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
361 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
362 kauth_resolver_identity
= 0;
363 /* kill all the other requestes that are waiting as well */
364 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
366 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
371 * drop our reference on the work item, and note whether we should
374 if (--workp
->kr_refs
<= 0) {
375 /* work out which list we have to remove it from */
376 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
377 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
378 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
379 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
380 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
381 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
383 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
387 /* someone else still has a reference on this request */
390 /* collect request result */
392 error
= workp
->kr_result
;
393 KAUTH_RESOLVER_UNLOCK();
395 * If we dropped the last reference, free the request.
398 FREE(workp
, M_KAUTH
);
400 KAUTH_DEBUG("RESOLVER - returning %d", error
);
408 * Description: System call interface for the external identity resolver.
410 * Parameters: uap->message Message from daemon to kernel
412 * Returns: 0 Successfully became resolver
413 * EPERM Not the resolver process
414 * kauth_authorize_generic:EPERM Not root user
415 * kauth_resolver_complete:EIO
416 * kauth_resolver_complete:EFAULT
417 * kauth_resolver_getwork:EINTR
418 * kauth_resolver_getwork:EFAULT
420 * Notes: This system call blocks until there is work enqueued, at
421 * which time the kernel wakes it up, and a message from the
422 * kernel is copied out to the identity resolution daemon, which
423 * proceed to attempt to resolve it. When the resolution has
424 * completed (successfully or not), the daemon called back into
425 * this system call to give the result to the kernel, and wait
426 * for the next request.
429 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
431 int opcode
= uap
->opcode
;
432 user_addr_t message
= uap
->message
;
433 struct kauth_resolver_work
*workp
;
438 * New server registering itself.
440 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
441 new_id
= current_proc()->p_pid
;
442 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
443 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
446 KAUTH_RESOLVER_LOCK();
447 if (kauth_resolver_identity
!= new_id
) {
448 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
450 * We have a new server, so assume that all the old requests have been lost.
452 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
453 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
454 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
455 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
456 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
458 kauth_resolver_identity
= new_id
;
459 kauth_resolver_registered
= 1;
460 wakeup(&kauth_resolver_unsubmitted
);
462 KAUTH_RESOLVER_UNLOCK();
467 * Beyond this point, we must be the resolver process.
469 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
470 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
474 if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
476 * Terminate outstanding requests; without an authoritative
477 * resolver, we are now back on our own authority.
479 struct kauth_resolver_work
*killp
;
481 KAUTH_RESOLVER_LOCK();
482 kauth_resolver_identity
= 0;
483 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
485 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
487 /* Cause all waiting-for-work threads to return EIO */
488 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
489 KAUTH_RESOLVER_UNLOCK();
493 * Got a result returning?
495 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
496 if ((error
= kauth_resolver_complete(message
)) != 0)
501 * Caller wants to take more work?
503 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
504 if ((error
= kauth_resolver_getwork(message
)) != 0)
513 * kauth_resolver_getwork_continue
515 * Description: Continuation for kauth_resolver_getwork
517 * Parameters: result Error code or 0 for the sleep
518 * that got us to this function
521 * EINTR Interrupted (e.g. by signal)
522 * kauth_resolver_getwork2:EFAULT
524 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
528 kauth_resolver_getwork_continue(int result
)
535 KAUTH_RESOLVER_UNLOCK();
540 * If we lost a race with another thread/memberd restarting, then we
541 * need to go back to sleep to look for more work. If it was memberd
542 * restarting, then the msleep0() will error out here, as our thread
543 * will already be "dead".
545 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
548 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
550 * If this is a wakeup from another thread in the resolver
551 * deregistering it, error out the request-for-work thread
553 if (!kauth_resolver_identity
)
555 KAUTH_RESOLVER_UNLOCK();
559 thread
= current_thread();
560 ut
= get_bsdthread_info(thread
);
561 message
= ut
->uu_kauth
.message
;
562 return(kauth_resolver_getwork2(message
));
567 * kauth_resolver_getwork2
569 * Decription: Common utility function to copy out a identity resolver work
570 * item from the kernel to user space as part of the user space
571 * identity resolver requesting work.
573 * Parameters: message message to user space
576 * EFAULT Bad user space message address
578 * Notes: This common function exists to permit the use of continuations
579 * in the identity resoultion process. This frees up the stack
580 * while we are waiting for the user space resolver to complete
581 * a request. This is specifically used so that our per thread
582 * cost can be small, and we will therefore be willing to run a
583 * larger number of threads in the user space identity resolver.
586 kauth_resolver_getwork2(user_addr_t message
)
588 struct kauth_resolver_work
*workp
;
592 * Note: We depend on the caller protecting us from a NULL work item
593 * queue, since we must have the kauth resolver lock on entry to this
596 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
598 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
599 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
602 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
603 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
604 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
605 workp
->kr_subtime
= mach_absolute_time();
606 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
609 KAUTH_RESOLVER_UNLOCK();
615 * kauth_resolver_getwork
617 * Description: Get a work item from the enqueued requests from the kernel and
618 * give it to the user space daemon.
620 * Parameters: message message to user space
623 * EINTR Interrupted (e.g. by signal)
624 * kauth_resolver_getwork2:EFAULT
626 * Notes: This function blocks in a continuation if there are no work
627 * items available for processing at the time the user space
628 * identity resolution daemon makes a request for work. This
629 * permits a large number of threads to be used by the daemon,
630 * without using a lot of wired kernel memory when there are no
631 * acctual request outstanding.
634 kauth_resolver_getwork(user_addr_t message
)
636 struct kauth_resolver_work
*workp
;
639 KAUTH_RESOLVER_LOCK();
641 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
642 thread_t thread
= current_thread();
643 struct uthread
*ut
= get_bsdthread_info(thread
);
645 ut
->uu_kauth
.message
= message
;
646 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
647 KAUTH_RESOLVER_UNLOCK();
649 * If this is a wakeup from another thread in the resolver
650 * deregistering it, error out the request-for-work thread
652 if (!kauth_resolver_identity
)
656 return kauth_resolver_getwork2(message
);
661 * kauth_resolver_complete
663 * Description: Return a result from userspace.
665 * Parameters: message message from user space
668 * EIO The resolver is dead
669 * copyin:EFAULT Bad message from user space
672 kauth_resolver_complete(user_addr_t message
)
674 struct kauth_identity_extlookup extl
;
675 struct kauth_resolver_work
*workp
;
676 struct kauth_resolver_work
*killp
;
679 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
680 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
684 KAUTH_RESOLVER_LOCK();
688 switch (extl
.el_result
) {
689 case KAUTH_EXTLOOKUP_INPROG
:
693 /* XXX this should go away once memberd is updated */
695 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
701 case KAUTH_EXTLOOKUP_SUCCESS
:
704 case KAUTH_EXTLOOKUP_FATAL
:
705 /* fatal error means the resolver is dead */
706 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
708 * Terminate outstanding requests; without an authoritative
709 * resolver, we are now back on our own authority.
711 kauth_resolver_identity
= 0;
712 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
714 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
716 /* Cause all waiting-for-work threads to return EIO */
717 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
718 /* and return EIO to the caller */
722 case KAUTH_EXTLOOKUP_BADRQ
:
723 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
727 case KAUTH_EXTLOOKUP_FAILURE
:
728 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
733 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
739 * In the case of a fatal error, we assume that the resolver will restart
740 * quickly and re-collect all of the outstanding requests. Thus, we don't
741 * complete the request which returned the fatal error status.
743 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
744 /* scan our list for this request */
745 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
747 if (workp
->kr_seqno
== extl
.el_seqno
) {
749 workp
->kr_work
= extl
;
750 /* move onto completed list and wake up requester(s) */
751 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
752 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
753 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
754 workp
->kr_result
= result
;
755 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
762 * Note that it's OK for us not to find anything; if the request has
763 * timed out the work record will be gone.
765 KAUTH_RESOLVER_UNLOCK();
775 struct kauth_identity
{
776 TAILQ_ENTRY(kauth_identity
) ki_link
;
778 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
779 #define KI_VALID_GID (1<<1)
780 #define KI_VALID_GUID (1<<2)
781 #define KI_VALID_NTSID (1<<3)
787 * Expiry times are the earliest time at which we will disregard the cached state and go to
788 * userland. Before then if the valid bit is set, we will return the cached value. If it's
789 * not set, we will not go to userland to resolve, just assume that there is no answer
792 time_t ki_guid_expiry
;
793 time_t ki_ntsid_expiry
;
796 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
797 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
798 static int kauth_identity_count
;
800 static lck_mtx_t
*kauth_identity_mtx
;
801 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
802 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
805 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
806 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
807 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
808 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
809 static void kauth_identity_lru(struct kauth_identity
*kip
);
810 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
811 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
812 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
813 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
814 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
815 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
819 * kauth_identity_init
821 * Description: Initialize the kernel side of the credential identity resolver
827 * Notes: Intialize the credential identity resolver for use; the
828 * credential identity resolver is the KPI used to communicate
829 * with a user space credential identity resolver daemon.
831 * This function is called from kauth_init() in the file
832 * kern_authorization.c.
835 kauth_identity_init(void)
837 TAILQ_INIT(&kauth_identities
);
838 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
843 * kauth_identity_alloc
845 * Description: Allocate and fill out a kauth_identity structure for
846 * translation between {UID|GID}/GUID/NTSID
850 * Returns: NULL Insufficient memory to satisfy
852 * !NULL A pointer to the applocated
853 * structure, filled in
855 * Notes: It is illegal to translate between UID and GID; any given UUID
856 * or NTSID can oly refer to an NTSIDE or UUID (respectively),
857 * and *either* a UID *or* a GID, but not both.
859 static struct kauth_identity
*
860 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
862 struct kauth_identity
*kip
;
864 /* get and fill in a new identity */
865 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
867 if (gid
!= KAUTH_GID_NONE
) {
869 kip
->ki_valid
= KI_VALID_GID
;
871 if (uid
!= KAUTH_UID_NONE
) {
872 if (kip
->ki_valid
& KI_VALID_GID
)
873 panic("can't allocate kauth identity with both uid and gid");
875 kip
->ki_valid
= KI_VALID_UID
;
878 kip
->ki_guid
= *guidp
;
879 kip
->ki_valid
|= KI_VALID_GUID
;
881 kip
->ki_guid_expiry
= guid_expiry
;
882 if (ntsidp
!= NULL
) {
883 kip
->ki_ntsid
= *ntsidp
;
884 kip
->ki_valid
|= KI_VALID_NTSID
;
886 kip
->ki_ntsid_expiry
= ntsid_expiry
;
893 * kauth_identity_register_and_free
895 * Description: Register an association between identity tokens. The passed
896 * 'kip' is freed by this function.
898 * Parameters: kip Pointer to kauth_identity
899 * structure to register
903 * Notes: The memory pointer to by 'kip' is assumed to have been
904 * previously allocated via kauth_identity_alloc().
907 kauth_identity_register_and_free(struct kauth_identity
*kip
)
909 struct kauth_identity
*ip
;
912 * We search the cache for the UID listed in the incoming association.
913 * If we already have an entry, the new information is merged.
916 KAUTH_IDENTITY_LOCK();
917 if (kip
->ki_valid
& KI_VALID_UID
) {
918 if (kip
->ki_valid
& KI_VALID_GID
)
919 panic("kauth_identity: can't insert record with both UID and GID as key");
920 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
921 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
923 } else if (kip
->ki_valid
& KI_VALID_GID
) {
924 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
925 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
928 panic("kauth_identity: can't insert record without UID or GID as key");
932 /* we already have an entry, merge/overwrite */
933 if (kip
->ki_valid
& KI_VALID_GUID
) {
934 ip
->ki_guid
= kip
->ki_guid
;
935 ip
->ki_valid
|= KI_VALID_GUID
;
937 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
938 if (kip
->ki_valid
& KI_VALID_NTSID
) {
939 ip
->ki_ntsid
= kip
->ki_ntsid
;
940 ip
->ki_valid
|= KI_VALID_NTSID
;
942 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
943 /* and discard the incoming identity */
947 /* don't have any information on this identity, so just add it */
948 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
949 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
950 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
951 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
952 kauth_identity_count
--;
955 KAUTH_IDENTITY_UNLOCK();
956 /* have to drop lock before freeing expired entry */
963 * kauth_identity_updatecache
965 * Description: Given a lookup result, add any associations that we don't
968 * Parameters: elp External lookup result from
969 * user space daemon to kernel
970 * rkip pointer to returned kauth
976 * *rkip Modified (if non-NULL)
979 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
982 struct kauth_identity
*kip
;
987 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
988 KAUTH_IDENTITY_LOCK();
989 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
990 /* matching record */
991 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
992 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
993 kip
->ki_guid
= elp
->el_uguid
;
994 kip
->ki_valid
|= KI_VALID_GUID
;
996 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
997 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
998 kip
->ki_ntsid
= elp
->el_usid
;
999 kip
->ki_valid
|= KI_VALID_NTSID
;
1001 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
1002 kauth_identity_lru(kip
);
1005 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1009 KAUTH_IDENTITY_UNLOCK();
1010 /* not found in cache, add new record */
1012 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1013 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1014 tv
.tv_sec
+ elp
->el_uguid_valid
,
1015 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1016 tv
.tv_sec
+ elp
->el_usid_valid
);
1020 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1021 kauth_identity_register_and_free(kip
);
1026 /* group identity? */
1027 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
1028 KAUTH_IDENTITY_LOCK();
1029 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1030 /* matching record */
1031 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1032 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1033 kip
->ki_guid
= elp
->el_gguid
;
1034 kip
->ki_valid
|= KI_VALID_GUID
;
1036 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
1037 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1038 kip
->ki_ntsid
= elp
->el_gsid
;
1039 kip
->ki_valid
|= KI_VALID_NTSID
;
1041 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
1042 kauth_identity_lru(kip
);
1045 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1049 KAUTH_IDENTITY_UNLOCK();
1050 /* not found in cache, add new record */
1052 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1053 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1054 tv
.tv_sec
+ elp
->el_gguid_valid
,
1055 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1056 tv
.tv_sec
+ elp
->el_gsid_valid
);
1060 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1061 kauth_identity_register_and_free(kip
);
1070 * kauth_identity_lru
1072 * Description: Promote the entry to the head of the LRU, assumes the cache
1075 * Parameters: kip kauth identity to move to the
1076 * head of the LRU list, if it's
1081 * Notes: This is called even if the entry has expired; typically an
1082 * expired entry that's been looked up is about to be revalidated,
1083 * and having it closer to the head of the LRU means finding it
1084 * quickly again when the revalidation comes through.
1087 kauth_identity_lru(struct kauth_identity
*kip
)
1089 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1090 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1091 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1097 * kauth_identity_guid_expired
1099 * Description: Handle lazy expiration of GUID translations.
1101 * Parameters: kip kauth identity to check for
1104 * Returns: 1 Expired
1108 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1113 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
1114 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1119 * kauth_identity_ntsid_expired
1121 * Description: Handle lazy expiration of NTSID translations.
1123 * Parameters: kip kauth identity to check for
1126 * Returns: 1 Expired
1130 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1135 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1136 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1141 * kauth_identity_find_uid
1143 * Description: Search for an entry by UID
1145 * Parameters: uid UID to find
1146 * kir Pointer to return area
1152 * *klr Modified, if found
1155 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
1157 struct kauth_identity
*kip
;
1159 KAUTH_IDENTITY_LOCK();
1160 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1161 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1162 kauth_identity_lru(kip
);
1163 /* Copy via structure assignment */
1168 KAUTH_IDENTITY_UNLOCK();
1169 return((kip
== NULL
) ? ENOENT
: 0);
1174 * kauth_identity_find_uid
1176 * Description: Search for an entry by GID
1178 * Parameters: gid GID to find
1179 * kir Pointer to return area
1185 * *klr Modified, if found
1188 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
1190 struct kauth_identity
*kip
;
1192 KAUTH_IDENTITY_LOCK();
1193 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1194 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1195 kauth_identity_lru(kip
);
1196 /* Copy via structure assignment */
1201 KAUTH_IDENTITY_UNLOCK();
1202 return((kip
== NULL
) ? ENOENT
: 0);
1207 * kauth_identity_find_guid
1209 * Description: Search for an entry by GUID
1211 * Parameters: guidp Pointer to GUID to find
1212 * kir Pointer to return area
1218 * *klr Modified, if found
1220 * Note: The association may be expired, in which case the caller
1221 * may elect to call out to userland to revalidate.
1224 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
1226 struct kauth_identity
*kip
;
1228 KAUTH_IDENTITY_LOCK();
1229 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1230 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1231 kauth_identity_lru(kip
);
1232 /* Copy via structure assignment */
1237 KAUTH_IDENTITY_UNLOCK();
1238 return((kip
== NULL
) ? ENOENT
: 0);
1243 * kauth_identity_find_ntsid
1245 * Description: Search for an entry by NTSID
1247 * Parameters: ntsid Pointer to NTSID to find
1248 * kir Pointer to return area
1254 * *klr Modified, if found
1256 * Note: The association may be expired, in which case the caller
1257 * may elect to call out to userland to revalidate.
1260 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
1262 struct kauth_identity
*kip
;
1264 KAUTH_IDENTITY_LOCK();
1265 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1266 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1267 kauth_identity_lru(kip
);
1268 /* Copy via structure assignment */
1273 KAUTH_IDENTITY_UNLOCK();
1274 return((kip
== NULL
) ? ENOENT
: 0);
1281 guid_t kauth_null_guid
;
1287 * Description: Determine the equality of two GUIDs
1289 * Parameters: guid1 Pointer to first GUID
1290 * guid2 Pointer to second GUID
1292 * Returns: 0 If GUIDs are inequal
1293 * !0 If GUIDs are equal
1296 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1298 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1303 * kauth_wellknown_guid
1305 * Description: Determine if a GUID is a well-known GUID
1307 * Parameters: guid Pointer to GUID to check
1309 * Returns: KAUTH_WKG_NOT Not a wel known GUID
1310 * KAUTH_WKG_EVERYBODY "Everybody"
1311 * KAUTH_WKG_NOBODY "Nobody"
1312 * KAUTH_WKG_OWNER "Other"
1313 * KAUTH_WKG_GROUP "Group"
1316 kauth_wellknown_guid(guid_t
*guid
)
1318 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1321 * All WKGs begin with the same 12 bytes.
1323 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1325 * The final 4 bytes are our code (in network byte order).
1327 code
= OSSwapHostToBigInt32(*(u_int32_t
*)&guid
->g_guid
[12]);
1330 return(KAUTH_WKG_EVERYBODY
);
1332 return(KAUTH_WKG_NOBODY
);
1334 return(KAUTH_WKG_OWNER
);
1336 return(KAUTH_WKG_GROUP
);
1339 return(KAUTH_WKG_NOT
);
1346 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1348 * Paramters: sid1 Pointer to first NTSID
1349 * sid2 Pointer to second NTSID
1351 * Returns: 0 If GUIDs are inequal
1352 * !0 If GUIDs are equal
1355 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1357 /* check sizes for equality, also sanity-check size while we're at it */
1358 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1359 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1360 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1369 * We support four tokens representing identity:
1370 * - Credential reference
1373 * - NT security identifier
1375 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1379 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
1383 * kauth_cred_change_egid
1385 * Description: Set EGID by changing the first element of cr_groups for the
1386 * passed credential; if the new EGID exists in the list of
1387 * groups already, then rotate the old EGID into its position,
1388 * otherwise replace it
1390 * Parameters: cred Pointer to the credential to modify
1391 * new_egid The new EGID to set
1393 * Returns: 0 The egid did not displace a member of
1394 * the supplementary group list
1395 * 1 The egid being set displaced a member
1396 * of the supplementary groups list
1398 * Note: Utility function; internal use only because of locking.
1400 * This function operates on the credential passed; the caller
1401 * must operate either on a newly allocated credential (one for
1402 * which there is no hash cache reference and no externally
1403 * visible pointer reference), or a template credential.
1406 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1412 #endif /* radar_4600026 */
1413 gid_t old_egid
= cred
->cr_groups
[0];
1415 /* Ignoring the first entry, scan for a match for the new egid */
1416 for (i
= 1; i
< cred
->cr_ngroups
; i
++) {
1418 * If we find a match, swap them so we don't lose overall
1421 if (cred
->cr_groups
[i
] == new_egid
) {
1422 cred
->cr_groups
[i
] = old_egid
;
1423 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1430 #error Fix radar 4600026 first!!!
1433 This is correct for memberd behaviour, but incorrect for POSIX; to address
1434 this, we would need to automatically opt-out any SUID/SGID binary, and force
1435 it to use initgroups to opt back in. We take the approach of considering it
1436 opt'ed out in any group of 16 displacement instead, since it's a much more
1437 conservative approach (i.e. less likely to cause things to break).
1441 * If we displaced a member of the supplementary groups list of the
1442 * credential, and we have not opted out of memberd, then if memberd
1443 * says that the credential is a member of the group, then it has not
1444 * actually been displaced.
1446 * NB: This is typically a cold code path.
1448 if (displaced
&& !(cred
->cr_flags
& CRF_NOMEMBERD
) &&
1449 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1452 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1454 #endif /* radar_4600026 */
1456 /* set the new EGID into the old spot */
1457 cred
->cr_groups
[0] = new_egid
;
1466 * Description: Fetch UID from credential
1468 * Parameters: cred Credential to examine
1470 * Returns: (uid_t) UID associated with credential
1473 kauth_cred_getuid(kauth_cred_t cred
)
1475 NULLCRED_CHECK(cred
);
1476 return(cred
->cr_uid
);
1483 * Description: Fetch GID from credential
1485 * Parameters: cred Credential to examine
1487 * Returns: (gid_t) GID associated with credential
1490 kauth_cred_getgid(kauth_cred_t cred
)
1492 NULLCRED_CHECK(cred
);
1493 return(cred
->cr_gid
);
1498 * kauth_cred_guid2uid
1500 * Description: Fetch UID from GUID
1502 * Parameters: guidp Pointer to GUID to examine
1503 * uidp Pointer to buffer for UID
1505 * Returns: 0 Success
1506 * kauth_cred_cache_lookup:EINVAL
1509 * *uidp Modified, if successful
1512 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
1514 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
1519 * kauth_cred_guid2gid
1521 * Description: Fetch GID from GUID
1523 * Parameters: guidp Pointer to GUID to examine
1524 * gidp Pointer to buffer for GID
1526 * Returns: 0 Success
1527 * kauth_cred_cache_lookup:EINVAL
1530 * *gidp Modified, if successful
1533 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
1535 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
1540 * kauth_cred_ntsid2uid
1542 * Description: Fetch UID from NTSID
1544 * Parameters: sidp Pointer to NTSID to examine
1545 * uidp Pointer to buffer for UID
1547 * Returns: 0 Success
1548 * kauth_cred_cache_lookup:EINVAL
1551 * *uidp Modified, if successful
1554 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
1556 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
1561 * kauth_cred_ntsid2gid
1563 * Description: Fetch GID from NTSID
1565 * Parameters: sidp Pointer to NTSID to examine
1566 * gidp Pointer to buffer for GID
1568 * Returns: 0 Success
1569 * kauth_cred_cache_lookup:EINVAL
1572 * *gidp Modified, if successful
1575 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
1577 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
1582 * kauth_cred_ntsid2guid
1584 * Description: Fetch GUID from NTSID
1586 * Parameters: sidp Pointer to NTSID to examine
1587 * guidp Pointer to buffer for GUID
1589 * Returns: 0 Success
1590 * kauth_cred_cache_lookup:EINVAL
1593 * *guidp Modified, if successful
1596 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
1598 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
1603 * kauth_cred_uid2guid
1605 * Description: Fetch GUID from UID
1607 * Parameters: uid UID to examine
1608 * guidp Pointer to buffer for GUID
1610 * Returns: 0 Success
1611 * kauth_cred_cache_lookup:EINVAL
1614 * *guidp Modified, if successful
1617 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
1619 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
1624 * kauth_cred_getguid
1626 * Description: Fetch GUID from credential
1628 * Parameters: cred Credential to examine
1629 * guidp Pointer to buffer for GUID
1631 * Returns: 0 Success
1632 * kauth_cred_cache_lookup:EINVAL
1635 * *guidp Modified, if successful
1638 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
1640 NULLCRED_CHECK(cred
);
1641 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
1646 * kauth_cred_getguid
1648 * Description: Fetch GUID from GID
1650 * Parameters: gid GID to examine
1651 * guidp Pointer to buffer for GUID
1653 * Returns: 0 Success
1654 * kauth_cred_cache_lookup:EINVAL
1657 * *guidp Modified, if successful
1660 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
1662 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
1667 * kauth_cred_uid2ntsid
1669 * Description: Fetch NTSID from UID
1671 * Parameters: uid UID to examine
1672 * sidp Pointer to buffer for NTSID
1674 * Returns: 0 Success
1675 * kauth_cred_cache_lookup:EINVAL
1678 * *sidp Modified, if successful
1681 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
1683 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
1688 * kauth_cred_getntsid
1690 * Description: Fetch NTSID from credential
1692 * Parameters: cred Credential to examine
1693 * sidp Pointer to buffer for NTSID
1695 * Returns: 0 Success
1696 * kauth_cred_cache_lookup:EINVAL
1699 * *sidp Modified, if successful
1702 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
1704 NULLCRED_CHECK(cred
);
1705 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
1710 * kauth_cred_gid2ntsid
1712 * Description: Fetch NTSID from GID
1714 * Parameters: gid GID to examine
1715 * sidp Pointer to buffer for NTSID
1717 * Returns: 0 Success
1718 * kauth_cred_cache_lookup:EINVAL
1721 * *sidp Modified, if successful
1724 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1726 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1731 * kauth_cred_guid2ntsid
1733 * Description: Fetch NTSID from GUID
1735 * Parameters: guidp Pointer to GUID to examine
1736 * sidp Pointer to buffer for NTSID
1738 * Returns: 0 Success
1739 * kauth_cred_cache_lookup:EINVAL
1742 * *sidp Modified, if successful
1745 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1747 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1752 * kauth_cred_cache_lookup
1754 * Description: Lookup a translation in the cache; if one is not found, and
1755 * the attempt was not fatal, submit the request to the resolver
1756 * instead, and wait for it to complete or be aborted.
1758 * Parameters: from Identity information we have
1759 * to Identity information we want
1760 * src Pointer to buffer containing
1761 * the source identity
1762 * dst Pointer to buffer to receive
1763 * the target identity
1765 * Returns: 0 Success
1766 * EINVAL Unknown source identity type
1769 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1771 struct kauth_identity ki
;
1772 struct kauth_identity_extlookup el
;
1774 int (* expired
)(struct kauth_identity
*kip
);
1776 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1779 * Look for an existing cache entry for this association.
1780 * If the entry has not expired, return the cached information.
1785 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1788 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1791 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1793 case KI_VALID_NTSID
:
1794 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1799 /* lookup failure or error */
1801 /* any other error is fatal */
1802 if (error
!= ENOENT
) {
1803 /* XXX bogus check - this is not possible */
1804 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1808 /* do we have a translation? */
1809 if (ki
.ki_valid
& to
) {
1810 /* found a valid cached entry, check expiry */
1813 expired
= kauth_identity_guid_expired
;
1815 case KI_VALID_NTSID
:
1816 expired
= kauth_identity_ntsid_expired
;
1821 expired
= kauth_identity_guid_expired
;
1823 case KI_VALID_NTSID
:
1824 expired
= kauth_identity_ntsid_expired
;
1830 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1832 * If no expiry function, or not expired, we have found
1836 KAUTH_DEBUG("CACHE - no expiry function");
1839 if (!expired(&ki
)) {
1840 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1844 * We leave ki_valid set here; it contains a
1845 * translation but the TTL has expired. If we can't
1846 * get a result from the resolver, we will use it as
1847 * a better-than nothing alternative.
1849 KAUTH_DEBUG("CACHE - expired entry found");
1854 * We failed to find a cache entry; call the resolver.
1856 * Note: We ask for as much data as we can get.
1858 bzero(&el
, sizeof(el
));
1859 el
.el_info_pid
= current_proc()->p_pid
;
1862 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1863 el
.el_uid
= *(uid_t
*)src
;
1866 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1867 el
.el_gid
= *(gid_t
*)src
;
1870 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1871 el
.el_uguid
= *(guid_t
*)src
;
1872 el
.el_gguid
= *(guid_t
*)src
;
1874 case KI_VALID_NTSID
:
1875 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1876 el
.el_usid
= *(ntsid_t
*)src
;
1877 el
.el_gsid
= *(ntsid_t
*)src
;
1883 * Here we ask for everything all at once, to avoid having to work
1884 * out what we really want now, or might want soon.
1886 * Asking for SID translations when we don't know we need them right
1887 * now is going to cause excess work to be done if we're connected
1888 * to a network that thinks it can translate them. This list needs
1889 * to get smaller/smarter.
1891 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1892 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1893 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1894 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1895 error
= kauth_resolver_submit(&el
);
1896 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1897 /* was the lookup successful? */
1900 * Save the results from the lookup - may have other
1901 * information even if we didn't get a guid.
1903 kauth_identity_updatecache(&el
, &ki
);
1906 * Check to see if we have a valid result.
1908 if (!error
&& !(ki
.ki_valid
& to
))
1915 *(uid_t
*)dst
= ki
.ki_uid
;
1918 *(gid_t
*)dst
= ki
.ki_gid
;
1921 *(guid_t
*)dst
= ki
.ki_guid
;
1923 case KI_VALID_NTSID
:
1924 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1929 KAUTH_DEBUG("CACHE - returned successfully");
1935 * Group membership cache.
1937 * XXX the linked-list implementation here needs to be optimized.
1940 struct kauth_group_membership
{
1941 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1942 uid_t gm_uid
; /* the identity whose membership we're recording */
1943 gid_t gm_gid
; /* group of which they are a member */
1944 time_t gm_expiry
; /* TTL for the membership */
1946 #define KAUTH_GROUP_ISMEMBER (1<<0)
1949 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1950 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1951 static int kauth_groups_count
;
1953 static lck_mtx_t
*kauth_groups_mtx
;
1954 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1955 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1957 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1958 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1959 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
1965 * Description: Initialize the groups cache
1967 * Parameters: (void)
1971 * Notes: Intialize the groups cache for use; the group cache is used
1972 * to avoid unnecessary calls out to user space.
1974 * This function is called from kauth_init() in the file
1975 * kern_authorization.c.
1978 kauth_groups_init(void)
1980 TAILQ_INIT(&kauth_groups
);
1981 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1986 * kauth_groups_expired
1988 * Description: Handle lazy expiration of group membership cache entries
1990 * Parameters: gm group membership entry to
1991 * check for expiration
1993 * Returns: 1 Expired
1997 kauth_groups_expired(struct kauth_group_membership
*gm
)
2002 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2009 * Description: Promote the entry to the head of the LRU, assumes the cache
2012 * Parameters: kip group membership entry to move
2013 * to the head of the LRU list,
2014 * if it's not already there
2018 * Notes: This is called even if the entry has expired; typically an
2019 * expired entry that's been looked up is about to be revalidated,
2020 * and having it closer to the head of the LRU means finding it
2021 * quickly again when the revalidation comes through.
2024 kauth_groups_lru(struct kauth_group_membership
*gm
)
2026 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2027 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2028 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2034 * kauth_groups_updatecache
2036 * Description: Given a lookup result, add any group cache associations that
2037 * we don't currently have.
2039 * Parameters: elp External lookup result from
2040 * user space daemon to kernel
2041 * rkip pointer to returned kauth
2047 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2049 struct kauth_group_membership
*gm
;
2052 /* need a valid response if we are to cache anything */
2054 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2055 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2061 * Search for an existing record for this association before inserting
2062 * a new one; if we find one, update it instead of creating a new one
2064 KAUTH_GROUPS_LOCK();
2065 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2066 if ((el
->el_uid
== gm
->gm_uid
) &&
2067 (el
->el_gid
== gm
->gm_gid
)) {
2068 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2069 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2071 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2073 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2074 kauth_groups_lru(gm
);
2078 KAUTH_GROUPS_UNLOCK();
2080 /* if we found an entry to update, stop here */
2084 /* allocate a new record */
2085 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2087 gm
->gm_uid
= el
->el_uid
;
2088 gm
->gm_gid
= el
->el_gid
;
2089 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2090 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2092 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2094 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2098 * Insert the new entry. Note that it's possible to race ourselves
2099 * here and end up with duplicate entries in the list. Wasteful, but
2100 * harmless since the first into the list will never be looked up,
2101 * and thus will eventually just fall off the end.
2103 KAUTH_GROUPS_LOCK();
2104 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2105 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
2106 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2107 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2108 kauth_groups_count
--;
2112 KAUTH_GROUPS_UNLOCK();
2114 /* free expired cache entry */
2121 * Group membership KPI
2125 * kauth_cred_ismember_gid
2127 * Description: Given a credential and a GID, determine if the GID is a member
2128 * of one of the supplementary groups associated with the given
2131 * Parameters: cred Credential to check in
2132 * gid GID to check for membership
2133 * resultp Pointer to int to contain the
2134 * result of the call
2136 * Returns: 0 Success
2137 * ENOENT Could not proform lookup
2138 * kauth_resolver_submit:EWOULDBLOCK
2139 * kauth_resolver_submit:EINTR
2140 * kauth_resolver_submit:ENOMEM
2141 * kauth_resolver_submit:??? Unlikely error from user space
2144 * *resultp (modified) 1 Is member
2147 * Notes: This function guarantees not to modify resultp when returning
2150 * This function effectively checkes the EGID as well, since the
2151 * EGID is cr_groups[0] as an implementation detail.
2154 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2156 struct kauth_group_membership
*gm
;
2157 struct kauth_identity_extlookup el
;
2161 * Check the per-credential list of override groups.
2163 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2164 * the cache should be used for that case.
2166 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
2167 if (gid
== cred
->cr_groups
[i
]) {
2174 * If we don't have a UID for group membership checks, the in-cred list
2175 * was authoritative and we can stop here.
2177 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
2184 * If the resolver hasn't checked in yet, we are early in the boot
2185 * phase and the local group list is complete and authoritative.
2187 if (!kauth_resolver_registered
) {
2193 /* XXX check supplementary groups */
2194 /* XXX check whiteout groups */
2195 /* XXX nesting of supplementary/whiteout groups? */
2198 * Check the group cache.
2200 KAUTH_GROUPS_LOCK();
2201 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2202 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2203 kauth_groups_lru(gm
);
2208 /* did we find a membership entry? */
2210 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2211 KAUTH_GROUPS_UNLOCK();
2213 /* if we did, we can return now */
2217 /* nothing in the cache, need to go to userland */
2218 bzero(&el
, sizeof(el
));
2219 el
.el_info_pid
= current_proc()->p_pid
;
2220 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2221 el
.el_uid
= cred
->cr_gmuid
;
2223 el
.el_member_valid
= 0; /* XXX set by resolver? */
2224 error
= kauth_resolver_submit(&el
);
2227 /* save the results from the lookup */
2228 kauth_groups_updatecache(&el
);
2230 /* if we successfully ascertained membership, report */
2231 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2232 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2241 * kauth_cred_ismember_guid
2243 * Description: Determine whether the supplied credential is a member of the
2244 * group nominated by GUID.
2246 * Parameters: cred Credential to check in
2247 * guidp Pointer to GUID whose group
2248 * we are testing for membership
2249 * resultp Pointer to int to contain the
2250 * result of the call
2252 * Returns: 0 Success
2253 * kauth_cred_guid2gid:EINVAL
2254 * kauth_cred_ismember_gid:ENOENT
2255 * kauth_cred_ismember_gid:EWOULDBLOCK
2256 * kauth_cred_ismember_gid:EINTR
2257 * kauth_cred_ismember_gid:ENOMEM
2258 * kauth_cred_ismember_gid:??? Unlikely error from user space
2261 * *resultp (modified) 1 Is member
2265 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2267 struct kauth_identity ki
;
2272 wkg
= kauth_wellknown_guid(guidp
);
2274 case KAUTH_WKG_NOBODY
:
2277 case KAUTH_WKG_EVERYBODY
:
2283 * Grovel the identity cache looking for this GUID.
2284 * If we find it, and it is for a user record, return
2285 * false because it's not a group.
2287 * This is necessary because we don't have -ve caching
2288 * of group memberships, and we really want to avoid
2289 * calling out to the resolver if at all possible.
2291 * Because we're called by the ACL evaluator, and the
2292 * ACL evaluator is likely to encounter ACEs for users,
2293 * this is expected to be a common case.
2296 if ((error
= kauth_identity_find_guid(guidp
, &ki
)) == 0 &&
2297 !kauth_identity_guid_expired(&ki
)) {
2298 if (ki
.ki_valid
& KI_VALID_GID
) {
2299 /* It's a group after all... */
2303 if (ki
.ki_valid
& KI_VALID_UID
) {
2308 #endif /* 6603280 */
2310 * Attempt to translate the GUID to a GID. Even if
2311 * this fails, we will have primed the cache if it is
2312 * a user record and we'll see it above the next time
2315 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2317 * If we have no guid -> gid translation, it's not a group and
2318 * thus the cred can't be a member.
2320 if (error
== ENOENT
) {
2326 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
2333 * kauth_cred_gid_subset
2335 * Description: Given two credentials, determine if all GIDs associated with
2336 * the first are also associated with the second
2338 * Parameters: cred1 Credential to check for
2339 * cred2 Credential to check in
2340 * resultp Pointer to int to contain the
2341 * result of the call
2343 * Returns: 0 Success
2344 * non-zero See kauth_cred_ismember_gid for
2348 * *resultp (modified) 1 Is subset
2351 * Notes: This function guarantees not to modify resultp when returning
2355 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
2357 int i
, err
, res
= 1;
2360 /* First, check the local list of groups */
2361 for (i
= 0; i
< cred1
->cr_ngroups
; i
++) {
2362 gid
= cred1
->cr_groups
[i
];
2363 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
2367 if (!res
&& gid
!= cred2
->cr_rgid
&& gid
!= cred2
->cr_svgid
) {
2373 /* Check real gid */
2374 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_rgid
, &res
)) != 0) {
2378 if (!res
&& cred1
->cr_rgid
!= cred2
->cr_rgid
&&
2379 cred1
->cr_rgid
!= cred2
->cr_svgid
) {
2384 /* Finally, check saved gid */
2385 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_svgid
, &res
)) != 0){
2389 if (!res
&& cred1
->cr_svgid
!= cred2
->cr_rgid
&&
2390 cred1
->cr_svgid
!= cred2
->cr_svgid
) {
2401 * kauth_cred_issuser
2403 * Description: Fast replacement for issuser()
2405 * Parameters: cred Credential to check for super
2408 * Returns: 0 Not super user
2411 * Notes: This function uses a magic number which is not a manifest
2412 * constant; this is bad practice.
2415 kauth_cred_issuser(kauth_cred_t cred
)
2417 return(cred
->cr_uid
== 0);
2425 /* lock protecting credential hash table */
2426 static lck_mtx_t
*kauth_cred_hash_mtx
;
2427 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2428 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2429 #if KAUTH_CRED_HASH_DEBUG
2430 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2431 #else /* !KAUTH_CRED_HASH_DEBUG */
2432 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2433 #endif /* !KAUTH_CRED_HASH_DEBUG */
2439 * Description: Initialize the credential hash cache
2441 * Parameters: (void)
2445 * Notes: Intialize the credential hash cache for use; the credential
2446 * hash cache is used convert duplicate credentials into a
2447 * single reference counted credential in order to save wired
2448 * kernel memory. In practice, this generally means a desktop
2449 * system runs with a few tens of credentials, instead of one
2450 * per process, one per thread, one per vnode cache entry, and
2451 * so on. This generally results in savings of 200K or more
2452 * (potentially much more on server systems).
2454 * The hash cache internally has a reference on the credential
2455 * for itself as a means of avoiding a reclaim race for a
2456 * credential in the process of having it's last non-hash
2457 * reference released. This would otherwise result in the
2458 * possibility of a freed credential that was still in uses due
2459 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
2461 * On final release, the hash reference is droped, and the
2462 * credential is freed back to the system.
2464 * This function is called from kauth_init() in the file
2465 * kern_authorization.c.
2468 kauth_cred_init(void)
2472 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2473 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
2475 /*allocate credential hash table */
2476 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
2477 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
2478 M_KAUTH
, M_WAITOK
| M_ZERO
);
2479 if (kauth_cred_table_anchor
== NULL
)
2480 panic("startup: kauth_cred_init");
2481 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2482 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
2490 * Description: Get the current thread's effective UID.
2492 * Parameters: (void)
2494 * Returns: (uid_t) The effective UID of the
2500 return(kauth_cred_get()->cr_uid
);
2507 * Description: Get the current thread's real UID.
2509 * Parameters: (void)
2511 * Returns: (uid_t) The real UID of the current
2517 return(kauth_cred_get()->cr_ruid
);
2524 * Description: Get the current thread's effective GID.
2526 * Parameters: (void)
2528 * Returns: (gid_t) The effective GID of the
2534 return(kauth_cred_get()->cr_groups
[0]);
2541 * Description: Get the current thread's real GID.
2543 * Parameters: (void)
2545 * Returns: (gid_t) The real GID of the current
2551 return(kauth_cred_get()->cr_rgid
);
2558 * Description: Returns a pointer to the current thread's credential
2560 * Parameters: (void)
2562 * Returns: (kauth_cred_t) Pointer to the current thread's
2565 * Notes: This function does not take a reference; because of this, the
2566 * caller MUST NOT do anything that would let the thread's
2567 * credential change while using the returned value, without
2568 * first explicitly taking their own reference.
2570 * If a caller intends to take a reference on the resulting
2571 * credential pointer from calling this function, it is strongly
2572 * recommended that the caller use kauth_cred_get_with_ref()
2573 * instead, to protect against any future changes to the cred
2574 * locking protocols; such changes could otherwise potentially
2575 * introduce race windows in the callers code.
2578 kauth_cred_get(void)
2581 struct uthread
*uthread
;
2583 uthread
= get_bsdthread_info(current_thread());
2585 if (uthread
== NULL
)
2586 panic("thread wants credential but has no BSD thread info");
2588 * We can lazy-bind credentials to threads, as long as their processes
2591 * XXX If we later inline this function, the code in this block
2592 * XXX should probably be called out in a function.
2594 if (uthread
->uu_ucred
== NOCRED
) {
2595 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2596 panic("thread wants credential but has no BSD process");
2597 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
2599 return(uthread
->uu_ucred
);
2603 mach_kauth_cred_uthread_update(void)
2608 uthread
= get_bsdthread_info(current_thread());
2609 proc
= current_proc();
2611 kauth_cred_uthread_update(uthread
, proc
);
2615 * kauth_cred_uthread_update
2617 * Description: Given a uthread, a proc, and whether or not the proc is locked,
2618 * late-bind the uthread cred to the proc cred.
2620 * Parameters: uthread_t The uthread to update
2621 * proc_t The process to update to
2625 * Notes: This code is common code called from system call or trap entry
2626 * in the case that the process thread may have been changed
2627 * since the last time the thread entered the kernel. It is
2628 * generally only called with the current uthread and process as
2632 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
2634 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
2635 (uthread
->uu_flag
& UT_SETUID
) == 0) {
2636 kauth_cred_t old
= uthread
->uu_ucred
;
2637 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
2638 if (IS_VALID_CRED(old
))
2639 kauth_cred_unref(&old
);
2645 * kauth_cred_get_with_ref
2647 * Description: Takes a reference on the current thread's credential, and then
2648 * returns a pointer to it to the caller.
2650 * Parameters: (void)
2652 * Returns: (kauth_cred_t) Pointer to the current thread's
2653 * newly referenced credential
2655 * Notes: This function takes a reference on the credential before
2656 * returning it to the caller.
2658 * It is the responsibility of the calling code to release this
2659 * reference when the credential is no longer in use.
2661 * Since the returned reference may be a persistent reference
2662 * (e.g. one cached in another data structure with a lifetime
2663 * longer than the calling function), this release may be delayed
2664 * until such time as the persistent reference is to be destroyed.
2665 * An example of this would be the per vnode credential cache used
2666 * to accelerate lookup operations.
2669 kauth_cred_get_with_ref(void)
2672 struct uthread
*uthread
;
2674 uthread
= get_bsdthread_info(current_thread());
2676 if (uthread
== NULL
)
2677 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
2678 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2679 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
2682 * We can lazy-bind credentials to threads, as long as their processes
2685 * XXX If we later inline this function, the code in this block
2686 * XXX should probably be called out in a function.
2688 if (uthread
->uu_ucred
== NOCRED
) {
2689 /* take reference for new cred in thread */
2690 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
2692 /* take a reference for our caller */
2693 kauth_cred_ref(uthread
->uu_ucred
);
2694 return(uthread
->uu_ucred
);
2699 * kauth_cred_proc_ref
2701 * Description: Takes a reference on the current process's credential, and
2702 * then returns a pointer to it to the caller.
2704 * Parameters: procp Process whose credential we
2705 * intend to take a reference on
2707 * Returns: (kauth_cred_t) Pointer to the process's
2708 * newly referenced credential
2710 * Locks: PROC_LOCK is held before taking the reference and released
2711 * after the refeence is taken to protect the p_ucred field of
2712 * the process referred to by procp.
2714 * Notes: This function takes a reference on the credential before
2715 * returning it to the caller.
2717 * It is the responsibility of the calling code to release this
2718 * reference when the credential is no longer in use.
2720 * Since the returned reference may be a persistent reference
2721 * (e.g. one cached in another data structure with a lifetime
2722 * longer than the calling function), this release may be delayed
2723 * until such time as the persistent reference is to be destroyed.
2724 * An example of this would be the per vnode credential cache used
2725 * to accelerate lookup operations.
2728 kauth_cred_proc_ref(proc_t procp
)
2733 cred
= proc_ucred(procp
);
2734 kauth_cred_ref(cred
);
2743 * Description: Allocate a new credential
2745 * Parameters: (void)
2747 * Returns: !NULL Newly allocated credential
2748 * NULL Insufficient memory
2750 * Notes: The newly allocated credential is zero'ed as part of the
2751 * allocation process, with the exception of the reference
2752 * count, which is set to 1 to indicate a single reference
2753 * held by the caller.
2755 * Since newly allocated credentials have no external pointers
2756 * referencing them, prior to making them visible in an externally
2757 * visible pointer (e.g. by adding them to the credential hash
2758 * cache) is the only legal time in which an existing credential
2759 * can be safely iinitialized or modified directly.
2761 * After initialization, the caller is expected to call the
2762 * function kauth_cred_add() to add the credential to the hash
2763 * cache, after which time it's frozen and becomes publically
2766 * The release protocol depends on kauth_hash_add() being called
2767 * before kauth_cred_rele() (there is a diagnostic panic which
2768 * will trigger if this protocol is not observed).
2770 * XXX: This function really ought to be static, rather than being
2771 * exported as KPI, since a failure of kauth_cred_add() can only
2772 * be handled by an explicit free of the credential; such frees
2773 * depend on knowlegdge of the allocation method used, which is
2774 * permitted to change between kernel revisions.
2776 * XXX: In the insufficient resource case, this code panic's rather
2777 * than returning a NULL pointer; the code that calls this
2778 * function needs to be audited before this can be changed.
2781 kauth_cred_alloc(void)
2783 kauth_cred_t newcred
;
2785 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
2787 bzero(newcred
, sizeof(*newcred
));
2788 newcred
->cr_ref
= 1;
2789 newcred
->cr_audit
.as_aia_p
= &audit_default_aia
;
2790 /* XXX the following will go away with cr_au */
2791 newcred
->cr_au
.ai_auid
= AU_DEFAUDITID
;
2792 /* must do this, or cred has same group membership as uid 0 */
2793 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
2796 panic("kauth_cred_alloc: couldn't allocate credential");
2800 #if KAUTH_CRED_HASH_DEBUG
2805 mac_cred_label_init(newcred
);
2815 * Description: Look to see if we already have a known credential in the hash
2816 * cache; if one is found, bump the reference count and return
2817 * it. If there are no credentials that match the given
2818 * credential, then allocate a new credential.
2820 * Parameters: cred Template for credential to
2823 * Returns: (kauth_cred_t) The credential that was found
2824 * in the hash or created
2825 * NULL kauth_cred_add() failed, or
2826 * there was not an egid specified
2828 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
2829 * maintain this field, we can't expect callers to know how it
2830 * needs to be set. Callers should be prepared for this field
2831 * to be overwritten.
2833 * XXX: This code will tight-loop if memory for a new credential is
2834 * persistently unavailable; this is perhaps not the wisest way
2835 * to handle this condition, but current callers do not expect
2839 kauth_cred_create(kauth_cred_t cred
)
2841 kauth_cred_t found_cred
, new_cred
= NULL
;
2843 KAUTH_CRED_HASH_LOCK_ASSERT();
2845 if (cred
->cr_flags
& CRF_NOMEMBERD
)
2846 cred
->cr_gmuid
= KAUTH_UID_NONE
;
2848 cred
->cr_gmuid
= cred
->cr_uid
;
2850 /* Caller *must* specify at least the egid in cr_groups[0] */
2851 if (cred
->cr_ngroups
< 1)
2855 KAUTH_CRED_HASH_LOCK();
2856 found_cred
= kauth_cred_find(cred
);
2857 if (found_cred
!= NULL
) {
2859 * Found an existing credential so we'll bump
2860 * reference count and return
2862 kauth_cred_ref(found_cred
);
2863 KAUTH_CRED_HASH_UNLOCK();
2866 KAUTH_CRED_HASH_UNLOCK();
2869 * No existing credential found. Create one and add it to
2872 new_cred
= kauth_cred_alloc();
2873 if (new_cred
!= NULL
) {
2875 new_cred
->cr_uid
= cred
->cr_uid
;
2876 new_cred
->cr_ruid
= cred
->cr_ruid
;
2877 new_cred
->cr_svuid
= cred
->cr_svuid
;
2878 new_cred
->cr_rgid
= cred
->cr_rgid
;
2879 new_cred
->cr_svgid
= cred
->cr_svgid
;
2880 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
2881 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
2882 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
2884 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
2885 sizeof(new_cred
->cr_audit
));
2886 /* XXX the following bcopy() will go away with cr_au */
2887 bcopy(&cred
->cr_au
, &new_cred
->cr_au
,
2888 sizeof(new_cred
->cr_au
));
2890 new_cred
->cr_flags
= cred
->cr_flags
;
2892 KAUTH_CRED_HASH_LOCK();
2893 err
= kauth_cred_add(new_cred
);
2894 KAUTH_CRED_HASH_UNLOCK();
2896 /* Retry if kauth_cred_add returns non zero value */
2900 mac_cred_label_destroy(new_cred
);
2902 AUDIT_SESSION_UNREF(new_cred
);
2904 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
2914 * kauth_cred_setresuid
2916 * Description: Update the given credential using the UID arguments. The given
2917 * UIDs are used to set the effective UID, real UID, saved UID,
2918 * and GMUID (used for group membership checking).
2920 * Parameters: cred The original credential
2921 * ruid The new real UID
2922 * euid The new effective UID
2923 * svuid The new saved UID
2924 * gmuid KAUTH_UID_NONE -or- the new
2925 * group membership UID
2927 * Returns: (kauth_cred_t) The updated credential
2929 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
2930 * setting, so if you don't want it to change, pass it the
2931 * previous value, explicitly.
2933 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
2934 * if it returns a credential other than the one it is passed,
2935 * will have dropped the reference on the passed credential. All
2936 * callers should be aware of this, and treat this function as an
2937 * unref + ref, potentially on different credentials.
2939 * Because of this, the caller is expected to take its own
2940 * reference on the credential passed as the first parameter,
2941 * and be prepared to release the reference on the credential
2942 * that is returned to them, if it is not intended to be a
2943 * persistent reference.
2946 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
2948 struct ucred temp_cred
;
2950 NULLCRED_CHECK(cred
);
2953 * We don't need to do anything if the UIDs we are changing are
2954 * already the same as the UIDs passed in
2956 if ((euid
== KAUTH_UID_NONE
|| cred
->cr_uid
== euid
) &&
2957 (ruid
== KAUTH_UID_NONE
|| cred
->cr_ruid
== ruid
) &&
2958 (svuid
== KAUTH_UID_NONE
|| cred
->cr_svuid
== svuid
) &&
2959 (cred
->cr_gmuid
== gmuid
)) {
2960 /* no change needed */
2965 * Look up in cred hash table to see if we have a matching credential
2966 * with the new values; this is done by calling kauth_cred_update().
2968 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
2969 if (euid
!= KAUTH_UID_NONE
) {
2970 temp_cred
.cr_uid
= euid
;
2972 if (ruid
!= KAUTH_UID_NONE
) {
2973 temp_cred
.cr_ruid
= ruid
;
2975 if (svuid
!= KAUTH_UID_NONE
) {
2976 temp_cred
.cr_svuid
= svuid
;
2980 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
2981 * opt out of participation in external group resolution, unless we
2982 * unless we explicitly opt back in later.
2984 if ((temp_cred
.cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
2985 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
2988 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
2993 * kauth_cred_setresgid
2995 * Description: Update the given credential using the GID arguments. The given
2996 * GIDs are used to set the effective GID, real GID, and saved
2999 * Parameters: cred The original credential
3000 * rgid The new real GID
3001 * egid The new effective GID
3002 * svgid The new saved GID
3004 * Returns: (kauth_cred_t) The updated credential
3006 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3007 * if it returns a credential other than the one it is passed,
3008 * will have dropped the reference on the passed credential. All
3009 * callers should be aware of this, and treat this function as an
3010 * unref + ref, potentially on different credentials.
3012 * Because of this, the caller is expected to take its own
3013 * reference on the credential passed as the first parameter,
3014 * and be prepared to release the reference on the credential
3015 * that is returned to them, if it is not intended to be a
3016 * persistent reference.
3019 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3021 struct ucred temp_cred
;
3023 NULLCRED_CHECK(cred
);
3024 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3027 * We don't need to do anything if the given GID are already the
3028 * same as the GIDs in the credential.
3030 if (cred
->cr_groups
[0] == egid
&&
3031 cred
->cr_rgid
== rgid
&&
3032 cred
->cr_svgid
== svgid
) {
3033 /* no change needed */
3038 * Look up in cred hash table to see if we have a matching credential
3039 * with the new values; this is done by calling kauth_cred_update().
3041 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3042 if (egid
!= KAUTH_GID_NONE
) {
3043 /* displacing a supplementary group opts us out of memberd */
3044 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3045 DEBUG_CRED_CHANGE("displaced!\n");
3046 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3047 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3049 DEBUG_CRED_CHANGE("not displaced\n");
3052 if (rgid
!= KAUTH_GID_NONE
) {
3053 temp_cred
.cr_rgid
= rgid
;
3055 if (svgid
!= KAUTH_GID_NONE
) {
3056 temp_cred
.cr_svgid
= svgid
;
3059 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3064 * Update the given credential with the given groups. We only allocate a new
3065 * credential when the given gid actually results in changes to the existing
3067 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3068 * which will be used for group membership checking.
3071 * kauth_cred_setgroups
3073 * Description: Update the given credential using the provide supplementary
3074 * group list and group membership UID
3076 * Parameters: cred The original credential
3077 * groups Pointer to gid_t array which
3078 * contains the new group list
3079 * groupcount The cound of valid groups which
3080 * are contained in 'groups'
3081 * gmuid KAUTH_UID_NONE -or- the new
3082 * group membership UID
3084 * Returns: (kauth_cred_t) The updated credential
3086 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3087 * setting, so if you don't want it to change, pass it the
3088 * previous value, explicitly.
3090 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3091 * if it returns a credential other than the one it is passed,
3092 * will have dropped the reference on the passed credential. All
3093 * callers should be aware of this, and treat this function as an
3094 * unref + ref, potentially on different credentials.
3096 * Because of this, the caller is expected to take its own
3097 * reference on the credential passed as the first parameter,
3098 * and be prepared to release the reference on the credential
3099 * that is returned to them, if it is not intended to be a
3100 * persistent reference.
3102 * XXX: Changes are determined in ordinal order - if the caller pasess
3103 * in the same groups list that is already present in the
3104 * credential, but the members are in a different order, even if
3105 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3106 * is considered a modification to the credential, and a new
3107 * credential is created.
3109 * This should perhaps be better optimized, but it is considered
3110 * to be the caller's problem.
3113 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
3116 struct ucred temp_cred
;
3118 NULLCRED_CHECK(cred
);
3121 * We don't need to do anything if the given list of groups does not
3124 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
3125 for (i
= 0; i
< groupcount
; i
++) {
3126 if (cred
->cr_groups
[i
] != groups
[i
])
3129 if (i
== groupcount
) {
3130 /* no change needed */
3136 * Look up in cred hash table to see if we have a matching credential
3137 * with new values. If we are setting or clearing the gmuid, then
3138 * update the cr_flags, since clearing it is sticky. This permits an
3139 * opt-out of memberd processing using setgroups(), and an opt-in
3140 * using initgroups(). This is required for POSIX conformance.
3142 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3143 temp_cred
.cr_ngroups
= groupcount
;
3144 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
3145 temp_cred
.cr_gmuid
= gmuid
;
3146 if (gmuid
== KAUTH_UID_NONE
)
3147 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3149 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3151 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3156 * kauth_cred_setuidgid
3158 * Description: Update the given credential using the UID and GID arguments.
3159 * The given UID is used to set the effective UID, real UID, and
3160 * saved UID. The given GID is used to set the effective GID,
3161 * real GID, and saved GID.
3163 * Parameters: cred The original credential
3164 * uid The new UID to use
3165 * gid The new GID to use
3167 * Returns: (kauth_cred_t) The updated credential
3169 * Notes: We set the gmuid to uid if the credential we are inheriting
3170 * from has not opted out of memberd participation; otherwise
3171 * we set it to KAUTH_UID_NONE
3173 * This code is only ever called from the per-thread credential
3174 * code path in the "set per thread credential" case; and in
3175 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3178 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3179 * if it returns a credential other than the one it is passed,
3180 * will have dropped the reference on the passed credential. All
3181 * callers should be aware of this, and treat this function as an
3182 * unref + ref, potentially on different credentials.
3184 * Because of this, the caller is expected to take its own
3185 * reference on the credential passed as the first parameter,
3186 * and be prepared to release the reference on the credential
3187 * that is returned to them, if it is not intended to be a
3188 * persistent reference.
3191 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3193 struct ucred temp_cred
;
3195 NULLCRED_CHECK(cred
);
3198 * We don't need to do anything if the effective, real and saved
3199 * user IDs are already the same as the user ID passed into us.
3201 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
3202 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
3203 /* no change needed */
3208 * Look up in cred hash table to see if we have a matching credential
3209 * with the new values.
3211 bzero(&temp_cred
, sizeof(temp_cred
));
3212 temp_cred
.cr_uid
= uid
;
3213 temp_cred
.cr_ruid
= uid
;
3214 temp_cred
.cr_svuid
= uid
;
3215 temp_cred
.cr_flags
= cred
->cr_flags
;
3216 /* inherit the opt-out of memberd */
3217 if (cred
->cr_flags
& CRF_NOMEMBERD
) {
3218 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3219 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3221 temp_cred
.cr_gmuid
= uid
;
3222 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3224 temp_cred
.cr_ngroups
= 1;
3225 /* displacing a supplementary group opts us out of memberd */
3226 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3227 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3228 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3230 temp_cred
.cr_rgid
= gid
;
3231 temp_cred
.cr_svgid
= gid
;
3233 temp_cred
.cr_label
= cred
->cr_label
;
3236 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3241 * kauth_cred_setsvuidgid
3243 * Description: Function used by execve to set the saved uid and gid values
3244 * for suid/sgid programs
3246 * Parameters: cred The credential to update
3247 * uid The saved uid to set
3248 * gid The saved gid to set
3250 * Returns: (kauth_cred_t) The updated credential
3252 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3253 * if it returns a credential other than the one it is passed,
3254 * will have dropped the reference on the passed credential. All
3255 * callers should be aware of this, and treat this function as an
3256 * unref + ref, potentially on different credentials.
3258 * Because of this, the caller is expected to take its own
3259 * reference on the credential passed as the first parameter,
3260 * and be prepared to release the reference on the credential
3261 * that is returned to them, if it is not intended to be a
3262 * persistent reference.
3265 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3267 struct ucred temp_cred
;
3269 NULLCRED_CHECK(cred
);
3270 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
3273 * We don't need to do anything if the effective, real and saved
3274 * uids are already the same as the uid provided. This check is
3275 * likely insufficient.
3277 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
3278 /* no change needed */
3281 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3283 /* look up in cred hash table to see if we have a matching credential
3286 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3287 temp_cred
.cr_svuid
= uid
;
3288 temp_cred
.cr_svgid
= gid
;
3290 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3295 * kauth_cred_setauditinfo
3297 * Description: Update the given credential using the given au_session_t.
3299 * Parameters: cred The original credential
3300 * auditinfo_p Pointer to ne audit information
3302 * Returns: (kauth_cred_t) The updated credential
3304 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3305 * if it returns a credential other than the one it is passed,
3306 * will have dropped the reference on the passed credential. All
3307 * callers should be aware of this, and treat this function as an
3308 * unref + ref, potentially on different credentials.
3310 * Because of this, the caller is expected to take its own
3311 * reference on the credential passed as the first parameter,
3312 * and be prepared to release the reference on the credential
3313 * that is returned to them, if it is not intended to be a
3314 * persistent reference.
3317 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
3319 struct ucred temp_cred
;
3321 NULLCRED_CHECK(cred
);
3324 * We don't need to do anything if the audit info is already the
3325 * same as the audit info in the credential provided.
3327 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
3328 /* no change needed */
3332 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3333 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
3334 /* XXX the following will go away with cr_au */
3335 temp_cred
.cr_au
.ai_auid
= auditinfo_p
->as_aia_p
->ai_auid
;
3336 temp_cred
.cr_au
.ai_mask
.am_success
=
3337 auditinfo_p
->as_mask
.am_success
;
3338 temp_cred
.cr_au
.ai_mask
.am_failure
=
3339 auditinfo_p
->as_mask
.am_failure
;
3340 temp_cred
.cr_au
.ai_termid
.port
=
3341 auditinfo_p
->as_aia_p
->ai_termid
.at_port
;
3342 temp_cred
.cr_au
.ai_termid
.machine
=
3343 auditinfo_p
->as_aia_p
->ai_termid
.at_addr
[0];
3344 temp_cred
.cr_au
.ai_asid
= auditinfo_p
->as_aia_p
->ai_asid
;
3347 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
3352 * kauth_cred_label_update
3354 * Description: Update the MAC label associated with a credential
3356 * Parameters: cred The original credential
3357 * label The MAC label to set
3359 * Returns: (kauth_cred_t) The updated credential
3361 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3362 * if it returns a credential other than the one it is passed,
3363 * will have dropped the reference on the passed credential. All
3364 * callers should be aware of this, and treat this function as an
3365 * unref + ref, potentially on different credentials.
3367 * Because of this, the caller is expected to take its own
3368 * reference on the credential passed as the first parameter,
3369 * and be prepared to release the reference on the credential
3370 * that is returned to them, if it is not intended to be a
3371 * persistent reference.
3374 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
3376 kauth_cred_t newcred
;
3377 struct ucred temp_cred
;
3379 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3381 mac_cred_label_init(&temp_cred
);
3382 mac_cred_label_associate(cred
, &temp_cred
);
3383 mac_cred_label_update(&temp_cred
, label
);
3385 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3386 mac_cred_label_destroy(&temp_cred
);
3391 * kauth_cred_label_update_execve
3393 * Description: Update the MAC label associated with a credential as
3396 * Parameters: cred The original credential
3398 * scriptl The script MAC label
3399 * execl The executable MAC label
3400 * disjointp Pointer to flag to set if old
3401 * and returned credentials are
3404 * Returns: (kauth_cred_t) The updated credential
3407 * *disjointp Set to 1 for disjoint creds
3409 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3410 * if it returns a credential other than the one it is passed,
3411 * will have dropped the reference on the passed credential. All
3412 * callers should be aware of this, and treat this function as an
3413 * unref + ref, potentially on different credentials.
3415 * Because of this, the caller is expected to take its own
3416 * reference on the credential passed as the first parameter,
3417 * and be prepared to release the reference on the credential
3418 * that is returned to them, if it is not intended to be a
3419 * persistent reference.
3423 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
3424 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
3427 kauth_cred_t newcred
;
3428 struct ucred temp_cred
;
3430 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3432 mac_cred_label_init(&temp_cred
);
3433 mac_cred_label_associate(cred
, &temp_cred
);
3434 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
3435 vp
, scriptl
, execl
);
3437 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3438 mac_cred_label_destroy(&temp_cred
);
3443 * kauth_proc_label_update
3445 * Description: Update the label inside the credential associated with the process.
3447 * Parameters: p The process to modify
3448 * label The label to place in the process credential
3450 * Notes: The credential associated with the process may change as a result
3451 * of this call. The caller should not assume the process reference to
3452 * the old credential still exists.
3454 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
3456 kauth_cred_t my_cred
, my_new_cred
;
3458 my_cred
= kauth_cred_proc_ref(p
);
3460 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
3462 /* get current credential and take a reference while we muck with it */
3466 * Set the credential with new info. If there is no change,
3467 * we get back the same credential we passed in; if there is
3468 * a change, we drop the reference on the credential we
3469 * passed in. The subsequent compare is safe, because it is
3470 * a pointer compare rather than a contents compare.
3472 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
3473 if (my_cred
!= my_new_cred
) {
3475 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
);
3479 * We need to protect for a race where another thread
3480 * also changed the credential after we took our
3481 * reference. If p_ucred has changed then we should
3482 * restart this again with the new cred.
3484 if (p
->p_ucred
!= my_cred
) {
3486 kauth_cred_unref(&my_new_cred
);
3487 my_cred
= kauth_cred_proc_ref(p
);
3491 p
->p_ucred
= my_new_cred
;
3492 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3497 /* Drop old proc reference or our extra reference */
3498 kauth_cred_unref(&my_cred
);
3504 * kauth_proc_label_update_execve
3506 * Description: Update the label inside the credential associated with the
3507 * process as part of a transitioning execve. The label will
3508 * be updated by the policies as part of this processing, not
3509 * provided up front.
3511 * Parameters: p The process to modify
3512 * ctx The context of the exec
3513 * vp The vnode being exec'ed
3514 * scriptl The script MAC label
3515 * execl The executable MAC label
3517 * Returns: 0 Label update did not make credential
3519 * 1 Label update caused credential to be
3522 * Notes: The credential associated with the process WILL change as a
3523 * result of this call. The caller should not assume the process
3524 * reference to the old credential still exists.
3527 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
3528 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
3530 kauth_cred_t my_cred
, my_new_cred
;
3533 my_cred
= kauth_cred_proc_ref(p
);
3535 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
3537 /* get current credential and take a reference while we muck with it */
3541 * Set the credential with new info. If there is no change,
3542 * we get back the same credential we passed in; if there is
3543 * a change, we drop the reference on the credential we
3544 * passed in. The subsequent compare is safe, because it is
3545 * a pointer compare rather than a contents compare.
3547 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
3548 if (my_cred
!= my_new_cred
) {
3550 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
);
3554 * We need to protect for a race where another thread
3555 * also changed the credential after we took our
3556 * reference. If p_ucred has changed then we should
3557 * restart this again with the new cred.
3559 if (p
->p_ucred
!= my_cred
) {
3561 kauth_cred_unref(&my_new_cred
);
3562 my_cred
= kauth_cred_proc_ref(p
);
3566 p
->p_ucred
= my_new_cred
;
3567 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3572 /* Drop old proc reference or our extra reference */
3573 kauth_cred_unref(&my_cred
);
3580 * for temporary binary compatibility
3582 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
3584 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
3586 return kauth_cred_label_update(cred
, label
);
3589 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
3591 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
3593 return kauth_proc_label_update(p
, label
);
3599 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
3600 * Since we cannot build our export lists based on the kernel configuration we need
3604 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
3610 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
3617 * for temporary binary compatibility
3619 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
3621 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
3626 int kauth_proc_setlabel(struct proc
*p
, void *label
);
3628 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
3638 * Description: Add a reference to the passed credential
3640 * Parameters: cred The credential to reference
3644 * Notes: This function adds a reference to the provided credential;
3645 * the existing reference on the credential is assumed to be
3646 * held stable over this operation by taking the appropriate
3647 * lock to protect the pointer from which it is being referenced,
3648 * if necessary (e.g. the proc lock is held over the call if the
3649 * credential being referenced is from p_ucred, the vnode lock
3650 * if from the per vnode name cache cred cache, and so on).
3652 * This is safe from the kauth_cred_unref() path, since an atomic
3653 * add is used, and the unref path specifically checks to see that
3654 * the value has not been changed to add a reference between the
3655 * time the credential is unreferenced by another pointer and the
3656 * time it is unreferenced from the cred hash cache.
3659 kauth_cred_ref(kauth_cred_t cred
)
3663 NULLCRED_CHECK(cred
);
3665 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
3668 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
3670 #if 0 // use this to watch a specific credential
3671 if ( is_target_cred( cred
) != 0 ) {
3681 * kauth_cred_unref_hashlocked
3683 * Description: release a credential reference; when the last reference is
3684 * released, the credential will be freed.
3686 * Parameters: credp Pointer to address containing
3687 * credential to be freed
3692 * *credp Set to NOCRED
3694 * Notes: This function assumes the credential hash lock is held.
3696 * This function is internal use only, since the hash lock is
3697 * scoped to this compilation unit.
3699 * This function destroys the contents of the pointer passed by
3700 * the caller to prevent the caller accidently attempting to
3701 * release a given reference twice in error.
3703 * The last reference is considered to be released when a release
3704 * of a credential of a reference count of 2 occurs; this is an
3705 * intended effect, to take into accout the reference held by
3706 * the credential hash, which is released at the same time.
3709 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
3713 KAUTH_CRED_HASH_LOCK_ASSERT();
3714 NULLCRED_CHECK(*credp
);
3716 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
3720 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
3722 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
3725 #if 0 // use this to watch a specific credential
3726 if ( is_target_cred( *credp
) != 0 ) {
3732 * If the old_value is 2, then we have just released the last external
3733 * reference to this credential
3735 if (old_value
< 3) {
3736 /* The last absolute reference is our credential hash table */
3737 kauth_cred_remove(*credp
);
3746 * Description: Release a credential reference while holding the credential
3747 * hash lock; when the last reference is released, the credential
3750 * Parameters: credp Pointer to address containing
3751 * credential to be freed
3756 * *credp Set to NOCRED
3758 * Notes: See kauth_cred_unref_hashlocked() for more information.
3762 kauth_cred_unref(kauth_cred_t
*credp
)
3764 KAUTH_CRED_HASH_LOCK();
3765 kauth_cred_unref_hashlocked(credp
);
3766 KAUTH_CRED_HASH_UNLOCK();
3774 * Description: release a credential reference; when the last reference is
3775 * released, the credential will be freed
3777 * Parameters: cred Credential to release
3781 * DEPRECATED: This interface is obsolete due to a failure to clear out the
3782 * clear the pointer in the caller to avoid multiple releases of
3783 * the same credential. The currently recommended interface is
3784 * kauth_cred_unref().
3787 kauth_cred_rele(kauth_cred_t cred
)
3789 kauth_cred_unref(&cred
);
3791 #endif /* !__LP64__ */
3797 * Description: Duplicate a credential via alloc and copy; the new credential
3800 * Parameters: cred The credential to duplicate
3802 * Returns: (kauth_cred_t) The duplicate credential
3804 * Notes: The typical value to calling this routine is if you are going
3805 * to modify an existing credential, and expect to need a new one
3806 * from the hash cache.
3808 * This should probably not be used in the majority of cases;
3809 * if you are using it instead of kauth_cred_create(), you are
3810 * likely making a mistake.
3812 * The newly allocated credential is copied as part of the
3813 * allocation process, with the exception of the reference
3814 * count, which is set to 1 to indicate a single reference
3815 * held by the caller.
3817 * Since newly allocated credentials have no external pointers
3818 * referencing them, prior to making them visible in an externally
3819 * visible pointer (e.g. by adding them to the credential hash
3820 * cache) is the only legal time in which an existing credential
3821 * can be safely iinitialized or modified directly.
3823 * After initialization, the caller is expected to call the
3824 * function kauth_cred_add() to add the credential to the hash
3825 * cache, after which time it's frozen and becomes publically
3828 * The release protocol depends on kauth_hash_add() being called
3829 * before kauth_cred_rele() (there is a diagnostic panic which
3830 * will trigger if this protocol is not observed).
3834 kauth_cred_dup(kauth_cred_t cred
)
3836 kauth_cred_t newcred
;
3838 struct label
*temp_label
;
3842 if (cred
== NOCRED
|| cred
== FSCRED
)
3843 panic("kauth_cred_dup: bad credential");
3845 newcred
= kauth_cred_alloc();
3846 if (newcred
!= NULL
) {
3848 temp_label
= newcred
->cr_label
;
3850 bcopy(cred
, newcred
, sizeof(*newcred
));
3852 newcred
->cr_label
= temp_label
;
3853 mac_cred_label_associate(cred
, newcred
);
3855 AUDIT_SESSION_REF(cred
);
3856 newcred
->cr_ref
= 1;
3862 * kauth_cred_copy_real
3864 * Description: Returns a credential based on the passed credential but which
3865 * reflects the real rather than effective UID and GID.
3867 * Parameters: cred The credential from which to
3868 * derive the new credential
3870 * Returns: (kauth_cred_t) The copied credential
3872 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
3873 * result, the caller is responsible for dropping BOTH the
3874 * additional reference on the passed cred (if any), and the
3875 * credential returned by this function. The drop should be
3876 * via the satnadr kauth_cred_unref() KPI.
3879 kauth_cred_copy_real(kauth_cred_t cred
)
3881 kauth_cred_t newcred
= NULL
, found_cred
;
3882 struct ucred temp_cred
;
3884 /* if the credential is already 'real', just take a reference */
3885 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
3886 (cred
->cr_rgid
== cred
->cr_gid
)) {
3887 kauth_cred_ref(cred
);
3892 * Look up in cred hash table to see if we have a matching credential
3893 * with the new values.
3895 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3896 temp_cred
.cr_uid
= cred
->cr_ruid
;
3897 /* displacing a supplementary group opts us out of memberd */
3898 if (kauth_cred_change_egid(&temp_cred
, cred
->cr_rgid
)) {
3899 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3900 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3903 * If the cred is not opted out, make sure we are using the r/euid
3906 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
3907 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
3912 KAUTH_CRED_HASH_LOCK();
3913 found_cred
= kauth_cred_find(&temp_cred
);
3914 if (found_cred
== cred
) {
3915 /* same cred so just bail */
3916 KAUTH_CRED_HASH_UNLOCK();
3919 if (found_cred
!= NULL
) {
3921 * Found a match so we bump reference count on new
3922 * one. We leave the old one alone.
3924 kauth_cred_ref(found_cred
);
3925 KAUTH_CRED_HASH_UNLOCK();
3930 * Must allocate a new credential, copy in old credential
3931 * data and update the real user and group IDs.
3933 newcred
= kauth_cred_dup(&temp_cred
);
3934 err
= kauth_cred_add(newcred
);
3935 KAUTH_CRED_HASH_UNLOCK();
3937 /* Retry if kauth_cred_add() fails */
3941 mac_cred_label_destroy(newcred
);
3943 AUDIT_SESSION_UNREF(newcred
);
3945 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
3956 * Description: Common code to update a credential
3958 * Parameters: old_cred Reference counted credential
3960 * model_cred Non-reference counted model
3961 * credential to apply to the
3962 * credential to be updated
3963 * retain_auditinfo Flag as to whether or not the
3964 * audit information should be
3965 * copied from the old_cred into
3968 * Returns: (kauth_cred_t) The updated credential
3970 * IMPORTANT: This function will potentially return a credential other than
3971 * the one it is passed, and if so, it will have dropped the
3972 * reference on the passed credential. All callers should be
3973 * aware of this, and treat this function as an unref + ref,
3974 * potentially on different credentials.
3976 * Because of this, the caller is expected to take its own
3977 * reference on the credential passed as the first parameter,
3978 * and be prepared to release the reference on the credential
3979 * that is returned to them, if it is not intended to be a
3980 * persistent reference.
3983 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
3984 boolean_t retain_auditinfo
)
3986 kauth_cred_t found_cred
, new_cred
= NULL
;
3989 * Make sure we carry the auditinfo forward to the new credential
3990 * unless we are actually updating the auditinfo.
3992 if (retain_auditinfo
) {
3993 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
3994 sizeof(model_cred
->cr_audit
));
3995 /* XXX following bcopy will go away with cr_au */
3996 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
,
3997 sizeof(model_cred
->cr_au
));
4003 KAUTH_CRED_HASH_LOCK();
4004 found_cred
= kauth_cred_find(model_cred
);
4005 if (found_cred
== old_cred
) {
4006 /* same cred so just bail */
4007 KAUTH_CRED_HASH_UNLOCK();
4010 if (found_cred
!= NULL
) {
4011 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
4013 * Found a match so we bump reference count on new
4014 * one and decrement reference count on the old one.
4016 kauth_cred_ref(found_cred
);
4017 kauth_cred_unref_hashlocked(&old_cred
);
4018 KAUTH_CRED_HASH_UNLOCK();
4023 * Must allocate a new credential using the model. also
4024 * adds the new credential to the credential hash table.
4026 new_cred
= kauth_cred_dup(model_cred
);
4027 err
= kauth_cred_add(new_cred
);
4028 KAUTH_CRED_HASH_UNLOCK();
4030 /* retry if kauth_cred_add returns non zero value */
4034 mac_cred_label_destroy(new_cred
);
4036 AUDIT_SESSION_UNREF(new_cred
);
4038 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
4042 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
4043 kauth_cred_unref(&old_cred
);
4051 * Description: Add the given credential to our credential hash table and
4052 * take an additional reference to account for our use of the
4053 * credential in the hash table
4055 * Parameters: new_cred Credential to insert into cred
4058 * Returns: 0 Success
4059 * -1 Hash insertion failed: caller
4062 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4064 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4067 kauth_cred_add(kauth_cred_t new_cred
)
4071 KAUTH_CRED_HASH_LOCK_ASSERT();
4073 hash_key
= kauth_cred_get_hashkey(new_cred
);
4074 hash_key
%= kauth_cred_table_size
;
4076 /* race fix - there is a window where another matching credential
4077 * could have been inserted between the time this one was created and we
4078 * got the hash lock. If we find a match return an error and have the
4081 if (kauth_cred_find(new_cred
) != NULL
) {
4085 /* take a reference for our use in credential hash table */
4086 kauth_cred_ref(new_cred
);
4088 /* insert the credential into the hash table */
4089 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
4098 * Description: Remove the given credential from our credential hash table
4100 * Parameters: cred Credential to remove from cred
4105 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4107 * Notes: The check for the reference increment after entry is generally
4108 * agree to be safe, since we use atomic operations, and the
4109 * following code occurs with the hash lock held; in theory, this
4110 * protects us from the 2->1 reference that gets us here.
4113 kauth_cred_remove(kauth_cred_t cred
)
4116 kauth_cred_t found_cred
;
4118 hash_key
= kauth_cred_get_hashkey(cred
);
4119 hash_key
%= kauth_cred_table_size
;
4122 if (cred
->cr_ref
< 1)
4123 panic("cred reference underflow");
4124 if (cred
->cr_ref
> 1)
4125 return; /* someone else got a ref */
4127 /* Find cred in the credential hash table */
4128 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4129 if (found_cred
== cred
) {
4130 /* found a match, remove it from the hash table */
4131 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
4133 mac_cred_label_destroy(cred
);
4135 AUDIT_SESSION_UNREF(cred
);
4138 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
4139 #if KAUTH_CRED_HASH_DEBUG
4146 /* Did not find a match... this should not happen! XXX Make panic? */
4147 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
4155 * Description: Using the given credential data, look for a match in our
4156 * credential hash table
4158 * Parameters: cred Credential to lookup in cred
4161 * Returns: NULL Not found
4162 * !NULL Matching cedential already in
4165 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4168 kauth_cred_find(kauth_cred_t cred
)
4171 kauth_cred_t found_cred
;
4173 KAUTH_CRED_HASH_LOCK_ASSERT();
4175 #if KAUTH_CRED_HASH_DEBUG
4176 static int test_count
= 0;
4179 if ((test_count
% 200) == 0) {
4180 kauth_cred_hash_print();
4184 hash_key
= kauth_cred_get_hashkey(cred
);
4185 hash_key
%= kauth_cred_table_size
;
4187 /* Find cred in the credential hash table */
4188 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4192 * don't worry about the label unless the flags in
4193 * either credential tell us to.
4195 if ((found_cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0 ||
4196 (cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) {
4197 /* include the label pointer in the compare */
4198 match
= (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4199 (sizeof(struct ucred
) -
4200 offsetof(struct ucred
, cr_uid
))) == 0);
4202 /* flags have to match, but skip the label in bcmp */
4203 match
= (found_cred
->cr_flags
== cred
->cr_flags
&&
4204 bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4205 (offsetof(struct ucred
, cr_label
) -
4206 offsetof(struct ucred
, cr_uid
))) == 0);
4213 /* No match found */
4222 * Description: Generates a hash key using data that makes up a credential;
4225 * Parameters: datap Pointer to data to hash
4226 * data_len Count of bytes to hash
4227 * start_key Start key value
4229 * Returns: (u_long) Returned hash key
4231 static inline u_long
4232 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4234 u_long hash_key
= start_key
;
4237 while (data_len
> 0) {
4238 hash_key
= (hash_key
<< 4) + *datap
++;
4239 temp
= hash_key
& 0xF0000000;
4241 hash_key
^= temp
>> 24;
4251 * kauth_cred_get_hashkey
4253 * Description: Generate a hash key using data that makes up a credential;
4254 * based on ElfHash. We hash on the entire credential data,
4255 * not including the ref count or the TAILQ, which are mutable;
4256 * everything else isn't.
4258 * We also avoid the label (if the flag is not set saying the
4259 * label is actually enforced).
4261 * Parameters: cred Credential for which hash is
4264 * Returns: (u_long) Returned hash key
4267 kauth_cred_get_hashkey(kauth_cred_t cred
)
4269 u_long hash_key
= 0;
4271 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
4272 ((cred
->cr_flags
& CRF_MAC_ENFORCE
) ?
4273 sizeof(struct ucred
) : offsetof(struct ucred
, cr_label
)) -
4274 offsetof(struct ucred
, cr_uid
),
4280 #if KAUTH_CRED_HASH_DEBUG
4282 * kauth_cred_hash_print
4284 * Description: Print out cred hash cache table information for debugging
4285 * purposes, including the credential contents
4287 * Parameters: (void)
4291 * Implicit returns: Results in console output
4294 kauth_cred_hash_print(void)
4297 kauth_cred_t found_cred
;
4299 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
4300 /* count slot hits, misses, collisions, and max depth */
4301 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4302 printf("[%02d] ", i
);
4304 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4309 kauth_cred_print(found_cred
);
4313 printf("NOCRED \n");
4317 #endif /* KAUTH_CRED_HASH_DEBUG */
4320 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4324 * Description: Print out an individual credential's contents for debugging
4327 * Parameters: cred The credential to print out
4331 * Implicit returns: Results in console output
4334 kauth_cred_print(kauth_cred_t cred
)
4338 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
);
4339 printf("group count %d gids ", cred
->cr_ngroups
);
4340 for (i
= 0; i
< NGROUPS
; i
++) {
4343 printf("%d ", cred
->cr_groups
[i
]);
4345 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
4346 printf("auditinfo_addr %d %d %d %d %d %d\n",
4347 cred
->cr_audit
.s_aia_p
->ai_auid
,
4348 cred
->cr_audit
.as_mask
.am_success
,
4349 cred
->cr_audit
.as_mask
.am_failure
,
4350 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
4351 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
4352 cred
->cr_audit
.as_aia_p
->ai_asid
);
4355 int is_target_cred( kauth_cred_t the_cred
)
4357 if ( the_cred
->cr_uid
!= 0 )
4359 if ( the_cred
->cr_ruid
!= 0 )
4361 if ( the_cred
->cr_svuid
!= 0 )
4363 if ( the_cred
->cr_ngroups
!= 11 )
4365 if ( the_cred
->cr_groups
[0] != 11 )
4367 if ( the_cred
->cr_groups
[1] != 81 )
4369 if ( the_cred
->cr_groups
[2] != 63947 )
4371 if ( the_cred
->cr_groups
[3] != 80288 )
4373 if ( the_cred
->cr_groups
[4] != 89006 )
4375 if ( the_cred
->cr_groups
[5] != 52173 )
4377 if ( the_cred
->cr_groups
[6] != 84524 )
4379 if ( the_cred
->cr_groups
[7] != 79 )
4381 if ( the_cred
->cr_groups
[8] != 80292 )
4383 if ( the_cred
->cr_groups
[9] != 80 )
4385 if ( the_cred
->cr_groups
[10] != 90824 )
4387 if ( the_cred
->cr_rgid
!= 11 )
4389 if ( the_cred
->cr_svgid
!= 11 )
4391 if ( the_cred
->cr_gmuid
!= 3475 )
4393 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
4396 if ( the_cred->cr_audit.as_mask.am_success != 0 )
4398 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
4400 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
4402 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
4404 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
4406 if ( the_cred->cr_flags != 0 )
4409 return( -1 ); // found target cred
4412 void get_backtrace( void )
4415 void * my_stack
[ MAX_STACK_DEPTH
];
4418 if ( cred_debug_buf_p
== NULL
) {
4419 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
4420 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
4423 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
4424 /* buffer is full */
4428 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
4429 if ( my_depth
== 0 ) {
4430 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
4434 /* fill new backtrace */
4435 my_slot
= cred_debug_buf_p
->next_slot
;
4436 cred_debug_buf_p
->next_slot
++;
4437 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
4438 for ( i
= 0; i
< my_depth
; i
++ ) {
4439 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
4446 /* subset of struct ucred for use in sysctl_dump_creds */
4447 struct debug_ucred
{
4449 u_long cr_ref
; /* reference count */
4450 uid_t cr_uid
; /* effective user id */
4451 uid_t cr_ruid
; /* real user id */
4452 uid_t cr_svuid
; /* saved user id */
4453 short cr_ngroups
; /* number of groups in advisory list */
4454 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
4455 gid_t cr_rgid
; /* real group id */
4456 gid_t cr_svgid
; /* saved group id */
4457 uid_t cr_gmuid
; /* UID for group membership purposes */
4458 struct auditinfo_addr cr_audit
; /* user auditing data. */
4459 void *cr_label
; /* MACF label */
4460 int cr_flags
; /* flags on credential */
4462 typedef struct debug_ucred debug_ucred
;
4464 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
4465 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
4468 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
4472 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4474 int i
, j
, counter
= 0;
4477 kauth_cred_t found_cred
;
4478 debug_ucred
* cred_listp
;
4479 debug_ucred
* nextp
;
4481 /* This is a readonly node. */
4482 if (req
->newptr
!= USER_ADDR_NULL
)
4485 /* calculate space needed */
4486 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4487 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4492 /* they are querying us so just return the space required. */
4493 if (req
->oldptr
== USER_ADDR_NULL
) {
4494 counter
+= 10; // add in some padding;
4495 req
->oldidx
= counter
* sizeof(debug_ucred
);
4499 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4500 if ( cred_listp
== NULL
) {
4504 /* fill in creds to send back */
4507 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4508 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4509 nextp
->credp
= found_cred
;
4510 nextp
->cr_ref
= found_cred
->cr_ref
;
4511 nextp
->cr_uid
= found_cred
->cr_uid
;
4512 nextp
->cr_ruid
= found_cred
->cr_ruid
;
4513 nextp
->cr_svuid
= found_cred
->cr_svuid
;
4514 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
4515 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
4516 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
4518 nextp
->cr_rgid
= found_cred
->cr_rgid
;
4519 nextp
->cr_svgid
= found_cred
->cr_svgid
;
4520 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
4521 nextp
->cr_audit
.ai_auid
=
4522 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
4523 nextp
->cr_audit
.ai_mask
.am_success
=
4524 found_cred
->cr_audit
.as_mask
.am_success
;
4525 nextp
->cr_audit
.ai_mask
.am_failure
=
4526 found_cred
->cr_audit
.as_mask
.am_failure
;
4527 nextp
->cr_audit
.ai_termid
.at_port
=
4528 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
4529 nextp
->cr_audit
.ai_termid
.at_type
=
4530 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
4531 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
4532 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
4533 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
4534 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
4535 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
4536 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
4537 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
4538 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
4539 nextp
->cr_audit
.ai_asid
=
4540 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
4541 nextp
->cr_audit
.ai_flags
=
4542 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
4543 nextp
->cr_label
= found_cred
->cr_label
;
4544 nextp
->cr_flags
= found_cred
->cr_flags
;
4546 space
+= sizeof(debug_ucred
);
4547 if ( space
> req
->oldlen
) {
4548 FREE(cred_listp
, M_TEMP
);
4553 req
->oldlen
= space
;
4554 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
4555 FREE(cred_listp
, M_TEMP
);
4560 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
4561 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
4564 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
4568 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4573 cred_debug_buffer
* bt_bufp
;
4574 cred_backtrace
* nextp
;
4576 /* This is a readonly node. */
4577 if (req
->newptr
!= USER_ADDR_NULL
)
4580 if ( cred_debug_buf_p
== NULL
) {
4584 /* calculate space needed */
4585 space
= sizeof( cred_debug_buf_p
->next_slot
);
4586 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
4588 /* they are querying us so just return the space required. */
4589 if (req
->oldptr
== USER_ADDR_NULL
) {
4590 req
->oldidx
= space
;
4594 if ( space
> req
->oldlen
) {
4598 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4599 if ( bt_bufp
== NULL
) {
4603 /* fill in backtrace info to send back */
4604 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
4605 space
= sizeof(bt_bufp
->next_slot
);
4607 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
4608 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
4609 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
4610 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
4611 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
4613 space
+= sizeof(*nextp
);
4616 req
->oldlen
= space
;
4617 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
4618 FREE(bt_bufp
, M_TEMP
);
4622 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */