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
;
148 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
150 struct kauth_resolver_work
{
151 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
152 struct kauth_identity_extlookup kr_work
;
154 uint64_t kr_subtime
; /* submission time */
157 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
158 #define KAUTH_REQUEST_SUBMITTED (1<<1)
159 #define KAUTH_REQUEST_DONE (1<<2)
163 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
164 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
165 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
167 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
);
168 static int kauth_resolver_complete(user_addr_t message
);
169 static int kauth_resolver_getwork(user_addr_t message
);
170 static int kauth_resolver_getwork2(user_addr_t message
);
172 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
173 static int kauth_cred_primes_index
= 0;
174 static int kauth_cred_table_size
= 0;
176 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
177 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
179 /* Weighted moving average for resolver response time */
180 static struct kco_moving_average resolver_ma
;
182 #define KAUTH_CRED_HASH_DEBUG 0
184 static int kauth_cred_add(kauth_cred_t new_cred
);
185 static void kauth_cred_remove(kauth_cred_t cred
);
186 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
187 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
188 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
189 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
191 #if KAUTH_CRED_HASH_DEBUG
192 static int kauth_cred_count
= 0;
193 static void kauth_cred_hash_print(void);
194 static void kauth_cred_print(kauth_cred_t cred
);
199 * kauth_resolver_init
201 * Description: Initialize the daemon side of the credential identity resolver
207 * Notes: Intialize the credential identity resolver for use; the
208 * credential identity resolver is the KPI used by the user
209 * space credential identity resolver daemon to communicate
210 * with the kernel via the identitysvc() system call..
212 * This is how membership in more than 16 groups (1 effective
213 * and 15 supplementary) is supported, and also how UID's,
214 * UUID's, and so on, are translated to/from POSIX credential
217 * The credential identity resolver operates by attempting to
218 * determine identity first from the credential, then from
219 * the kernel credential identity cache, and finally by
220 * enqueueing a request to a user space daemon.
222 * This function is called from kauth_init() in the file
223 * kern_authorization.c.
226 kauth_resolver_init(void)
228 TAILQ_INIT(&kauth_resolver_unsubmitted
);
229 TAILQ_INIT(&kauth_resolver_submitted
);
230 TAILQ_INIT(&kauth_resolver_done
);
231 kauth_resolver_sequence
= 31337;
232 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
235 * 110% of average response time is "too long" and should be reported
237 kco_ma_init(&resolver_ma
, 110, KCO_MA_F_WMA
);
242 * kauth_resolver_submit
244 * Description: Submit an external credential identity resolution request to
245 * the user space daemon.
247 * Parameters: lkp A pointer to an external
251 * EWOULDBLOCK No resolver registered
252 * EINTR Operation interrupted (e.g. by
254 * ENOMEM Could not allocate work item
255 * workp->kr_result:??? An error from the user space
256 * daemon (includes ENOENT!)
258 * Notes: Allocate a work queue entry, submit the work and wait for
259 * the operation to either complete or time out. Outstanding
260 * operations may also be cancelled.
263 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
)
265 struct kauth_resolver_work
*workp
, *killp
;
267 int error
, shouldfree
;
270 /* no point actually blocking if the resolver isn't up yet */
271 if (kauth_resolver_identity
== 0) {
273 * We've already waited an initial <kauth_resolver_timeout>
274 * seconds with no result.
276 * Sleep on a stack address so no one wakes us before timeout;
277 * we sleep a half a second in case we are a high priority
278 * process, so that memberd doesn't starve while we are in a
279 * tight loop between user and kernel, eating all the CPU.
281 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
282 if (kauth_resolver_identity
== 0) {
284 * if things haven't changed while we were asleep,
285 * tell the caller we couldn't get an authoritative
292 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
296 workp
->kr_work
= *lkp
;
298 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
299 workp
->kr_result
= 0;
302 * We insert the request onto the unsubmitted queue, the call in from
303 * the resolver will it to the submitted thread when appropriate.
305 KAUTH_RESOLVER_LOCK();
306 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
307 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
310 * XXX As an optimisation, we could check the queue for identical
311 * XXX items and coalesce them
313 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
315 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
317 /* we could compute a better timeout here */
318 ts
.tv_sec
= kauth_resolver_timeout
;
320 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
321 /* request has been completed? */
322 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
324 /* woken because the resolver has died? */
325 if (kauth_resolver_identity
== 0) {
335 * Update the moving average of how long it took; if it took longer
336 * than the time threshold, then we complain about it being slow.
338 duration
= mach_absolute_time() - workp
->kr_subtime
;
339 if (kco_ma_addsample(&resolver_ma
, duration
)) {
341 uint64_t old_average
;
345 /* If we can't get information, don't log anything */
346 if (kco_ma_info(&resolver_ma
, KCO_MA_F_WMA
, &average
, &old_average
, &threshold
, &count
)) {
347 char pname
[MAXCOMLEN
+1] = "(NULL)";
348 proc_name(kauth_resolver_identity
, pname
, sizeof(pname
));
349 // <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);
353 /* if the request was processed, copy the result */
355 *lkp
= workp
->kr_work
;
358 * If the request timed out and was never collected, the resolver
359 * is dead and probably not coming back anytime soon. In this
360 * case we revert to no-resolver behaviour, and punt all the other
361 * sleeping requests to clear the backlog.
363 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
364 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
367 * Make the current resolver non-authoritative, and mark it as
368 * no longer registered to prevent kauth_cred_ismember_gid()
369 * enqueueing more work until a new one is registered. This
370 * mitigates the damage a crashing resolver may inflict.
372 kauth_resolver_identity
= 0;
373 kauth_resolver_registered
= 0;
375 /* kill all the other requestes that are waiting as well */
376 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
378 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
380 /* Cause all waiting-for-work threads to return EIO */
381 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
385 * drop our reference on the work item, and note whether we should
388 if (--workp
->kr_refs
<= 0) {
389 /* work out which list we have to remove it from */
390 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
391 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
392 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
393 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
394 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
395 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
397 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
401 /* someone else still has a reference on this request */
404 /* collect request result */
406 error
= workp
->kr_result
;
407 KAUTH_RESOLVER_UNLOCK();
409 * If we dropped the last reference, free the request.
412 FREE(workp
, M_KAUTH
);
414 KAUTH_DEBUG("RESOLVER - returning %d", error
);
422 * Description: System call interface for the external identity resolver.
424 * Parameters: uap->message Message from daemon to kernel
426 * Returns: 0 Successfully became resolver
427 * EPERM Not the resolver process
428 * kauth_authorize_generic:EPERM Not root user
429 * kauth_resolver_complete:EIO
430 * kauth_resolver_complete:EFAULT
431 * kauth_resolver_getwork:EINTR
432 * kauth_resolver_getwork:EFAULT
434 * Notes: This system call blocks until there is work enqueued, at
435 * which time the kernel wakes it up, and a message from the
436 * kernel is copied out to the identity resolution daemon, which
437 * proceed to attempt to resolve it. When the resolution has
438 * completed (successfully or not), the daemon called back into
439 * this system call to give the result to the kernel, and wait
440 * for the next request.
443 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
445 int opcode
= uap
->opcode
;
446 user_addr_t message
= uap
->message
;
447 struct kauth_resolver_work
*workp
;
452 * New server registering itself.
454 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
455 new_id
= current_proc()->p_pid
;
456 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
457 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
460 KAUTH_RESOLVER_LOCK();
461 if (kauth_resolver_identity
!= new_id
) {
462 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
464 * We have a new server, so assume that all the old requests have been lost.
466 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
467 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
468 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
469 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
470 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
473 * Allow user space resolver to override the
474 * external resolution timeout
476 if (message
>= 30 && message
<= 10000) {
477 kauth_resolver_timeout
= message
;
478 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
480 kauth_resolver_identity
= new_id
;
481 kauth_resolver_registered
= 1;
482 wakeup(&kauth_resolver_unsubmitted
);
484 KAUTH_RESOLVER_UNLOCK();
489 * Beyond this point, we must be the resolver process.
491 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
492 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
496 if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
498 * Terminate outstanding requests; without an authoritative
499 * resolver, we are now back on our own authority.
501 struct kauth_resolver_work
*killp
;
503 KAUTH_RESOLVER_LOCK();
506 * Clear the identity, but also mark it as unregistered so
507 * there is no explicit future expectation of us getting a
508 * new resolver any time soon.
510 kauth_resolver_identity
= 0;
511 kauth_resolver_registered
= 0;
513 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
515 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
517 /* Cause all waiting-for-work threads to return EIO */
518 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
519 KAUTH_RESOLVER_UNLOCK();
523 * Got a result returning?
525 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
526 if ((error
= kauth_resolver_complete(message
)) != 0)
531 * Caller wants to take more work?
533 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
534 if ((error
= kauth_resolver_getwork(message
)) != 0)
543 * kauth_resolver_getwork_continue
545 * Description: Continuation for kauth_resolver_getwork
547 * Parameters: result Error code or 0 for the sleep
548 * that got us to this function
551 * EINTR Interrupted (e.g. by signal)
552 * kauth_resolver_getwork2:EFAULT
554 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
558 kauth_resolver_getwork_continue(int result
)
565 KAUTH_RESOLVER_UNLOCK();
570 * If we lost a race with another thread/memberd restarting, then we
571 * need to go back to sleep to look for more work. If it was memberd
572 * restarting, then the msleep0() will error out here, as our thread
573 * will already be "dead".
575 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
578 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
580 * If this is a wakeup from another thread in the resolver
581 * deregistering it, error out the request-for-work thread
583 if (!kauth_resolver_identity
)
585 KAUTH_RESOLVER_UNLOCK();
589 thread
= current_thread();
590 ut
= get_bsdthread_info(thread
);
591 message
= ut
->uu_kauth
.message
;
592 return(kauth_resolver_getwork2(message
));
597 * kauth_resolver_getwork2
599 * Decription: Common utility function to copy out a identity resolver work
600 * item from the kernel to user space as part of the user space
601 * identity resolver requesting work.
603 * Parameters: message message to user space
606 * EFAULT Bad user space message address
608 * Notes: This common function exists to permit the use of continuations
609 * in the identity resoultion process. This frees up the stack
610 * while we are waiting for the user space resolver to complete
611 * a request. This is specifically used so that our per thread
612 * cost can be small, and we will therefore be willing to run a
613 * larger number of threads in the user space identity resolver.
616 kauth_resolver_getwork2(user_addr_t message
)
618 struct kauth_resolver_work
*workp
;
622 * Note: We depend on the caller protecting us from a NULL work item
623 * queue, since we must have the kauth resolver lock on entry to this
626 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
628 if ((error
= copyout(&workp
->kr_work
, message
, sizeof(workp
->kr_work
))) != 0) {
629 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
632 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
633 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
634 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
635 workp
->kr_subtime
= mach_absolute_time();
636 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
639 KAUTH_RESOLVER_UNLOCK();
645 * kauth_resolver_getwork
647 * Description: Get a work item from the enqueued requests from the kernel and
648 * give it to the user space daemon.
650 * Parameters: message message to user space
653 * EINTR Interrupted (e.g. by signal)
654 * kauth_resolver_getwork2:EFAULT
656 * Notes: This function blocks in a continuation if there are no work
657 * items available for processing at the time the user space
658 * identity resolution daemon makes a request for work. This
659 * permits a large number of threads to be used by the daemon,
660 * without using a lot of wired kernel memory when there are no
661 * acctual request outstanding.
664 kauth_resolver_getwork(user_addr_t message
)
666 struct kauth_resolver_work
*workp
;
669 KAUTH_RESOLVER_LOCK();
671 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
672 thread_t thread
= current_thread();
673 struct uthread
*ut
= get_bsdthread_info(thread
);
675 ut
->uu_kauth
.message
= message
;
676 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
677 KAUTH_RESOLVER_UNLOCK();
679 * If this is a wakeup from another thread in the resolver
680 * deregistering it, error out the request-for-work thread
682 if (!kauth_resolver_identity
)
686 return kauth_resolver_getwork2(message
);
691 * kauth_resolver_complete
693 * Description: Return a result from userspace.
695 * Parameters: message message from user space
698 * EIO The resolver is dead
699 * copyin:EFAULT Bad message from user space
702 kauth_resolver_complete(user_addr_t message
)
704 struct kauth_identity_extlookup extl
;
705 struct kauth_resolver_work
*workp
;
706 struct kauth_resolver_work
*killp
;
709 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
710 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
714 KAUTH_RESOLVER_LOCK();
718 switch (extl
.el_result
) {
719 case KAUTH_EXTLOOKUP_INPROG
:
723 /* XXX this should go away once memberd is updated */
725 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
731 case KAUTH_EXTLOOKUP_SUCCESS
:
734 case KAUTH_EXTLOOKUP_FATAL
:
735 /* fatal error means the resolver is dead */
736 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
738 * Terminate outstanding requests; without an authoritative
739 * resolver, we are now back on our own authority. Tag the
740 * resolver unregistered to prevent kauth_cred_ismember_gid()
741 * enqueueing more work until a new one is registered. This
742 * mitigates the damage a crashing resolver may inflict.
744 kauth_resolver_identity
= 0;
745 kauth_resolver_registered
= 0;
747 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
749 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
751 /* Cause all waiting-for-work threads to return EIO */
752 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
753 /* and return EIO to the caller */
757 case KAUTH_EXTLOOKUP_BADRQ
:
758 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
762 case KAUTH_EXTLOOKUP_FAILURE
:
763 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
768 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
774 * In the case of a fatal error, we assume that the resolver will restart
775 * quickly and re-collect all of the outstanding requests. Thus, we don't
776 * complete the request which returned the fatal error status.
778 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
779 /* scan our list for this request */
780 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
782 if (workp
->kr_seqno
== extl
.el_seqno
) {
784 workp
->kr_work
= extl
;
785 /* move onto completed list and wake up requester(s) */
786 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
787 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
788 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
789 workp
->kr_result
= result
;
790 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
797 * Note that it's OK for us not to find anything; if the request has
798 * timed out the work record will be gone.
800 KAUTH_RESOLVER_UNLOCK();
810 struct kauth_identity
{
811 TAILQ_ENTRY(kauth_identity
) ki_link
;
813 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
814 #define KI_VALID_GID (1<<1)
815 #define KI_VALID_GUID (1<<2)
816 #define KI_VALID_NTSID (1<<3)
822 * Expiry times are the earliest time at which we will disregard the cached state and go to
823 * userland. Before then if the valid bit is set, we will return the cached value. If it's
824 * not set, we will not go to userland to resolve, just assume that there is no answer
827 time_t ki_guid_expiry
;
828 time_t ki_ntsid_expiry
;
831 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
832 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
833 static int kauth_identity_count
;
835 static lck_mtx_t
*kauth_identity_mtx
;
836 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
837 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
840 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
841 ntsid_t
*ntsidp
, time_t ntsid_expiry
);
842 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
843 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
);
844 static void kauth_identity_lru(struct kauth_identity
*kip
);
845 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
846 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
847 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
);
848 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
);
849 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
);
850 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
);
854 * kauth_identity_init
856 * Description: Initialize the kernel side of the credential identity resolver
862 * Notes: Intialize the credential identity resolver for use; the
863 * credential identity resolver is the KPI used to communicate
864 * with a user space credential identity resolver daemon.
866 * This function is called from kauth_init() in the file
867 * kern_authorization.c.
870 kauth_identity_init(void)
872 TAILQ_INIT(&kauth_identities
);
873 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
878 * kauth_identity_alloc
880 * Description: Allocate and fill out a kauth_identity structure for
881 * translation between {UID|GID}/GUID/NTSID
885 * Returns: NULL Insufficient memory to satisfy
887 * !NULL A pointer to the applocated
888 * structure, filled in
890 * Notes: It is illegal to translate between UID and GID; any given UUID
891 * or NTSID can oly refer to an NTSIDE or UUID (respectively),
892 * and *either* a UID *or* a GID, but not both.
894 static struct kauth_identity
*
895 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
)
897 struct kauth_identity
*kip
;
899 /* get and fill in a new identity */
900 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
902 if (gid
!= KAUTH_GID_NONE
) {
904 kip
->ki_valid
= KI_VALID_GID
;
906 if (uid
!= KAUTH_UID_NONE
) {
907 if (kip
->ki_valid
& KI_VALID_GID
)
908 panic("can't allocate kauth identity with both uid and gid");
910 kip
->ki_valid
= KI_VALID_UID
;
913 kip
->ki_guid
= *guidp
;
914 kip
->ki_valid
|= KI_VALID_GUID
;
916 kip
->ki_guid_expiry
= guid_expiry
;
917 if (ntsidp
!= NULL
) {
918 kip
->ki_ntsid
= *ntsidp
;
919 kip
->ki_valid
|= KI_VALID_NTSID
;
921 kip
->ki_ntsid_expiry
= ntsid_expiry
;
928 * kauth_identity_register_and_free
930 * Description: Register an association between identity tokens. The passed
931 * 'kip' is freed by this function.
933 * Parameters: kip Pointer to kauth_identity
934 * structure to register
938 * Notes: The memory pointer to by 'kip' is assumed to have been
939 * previously allocated via kauth_identity_alloc().
942 kauth_identity_register_and_free(struct kauth_identity
*kip
)
944 struct kauth_identity
*ip
;
947 * We search the cache for the UID listed in the incoming association.
948 * If we already have an entry, the new information is merged.
951 KAUTH_IDENTITY_LOCK();
952 if (kip
->ki_valid
& KI_VALID_UID
) {
953 if (kip
->ki_valid
& KI_VALID_GID
)
954 panic("kauth_identity: can't insert record with both UID and GID as key");
955 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
956 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
958 } else if (kip
->ki_valid
& KI_VALID_GID
) {
959 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
960 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
963 panic("kauth_identity: can't insert record without UID or GID as key");
967 /* we already have an entry, merge/overwrite */
968 if (kip
->ki_valid
& KI_VALID_GUID
) {
969 ip
->ki_guid
= kip
->ki_guid
;
970 ip
->ki_valid
|= KI_VALID_GUID
;
972 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
973 if (kip
->ki_valid
& KI_VALID_NTSID
) {
974 ip
->ki_ntsid
= kip
->ki_ntsid
;
975 ip
->ki_valid
|= KI_VALID_NTSID
;
977 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
978 /* and discard the incoming identity */
982 /* don't have any information on this identity, so just add it */
983 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
984 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
985 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
986 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
987 kauth_identity_count
--;
990 KAUTH_IDENTITY_UNLOCK();
991 /* have to drop lock before freeing expired entry */
998 * kauth_identity_updatecache
1000 * Description: Given a lookup result, add any associations that we don't
1003 * Parameters: elp External lookup result from
1004 * user space daemon to kernel
1005 * rkip pointer to returned kauth
1011 * *rkip Modified (if non-NULL)
1014 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
)
1017 struct kauth_identity
*kip
;
1021 /* user identity? */
1022 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1023 KAUTH_IDENTITY_LOCK();
1024 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1025 /* matching record */
1026 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1027 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1028 kip
->ki_guid
= elp
->el_uguid
;
1029 kip
->ki_valid
|= KI_VALID_GUID
;
1031 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
1032 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1033 kip
->ki_ntsid
= elp
->el_usid
;
1034 kip
->ki_valid
|= KI_VALID_NTSID
;
1036 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
1037 kauth_identity_lru(kip
);
1040 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1044 KAUTH_IDENTITY_UNLOCK();
1045 /* not found in cache, add new record */
1047 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1048 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1049 tv
.tv_sec
+ elp
->el_uguid_valid
,
1050 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1051 tv
.tv_sec
+ elp
->el_usid_valid
);
1055 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1056 kauth_identity_register_and_free(kip
);
1061 /* group identity? */
1062 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
) {
1063 KAUTH_IDENTITY_LOCK();
1064 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1065 /* matching record */
1066 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1067 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1068 kip
->ki_guid
= elp
->el_gguid
;
1069 kip
->ki_valid
|= KI_VALID_GUID
;
1071 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
1072 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1073 kip
->ki_ntsid
= elp
->el_gsid
;
1074 kip
->ki_valid
|= KI_VALID_NTSID
;
1076 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
1077 kauth_identity_lru(kip
);
1080 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1084 KAUTH_IDENTITY_UNLOCK();
1085 /* not found in cache, add new record */
1087 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1088 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1089 tv
.tv_sec
+ elp
->el_gguid_valid
,
1090 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1091 tv
.tv_sec
+ elp
->el_gsid_valid
);
1095 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1096 kauth_identity_register_and_free(kip
);
1105 * kauth_identity_lru
1107 * Description: Promote the entry to the head of the LRU, assumes the cache
1110 * Parameters: kip kauth identity to move to the
1111 * head of the LRU list, if it's
1116 * Notes: This is called even if the entry has expired; typically an
1117 * expired entry that's been looked up is about to be revalidated,
1118 * and having it closer to the head of the LRU means finding it
1119 * quickly again when the revalidation comes through.
1122 kauth_identity_lru(struct kauth_identity
*kip
)
1124 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1125 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1126 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1132 * kauth_identity_guid_expired
1134 * Description: Handle lazy expiration of GUID translations.
1136 * Parameters: kip kauth identity to check for
1139 * Returns: 1 Expired
1143 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1148 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
1149 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1154 * kauth_identity_ntsid_expired
1156 * Description: Handle lazy expiration of NTSID translations.
1158 * Parameters: kip kauth identity to check for
1161 * Returns: 1 Expired
1165 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1170 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1171 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1176 * kauth_identity_find_uid
1178 * Description: Search for an entry by UID
1180 * Parameters: uid UID to find
1181 * kir Pointer to return area
1187 * *klr Modified, if found
1190 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
)
1192 struct kauth_identity
*kip
;
1194 KAUTH_IDENTITY_LOCK();
1195 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1196 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1197 kauth_identity_lru(kip
);
1198 /* Copy via structure assignment */
1203 KAUTH_IDENTITY_UNLOCK();
1204 return((kip
== NULL
) ? ENOENT
: 0);
1209 * kauth_identity_find_uid
1211 * Description: Search for an entry by GID
1213 * Parameters: gid GID to find
1214 * kir Pointer to return area
1220 * *klr Modified, if found
1223 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
)
1225 struct kauth_identity
*kip
;
1227 KAUTH_IDENTITY_LOCK();
1228 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1229 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1230 kauth_identity_lru(kip
);
1231 /* Copy via structure assignment */
1236 KAUTH_IDENTITY_UNLOCK();
1237 return((kip
== NULL
) ? ENOENT
: 0);
1242 * kauth_identity_find_guid
1244 * Description: Search for an entry by GUID
1246 * Parameters: guidp Pointer to GUID to find
1247 * kir Pointer to return area
1253 * *klr Modified, if found
1255 * Note: The association may be expired, in which case the caller
1256 * may elect to call out to userland to revalidate.
1259 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
)
1261 struct kauth_identity
*kip
;
1263 KAUTH_IDENTITY_LOCK();
1264 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1265 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1266 kauth_identity_lru(kip
);
1267 /* Copy via structure assignment */
1272 KAUTH_IDENTITY_UNLOCK();
1273 return((kip
== NULL
) ? ENOENT
: 0);
1278 * kauth_identity_find_ntsid
1280 * Description: Search for an entry by NTSID
1282 * Parameters: ntsid Pointer to NTSID to find
1283 * kir Pointer to return area
1289 * *klr Modified, if found
1291 * Note: The association may be expired, in which case the caller
1292 * may elect to call out to userland to revalidate.
1295 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
)
1297 struct kauth_identity
*kip
;
1299 KAUTH_IDENTITY_LOCK();
1300 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1301 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1302 kauth_identity_lru(kip
);
1303 /* Copy via structure assignment */
1308 KAUTH_IDENTITY_UNLOCK();
1309 return((kip
== NULL
) ? ENOENT
: 0);
1316 guid_t kauth_null_guid
;
1322 * Description: Determine the equality of two GUIDs
1324 * Parameters: guid1 Pointer to first GUID
1325 * guid2 Pointer to second GUID
1327 * Returns: 0 If GUIDs are inequal
1328 * !0 If GUIDs are equal
1331 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1333 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1338 * kauth_wellknown_guid
1340 * Description: Determine if a GUID is a well-known GUID
1342 * Parameters: guid Pointer to GUID to check
1344 * Returns: KAUTH_WKG_NOT Not a wel known GUID
1345 * KAUTH_WKG_EVERYBODY "Everybody"
1346 * KAUTH_WKG_NOBODY "Nobody"
1347 * KAUTH_WKG_OWNER "Other"
1348 * KAUTH_WKG_GROUP "Group"
1351 kauth_wellknown_guid(guid_t
*guid
)
1353 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1356 * All WKGs begin with the same 12 bytes.
1358 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1360 * The final 4 bytes are our code (in network byte order).
1362 code
= OSSwapHostToBigInt32(*(u_int32_t
*)&guid
->g_guid
[12]);
1365 return(KAUTH_WKG_EVERYBODY
);
1367 return(KAUTH_WKG_NOBODY
);
1369 return(KAUTH_WKG_OWNER
);
1371 return(KAUTH_WKG_GROUP
);
1374 return(KAUTH_WKG_NOT
);
1381 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1383 * Paramters: sid1 Pointer to first NTSID
1384 * sid2 Pointer to second NTSID
1386 * Returns: 0 If GUIDs are inequal
1387 * !0 If GUIDs are equal
1390 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1392 /* check sizes for equality, also sanity-check size while we're at it */
1393 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1394 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1395 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1404 * We support four tokens representing identity:
1405 * - Credential reference
1408 * - NT security identifier
1410 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1414 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
1418 * kauth_cred_change_egid
1420 * Description: Set EGID by changing the first element of cr_groups for the
1421 * passed credential; if the new EGID exists in the list of
1422 * groups already, then rotate the old EGID into its position,
1423 * otherwise replace it
1425 * Parameters: cred Pointer to the credential to modify
1426 * new_egid The new EGID to set
1428 * Returns: 0 The egid did not displace a member of
1429 * the supplementary group list
1430 * 1 The egid being set displaced a member
1431 * of the supplementary groups list
1433 * Note: Utility function; internal use only because of locking.
1435 * This function operates on the credential passed; the caller
1436 * must operate either on a newly allocated credential (one for
1437 * which there is no hash cache reference and no externally
1438 * visible pointer reference), or a template credential.
1441 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1447 #endif /* radar_4600026 */
1448 gid_t old_egid
= cred
->cr_groups
[0];
1450 /* Ignoring the first entry, scan for a match for the new egid */
1451 for (i
= 1; i
< cred
->cr_ngroups
; i
++) {
1453 * If we find a match, swap them so we don't lose overall
1456 if (cred
->cr_groups
[i
] == new_egid
) {
1457 cred
->cr_groups
[i
] = old_egid
;
1458 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1465 #error Fix radar 4600026 first!!!
1468 This is correct for memberd behaviour, but incorrect for POSIX; to address
1469 this, we would need to automatically opt-out any SUID/SGID binary, and force
1470 it to use initgroups to opt back in. We take the approach of considering it
1471 opt'ed out in any group of 16 displacement instead, since it's a much more
1472 conservative approach (i.e. less likely to cause things to break).
1476 * If we displaced a member of the supplementary groups list of the
1477 * credential, and we have not opted out of memberd, then if memberd
1478 * says that the credential is a member of the group, then it has not
1479 * actually been displaced.
1481 * NB: This is typically a cold code path.
1483 if (displaced
&& !(cred
->cr_flags
& CRF_NOMEMBERD
) &&
1484 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1487 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1489 #endif /* radar_4600026 */
1491 /* set the new EGID into the old spot */
1492 cred
->cr_groups
[0] = new_egid
;
1501 * Description: Fetch UID from credential
1503 * Parameters: cred Credential to examine
1505 * Returns: (uid_t) UID associated with credential
1508 kauth_cred_getuid(kauth_cred_t cred
)
1510 NULLCRED_CHECK(cred
);
1511 return(cred
->cr_uid
);
1518 * Description: Fetch GID from credential
1520 * Parameters: cred Credential to examine
1522 * Returns: (gid_t) GID associated with credential
1525 kauth_cred_getgid(kauth_cred_t cred
)
1527 NULLCRED_CHECK(cred
);
1528 return(cred
->cr_gid
);
1533 * kauth_cred_guid2uid
1535 * Description: Fetch UID from GUID
1537 * Parameters: guidp Pointer to GUID to examine
1538 * uidp Pointer to buffer for UID
1540 * Returns: 0 Success
1541 * kauth_cred_cache_lookup:EINVAL
1544 * *uidp Modified, if successful
1547 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
1549 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
1554 * kauth_cred_guid2gid
1556 * Description: Fetch GID from GUID
1558 * Parameters: guidp Pointer to GUID to examine
1559 * gidp Pointer to buffer for GID
1561 * Returns: 0 Success
1562 * kauth_cred_cache_lookup:EINVAL
1565 * *gidp Modified, if successful
1568 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
1570 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
1575 * kauth_cred_ntsid2uid
1577 * Description: Fetch UID from NTSID
1579 * Parameters: sidp Pointer to NTSID to examine
1580 * uidp Pointer to buffer for UID
1582 * Returns: 0 Success
1583 * kauth_cred_cache_lookup:EINVAL
1586 * *uidp Modified, if successful
1589 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
1591 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
1596 * kauth_cred_ntsid2gid
1598 * Description: Fetch GID from NTSID
1600 * Parameters: sidp Pointer to NTSID to examine
1601 * gidp Pointer to buffer for GID
1603 * Returns: 0 Success
1604 * kauth_cred_cache_lookup:EINVAL
1607 * *gidp Modified, if successful
1610 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
1612 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
1617 * kauth_cred_ntsid2guid
1619 * Description: Fetch GUID from NTSID
1621 * Parameters: sidp Pointer to NTSID to examine
1622 * guidp Pointer to buffer for GUID
1624 * Returns: 0 Success
1625 * kauth_cred_cache_lookup:EINVAL
1628 * *guidp Modified, if successful
1631 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
1633 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
1638 * kauth_cred_uid2guid
1640 * Description: Fetch GUID from UID
1642 * Parameters: uid UID to examine
1643 * guidp Pointer to buffer for GUID
1645 * Returns: 0 Success
1646 * kauth_cred_cache_lookup:EINVAL
1649 * *guidp Modified, if successful
1652 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
1654 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
1659 * kauth_cred_getguid
1661 * Description: Fetch GUID from credential
1663 * Parameters: cred Credential to examine
1664 * guidp Pointer to buffer for GUID
1666 * Returns: 0 Success
1667 * kauth_cred_cache_lookup:EINVAL
1670 * *guidp Modified, if successful
1673 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
1675 NULLCRED_CHECK(cred
);
1676 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
1681 * kauth_cred_getguid
1683 * Description: Fetch GUID from GID
1685 * Parameters: gid GID to examine
1686 * guidp Pointer to buffer for GUID
1688 * Returns: 0 Success
1689 * kauth_cred_cache_lookup:EINVAL
1692 * *guidp Modified, if successful
1695 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
1697 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
1702 * kauth_cred_uid2ntsid
1704 * Description: Fetch NTSID from UID
1706 * Parameters: uid UID to examine
1707 * sidp Pointer to buffer for NTSID
1709 * Returns: 0 Success
1710 * kauth_cred_cache_lookup:EINVAL
1713 * *sidp Modified, if successful
1716 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
1718 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
1723 * kauth_cred_getntsid
1725 * Description: Fetch NTSID from credential
1727 * Parameters: cred Credential to examine
1728 * sidp Pointer to buffer for NTSID
1730 * Returns: 0 Success
1731 * kauth_cred_cache_lookup:EINVAL
1734 * *sidp Modified, if successful
1737 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
1739 NULLCRED_CHECK(cred
);
1740 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
1745 * kauth_cred_gid2ntsid
1747 * Description: Fetch NTSID from GID
1749 * Parameters: gid GID to examine
1750 * sidp Pointer to buffer for NTSID
1752 * Returns: 0 Success
1753 * kauth_cred_cache_lookup:EINVAL
1756 * *sidp Modified, if successful
1759 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
1761 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
1766 * kauth_cred_guid2ntsid
1768 * Description: Fetch NTSID from GUID
1770 * Parameters: guidp Pointer to GUID to examine
1771 * sidp Pointer to buffer for NTSID
1773 * Returns: 0 Success
1774 * kauth_cred_cache_lookup:EINVAL
1777 * *sidp Modified, if successful
1780 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
1782 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
1787 * kauth_cred_cache_lookup
1789 * Description: Lookup a translation in the cache; if one is not found, and
1790 * the attempt was not fatal, submit the request to the resolver
1791 * instead, and wait for it to complete or be aborted.
1793 * Parameters: from Identity information we have
1794 * to Identity information we want
1795 * src Pointer to buffer containing
1796 * the source identity
1797 * dst Pointer to buffer to receive
1798 * the target identity
1800 * Returns: 0 Success
1801 * EINVAL Unknown source identity type
1804 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
1806 struct kauth_identity ki
;
1807 struct kauth_identity_extlookup el
;
1809 int (* expired
)(struct kauth_identity
*kip
);
1811 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
1814 * Look for an existing cache entry for this association.
1815 * If the entry has not expired, return the cached information.
1820 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
);
1823 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
);
1826 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
);
1828 case KI_VALID_NTSID
:
1829 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
);
1834 /* lookup failure or error */
1836 /* any other error is fatal */
1837 if (error
!= ENOENT
) {
1838 /* XXX bogus check - this is not possible */
1839 KAUTH_DEBUG("CACHE - cache search error %d", error
);
1843 /* do we have a translation? */
1844 if (ki
.ki_valid
& to
) {
1845 /* found a valid cached entry, check expiry */
1848 expired
= kauth_identity_guid_expired
;
1850 case KI_VALID_NTSID
:
1851 expired
= kauth_identity_ntsid_expired
;
1856 expired
= kauth_identity_guid_expired
;
1858 case KI_VALID_NTSID
:
1859 expired
= kauth_identity_ntsid_expired
;
1865 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki
.ki_valid
);
1867 * If no expiry function, or not expired, we have found
1871 KAUTH_DEBUG("CACHE - no expiry function");
1874 if (!expired(&ki
)) {
1875 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1879 * We leave ki_valid set here; it contains a
1880 * translation but the TTL has expired. If we can't
1881 * get a result from the resolver, we will use it as
1882 * a better-than nothing alternative.
1884 KAUTH_DEBUG("CACHE - expired entry found");
1889 * We failed to find a cache entry; call the resolver.
1891 * Note: We ask for as much data as we can get.
1893 bzero(&el
, sizeof(el
));
1894 el
.el_info_pid
= current_proc()->p_pid
;
1897 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
1898 el
.el_uid
= *(uid_t
*)src
;
1901 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
1902 el
.el_gid
= *(gid_t
*)src
;
1905 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
1906 el
.el_uguid
= *(guid_t
*)src
;
1907 el
.el_gguid
= *(guid_t
*)src
;
1909 case KI_VALID_NTSID
:
1910 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
1911 el
.el_usid
= *(ntsid_t
*)src
;
1912 el
.el_gsid
= *(ntsid_t
*)src
;
1918 * Here we ask for everything all at once, to avoid having to work
1919 * out what we really want now, or might want soon.
1921 * Asking for SID translations when we don't know we need them right
1922 * now is going to cause excess work to be done if we're connected
1923 * to a network that thinks it can translate them. This list needs
1924 * to get smaller/smarter.
1926 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
1927 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
1928 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
1929 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
1930 error
= kauth_resolver_submit(&el
);
1931 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
1932 /* was the lookup successful? */
1935 * Save the results from the lookup - may have other
1936 * information even if we didn't get a guid.
1938 kauth_identity_updatecache(&el
, &ki
);
1941 * Check to see if we have a valid result.
1943 if (!error
&& !(ki
.ki_valid
& to
))
1950 *(uid_t
*)dst
= ki
.ki_uid
;
1953 *(gid_t
*)dst
= ki
.ki_gid
;
1956 *(guid_t
*)dst
= ki
.ki_guid
;
1958 case KI_VALID_NTSID
:
1959 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
1964 KAUTH_DEBUG("CACHE - returned successfully");
1970 * Group membership cache.
1972 * XXX the linked-list implementation here needs to be optimized.
1975 struct kauth_group_membership
{
1976 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
1977 uid_t gm_uid
; /* the identity whose membership we're recording */
1978 gid_t gm_gid
; /* group of which they are a member */
1979 time_t gm_expiry
; /* TTL for the membership */
1981 #define KAUTH_GROUP_ISMEMBER (1<<0)
1984 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
1985 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1986 static int kauth_groups_count
;
1988 static lck_mtx_t
*kauth_groups_mtx
;
1989 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1990 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1992 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
1993 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
1994 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
2000 * Description: Initialize the groups cache
2002 * Parameters: (void)
2006 * Notes: Intialize the groups cache for use; the group cache is used
2007 * to avoid unnecessary calls out to user space.
2009 * This function is called from kauth_init() in the file
2010 * kern_authorization.c.
2013 kauth_groups_init(void)
2015 TAILQ_INIT(&kauth_groups
);
2016 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2021 * kauth_groups_expired
2023 * Description: Handle lazy expiration of group membership cache entries
2025 * Parameters: gm group membership entry to
2026 * check for expiration
2028 * Returns: 1 Expired
2032 kauth_groups_expired(struct kauth_group_membership
*gm
)
2037 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2044 * Description: Promote the entry to the head of the LRU, assumes the cache
2047 * Parameters: kip group membership entry to move
2048 * to the head of the LRU list,
2049 * if it's not already there
2053 * Notes: This is called even if the entry has expired; typically an
2054 * expired entry that's been looked up is about to be revalidated,
2055 * and having it closer to the head of the LRU means finding it
2056 * quickly again when the revalidation comes through.
2059 kauth_groups_lru(struct kauth_group_membership
*gm
)
2061 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2062 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2063 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2069 * kauth_groups_updatecache
2071 * Description: Given a lookup result, add any group cache associations that
2072 * we don't currently have.
2074 * Parameters: elp External lookup result from
2075 * user space daemon to kernel
2076 * rkip pointer to returned kauth
2082 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2084 struct kauth_group_membership
*gm
;
2087 /* need a valid response if we are to cache anything */
2089 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2090 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2096 * Search for an existing record for this association before inserting
2097 * a new one; if we find one, update it instead of creating a new one
2099 KAUTH_GROUPS_LOCK();
2100 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2101 if ((el
->el_uid
== gm
->gm_uid
) &&
2102 (el
->el_gid
== gm
->gm_gid
)) {
2103 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2104 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2106 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2108 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2109 kauth_groups_lru(gm
);
2113 KAUTH_GROUPS_UNLOCK();
2115 /* if we found an entry to update, stop here */
2119 /* allocate a new record */
2120 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2122 gm
->gm_uid
= el
->el_uid
;
2123 gm
->gm_gid
= el
->el_gid
;
2124 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2125 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2127 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2129 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2133 * Insert the new entry. Note that it's possible to race ourselves
2134 * here and end up with duplicate entries in the list. Wasteful, but
2135 * harmless since the first into the list will never be looked up,
2136 * and thus will eventually just fall off the end.
2138 KAUTH_GROUPS_LOCK();
2139 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2140 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
2141 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2142 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2143 kauth_groups_count
--;
2147 KAUTH_GROUPS_UNLOCK();
2149 /* free expired cache entry */
2156 * Group membership KPI
2160 * kauth_cred_ismember_gid
2162 * Description: Given a credential and a GID, determine if the GID is a member
2163 * of one of the supplementary groups associated with the given
2166 * Parameters: cred Credential to check in
2167 * gid GID to check for membership
2168 * resultp Pointer to int to contain the
2169 * result of the call
2171 * Returns: 0 Success
2172 * ENOENT Could not proform lookup
2173 * kauth_resolver_submit:EWOULDBLOCK
2174 * kauth_resolver_submit:EINTR
2175 * kauth_resolver_submit:ENOMEM
2176 * kauth_resolver_submit:ENOENT User space daemon did not vend
2178 * kauth_resolver_submit:??? Unlikely error from user space
2181 * *resultp (modified) 1 Is member
2184 * Notes: This function guarantees not to modify resultp when returning
2187 * This function effectively checkes the EGID as well, since the
2188 * EGID is cr_groups[0] as an implementation detail.
2191 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2193 struct kauth_group_membership
*gm
;
2194 struct kauth_identity_extlookup el
;
2198 * Check the per-credential list of override groups.
2200 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2201 * the cache should be used for that case.
2203 for (i
= 0; i
< cred
->cr_ngroups
; i
++) {
2204 if (gid
== cred
->cr_groups
[i
]) {
2211 * If we don't have a UID for group membership checks, the in-cred list
2212 * was authoritative and we can stop here.
2214 if (cred
->cr_gmuid
== KAUTH_UID_NONE
) {
2221 * If the resolver hasn't checked in yet, we are early in the boot
2222 * phase and the local group list is complete and authoritative.
2224 if (!kauth_resolver_registered
) {
2230 /* XXX check supplementary groups */
2231 /* XXX check whiteout groups */
2232 /* XXX nesting of supplementary/whiteout groups? */
2235 * Check the group cache.
2237 KAUTH_GROUPS_LOCK();
2238 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2239 if ((gm
->gm_uid
== cred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2240 kauth_groups_lru(gm
);
2245 /* did we find a membership entry? */
2247 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2248 KAUTH_GROUPS_UNLOCK();
2250 /* if we did, we can return now */
2254 /* nothing in the cache, need to go to userland */
2255 bzero(&el
, sizeof(el
));
2256 el
.el_info_pid
= current_proc()->p_pid
;
2257 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2258 el
.el_uid
= cred
->cr_gmuid
;
2260 el
.el_member_valid
= 0; /* XXX set by resolver? */
2261 error
= kauth_resolver_submit(&el
);
2264 /* save the results from the lookup */
2265 kauth_groups_updatecache(&el
);
2267 /* if we successfully ascertained membership, report */
2268 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2269 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2278 * kauth_cred_ismember_guid
2280 * Description: Determine whether the supplied credential is a member of the
2281 * group nominated by GUID.
2283 * Parameters: cred Credential to check in
2284 * guidp Pointer to GUID whose group
2285 * we are testing for membership
2286 * resultp Pointer to int to contain the
2287 * result of the call
2289 * Returns: 0 Success
2290 * kauth_cred_guid2gid:EINVAL
2291 * kauth_cred_ismember_gid:ENOENT
2292 * kauth_resolver_submit:ENOENT User space daemon did not vend
2294 * kauth_cred_ismember_gid:EWOULDBLOCK
2295 * kauth_cred_ismember_gid:EINTR
2296 * kauth_cred_ismember_gid:ENOMEM
2297 * kauth_cred_ismember_gid:??? Unlikely error from user space
2300 * *resultp (modified) 1 Is member
2304 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2306 struct kauth_identity ki
;
2311 wkg
= kauth_wellknown_guid(guidp
);
2313 case KAUTH_WKG_NOBODY
:
2316 case KAUTH_WKG_EVERYBODY
:
2322 * Grovel the identity cache looking for this GUID.
2323 * If we find it, and it is for a user record, return
2324 * false because it's not a group.
2326 * This is necessary because we don't have -ve caching
2327 * of group memberships, and we really want to avoid
2328 * calling out to the resolver if at all possible.
2330 * Because we're called by the ACL evaluator, and the
2331 * ACL evaluator is likely to encounter ACEs for users,
2332 * this is expected to be a common case.
2335 if ((error
= kauth_identity_find_guid(guidp
, &ki
)) == 0 &&
2336 !kauth_identity_guid_expired(&ki
)) {
2337 if (ki
.ki_valid
& KI_VALID_GID
) {
2338 /* It's a group after all... */
2342 if (ki
.ki_valid
& KI_VALID_UID
) {
2347 #endif /* 6603280 */
2349 * Attempt to translate the GUID to a GID. Even if
2350 * this fails, we will have primed the cache if it is
2351 * a user record and we'll see it above the next time
2354 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2356 * If we have no guid -> gid translation, it's not a group and
2357 * thus the cred can't be a member.
2359 if (error
== ENOENT
) {
2365 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
2372 * kauth_cred_gid_subset
2374 * Description: Given two credentials, determine if all GIDs associated with
2375 * the first are also associated with the second
2377 * Parameters: cred1 Credential to check for
2378 * cred2 Credential to check in
2379 * resultp Pointer to int to contain the
2380 * result of the call
2382 * Returns: 0 Success
2383 * non-zero See kauth_cred_ismember_gid for
2387 * *resultp (modified) 1 Is subset
2390 * Notes: This function guarantees not to modify resultp when returning
2394 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
2396 int i
, err
, res
= 1;
2399 /* First, check the local list of groups */
2400 for (i
= 0; i
< cred1
->cr_ngroups
; i
++) {
2401 gid
= cred1
->cr_groups
[i
];
2402 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
2406 if (!res
&& gid
!= cred2
->cr_rgid
&& gid
!= cred2
->cr_svgid
) {
2412 /* Check real gid */
2413 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_rgid
, &res
)) != 0) {
2417 if (!res
&& cred1
->cr_rgid
!= cred2
->cr_rgid
&&
2418 cred1
->cr_rgid
!= cred2
->cr_svgid
) {
2423 /* Finally, check saved gid */
2424 if ((err
= kauth_cred_ismember_gid(cred2
, cred1
->cr_svgid
, &res
)) != 0){
2428 if (!res
&& cred1
->cr_svgid
!= cred2
->cr_rgid
&&
2429 cred1
->cr_svgid
!= cred2
->cr_svgid
) {
2440 * kauth_cred_issuser
2442 * Description: Fast replacement for issuser()
2444 * Parameters: cred Credential to check for super
2447 * Returns: 0 Not super user
2450 * Notes: This function uses a magic number which is not a manifest
2451 * constant; this is bad practice.
2454 kauth_cred_issuser(kauth_cred_t cred
)
2456 return(cred
->cr_uid
== 0);
2464 /* lock protecting credential hash table */
2465 static lck_mtx_t
*kauth_cred_hash_mtx
;
2466 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2467 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2468 #if KAUTH_CRED_HASH_DEBUG
2469 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2470 #else /* !KAUTH_CRED_HASH_DEBUG */
2471 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2472 #endif /* !KAUTH_CRED_HASH_DEBUG */
2478 * Description: Initialize the credential hash cache
2480 * Parameters: (void)
2484 * Notes: Intialize the credential hash cache for use; the credential
2485 * hash cache is used convert duplicate credentials into a
2486 * single reference counted credential in order to save wired
2487 * kernel memory. In practice, this generally means a desktop
2488 * system runs with a few tens of credentials, instead of one
2489 * per process, one per thread, one per vnode cache entry, and
2490 * so on. This generally results in savings of 200K or more
2491 * (potentially much more on server systems).
2493 * The hash cache internally has a reference on the credential
2494 * for itself as a means of avoiding a reclaim race for a
2495 * credential in the process of having it's last non-hash
2496 * reference released. This would otherwise result in the
2497 * possibility of a freed credential that was still in uses due
2498 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
2500 * On final release, the hash reference is droped, and the
2501 * credential is freed back to the system.
2503 * This function is called from kauth_init() in the file
2504 * kern_authorization.c.
2507 kauth_cred_init(void)
2511 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2512 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
2514 /*allocate credential hash table */
2515 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
2516 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
2517 M_KAUTH
, M_WAITOK
| M_ZERO
);
2518 if (kauth_cred_table_anchor
== NULL
)
2519 panic("startup: kauth_cred_init");
2520 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
2521 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
2529 * Description: Get the current thread's effective UID.
2531 * Parameters: (void)
2533 * Returns: (uid_t) The effective UID of the
2539 return(kauth_cred_get()->cr_uid
);
2546 * Description: Get the current thread's real UID.
2548 * Parameters: (void)
2550 * Returns: (uid_t) The real UID of the current
2556 return(kauth_cred_get()->cr_ruid
);
2563 * Description: Get the current thread's effective GID.
2565 * Parameters: (void)
2567 * Returns: (gid_t) The effective GID of the
2573 return(kauth_cred_get()->cr_groups
[0]);
2580 * Description: Get the current thread's real GID.
2582 * Parameters: (void)
2584 * Returns: (gid_t) The real GID of the current
2590 return(kauth_cred_get()->cr_rgid
);
2597 * Description: Returns a pointer to the current thread's credential
2599 * Parameters: (void)
2601 * Returns: (kauth_cred_t) Pointer to the current thread's
2604 * Notes: This function does not take a reference; because of this, the
2605 * caller MUST NOT do anything that would let the thread's
2606 * credential change while using the returned value, without
2607 * first explicitly taking their own reference.
2609 * If a caller intends to take a reference on the resulting
2610 * credential pointer from calling this function, it is strongly
2611 * recommended that the caller use kauth_cred_get_with_ref()
2612 * instead, to protect against any future changes to the cred
2613 * locking protocols; such changes could otherwise potentially
2614 * introduce race windows in the callers code.
2617 kauth_cred_get(void)
2620 struct uthread
*uthread
;
2622 uthread
= get_bsdthread_info(current_thread());
2624 if (uthread
== NULL
)
2625 panic("thread wants credential but has no BSD thread info");
2627 * We can lazy-bind credentials to threads, as long as their processes
2630 * XXX If we later inline this function, the code in this block
2631 * XXX should probably be called out in a function.
2633 if (uthread
->uu_ucred
== NOCRED
) {
2634 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2635 panic("thread wants credential but has no BSD process");
2636 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
2638 return(uthread
->uu_ucred
);
2642 mach_kauth_cred_uthread_update(void)
2647 uthread
= get_bsdthread_info(current_thread());
2648 proc
= current_proc();
2650 kauth_cred_uthread_update(uthread
, proc
);
2654 * kauth_cred_uthread_update
2656 * Description: Given a uthread, a proc, and whether or not the proc is locked,
2657 * late-bind the uthread cred to the proc cred.
2659 * Parameters: uthread_t The uthread to update
2660 * proc_t The process to update to
2664 * Notes: This code is common code called from system call or trap entry
2665 * in the case that the process thread may have been changed
2666 * since the last time the thread entered the kernel. It is
2667 * generally only called with the current uthread and process as
2671 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
2673 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
2674 (uthread
->uu_flag
& UT_SETUID
) == 0) {
2675 kauth_cred_t old
= uthread
->uu_ucred
;
2676 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
2677 if (IS_VALID_CRED(old
))
2678 kauth_cred_unref(&old
);
2684 * kauth_cred_get_with_ref
2686 * Description: Takes a reference on the current thread's credential, and then
2687 * returns a pointer to it to the caller.
2689 * Parameters: (void)
2691 * Returns: (kauth_cred_t) Pointer to the current thread's
2692 * newly referenced credential
2694 * Notes: This function takes a reference on the credential before
2695 * returning it to the caller.
2697 * It is the responsibility of the calling code to release this
2698 * reference when the credential is no longer in use.
2700 * Since the returned reference may be a persistent reference
2701 * (e.g. one cached in another data structure with a lifetime
2702 * longer than the calling function), this release may be delayed
2703 * until such time as the persistent reference is to be destroyed.
2704 * An example of this would be the per vnode credential cache used
2705 * to accelerate lookup operations.
2708 kauth_cred_get_with_ref(void)
2711 struct uthread
*uthread
;
2713 uthread
= get_bsdthread_info(current_thread());
2715 if (uthread
== NULL
)
2716 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
2717 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
2718 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
2721 * We can lazy-bind credentials to threads, as long as their processes
2724 * XXX If we later inline this function, the code in this block
2725 * XXX should probably be called out in a function.
2727 if (uthread
->uu_ucred
== NOCRED
) {
2728 /* take reference for new cred in thread */
2729 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
2731 /* take a reference for our caller */
2732 kauth_cred_ref(uthread
->uu_ucred
);
2733 return(uthread
->uu_ucred
);
2738 * kauth_cred_proc_ref
2740 * Description: Takes a reference on the current process's credential, and
2741 * then returns a pointer to it to the caller.
2743 * Parameters: procp Process whose credential we
2744 * intend to take a reference on
2746 * Returns: (kauth_cred_t) Pointer to the process's
2747 * newly referenced credential
2749 * Locks: PROC_LOCK is held before taking the reference and released
2750 * after the refeence is taken to protect the p_ucred field of
2751 * the process referred to by procp.
2753 * Notes: This function takes a reference on the credential before
2754 * returning it to the caller.
2756 * It is the responsibility of the calling code to release this
2757 * reference when the credential is no longer in use.
2759 * Since the returned reference may be a persistent reference
2760 * (e.g. one cached in another data structure with a lifetime
2761 * longer than the calling function), this release may be delayed
2762 * until such time as the persistent reference is to be destroyed.
2763 * An example of this would be the per vnode credential cache used
2764 * to accelerate lookup operations.
2767 kauth_cred_proc_ref(proc_t procp
)
2772 cred
= proc_ucred(procp
);
2773 kauth_cred_ref(cred
);
2782 * Description: Allocate a new credential
2784 * Parameters: (void)
2786 * Returns: !NULL Newly allocated credential
2787 * NULL Insufficient memory
2789 * Notes: The newly allocated credential is zero'ed as part of the
2790 * allocation process, with the exception of the reference
2791 * count, which is set to 1 to indicate a single reference
2792 * held by the caller.
2794 * Since newly allocated credentials have no external pointers
2795 * referencing them, prior to making them visible in an externally
2796 * visible pointer (e.g. by adding them to the credential hash
2797 * cache) is the only legal time in which an existing credential
2798 * can be safely iinitialized or modified directly.
2800 * After initialization, the caller is expected to call the
2801 * function kauth_cred_add() to add the credential to the hash
2802 * cache, after which time it's frozen and becomes publically
2805 * The release protocol depends on kauth_hash_add() being called
2806 * before kauth_cred_rele() (there is a diagnostic panic which
2807 * will trigger if this protocol is not observed).
2809 * XXX: This function really ought to be static, rather than being
2810 * exported as KPI, since a failure of kauth_cred_add() can only
2811 * be handled by an explicit free of the credential; such frees
2812 * depend on knowlegdge of the allocation method used, which is
2813 * permitted to change between kernel revisions.
2815 * XXX: In the insufficient resource case, this code panic's rather
2816 * than returning a NULL pointer; the code that calls this
2817 * function needs to be audited before this can be changed.
2820 kauth_cred_alloc(void)
2822 kauth_cred_t newcred
;
2824 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
2826 bzero(newcred
, sizeof(*newcred
));
2827 newcred
->cr_ref
= 1;
2828 newcred
->cr_audit
.as_aia_p
= &audit_default_aia
;
2829 /* XXX the following will go away with cr_au */
2830 newcred
->cr_au
.ai_auid
= AU_DEFAUDITID
;
2831 /* must do this, or cred has same group membership as uid 0 */
2832 newcred
->cr_gmuid
= KAUTH_UID_NONE
;
2835 panic("kauth_cred_alloc: couldn't allocate credential");
2839 #if KAUTH_CRED_HASH_DEBUG
2844 mac_cred_label_init(newcred
);
2854 * Description: Look to see if we already have a known credential in the hash
2855 * cache; if one is found, bump the reference count and return
2856 * it. If there are no credentials that match the given
2857 * credential, then allocate a new credential.
2859 * Parameters: cred Template for credential to
2862 * Returns: (kauth_cred_t) The credential that was found
2863 * in the hash or created
2864 * NULL kauth_cred_add() failed, or
2865 * there was not an egid specified
2867 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
2868 * maintain this field, we can't expect callers to know how it
2869 * needs to be set. Callers should be prepared for this field
2870 * to be overwritten.
2872 * XXX: This code will tight-loop if memory for a new credential is
2873 * persistently unavailable; this is perhaps not the wisest way
2874 * to handle this condition, but current callers do not expect
2878 kauth_cred_create(kauth_cred_t cred
)
2880 kauth_cred_t found_cred
, new_cred
= NULL
;
2883 KAUTH_CRED_HASH_LOCK_ASSERT();
2885 if (cred
->cr_flags
& CRF_NOMEMBERD
) {
2886 cred
->cr_gmuid
= KAUTH_UID_NONE
;
2889 * If the template credential is not opting out of external
2890 * group membership resolution, then we need to check that
2891 * the UID we will be using is resolvable by the external
2892 * resolver. If it's not, then we opt it out anyway, since
2893 * all future external resolution requests will be failing
2894 * anyway, and potentially taking a long time to do it. We
2895 * use gid 0 because we always know it will exist and not
2896 * trigger additional lookups. This is OK, because we end up
2897 * precatching the information here as a result.
2899 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
2901 * It's a recognized value; we don't really care about
2902 * the answer, so long as it's something the external
2903 * resolver could have vended.
2905 cred
->cr_gmuid
= cred
->cr_uid
;
2908 * It's not something the external resolver could
2909 * have vended, so we don't want to ask it more
2910 * questions about the credential in the future. This
2911 * speeds up future lookups, as long as the caller
2912 * caches results; otherwise, it the same recurring
2913 * cost. Since most credentials are used multiple
2914 * times, we still get some performance win from this.
2916 cred
->cr_gmuid
= KAUTH_UID_NONE
;
2917 cred
->cr_flags
|= CRF_NOMEMBERD
;
2921 /* Caller *must* specify at least the egid in cr_groups[0] */
2922 if (cred
->cr_ngroups
< 1)
2926 KAUTH_CRED_HASH_LOCK();
2927 found_cred
= kauth_cred_find(cred
);
2928 if (found_cred
!= NULL
) {
2930 * Found an existing credential so we'll bump
2931 * reference count and return
2933 kauth_cred_ref(found_cred
);
2934 KAUTH_CRED_HASH_UNLOCK();
2937 KAUTH_CRED_HASH_UNLOCK();
2940 * No existing credential found. Create one and add it to
2943 new_cred
= kauth_cred_alloc();
2944 if (new_cred
!= NULL
) {
2946 new_cred
->cr_uid
= cred
->cr_uid
;
2947 new_cred
->cr_ruid
= cred
->cr_ruid
;
2948 new_cred
->cr_svuid
= cred
->cr_svuid
;
2949 new_cred
->cr_rgid
= cred
->cr_rgid
;
2950 new_cred
->cr_svgid
= cred
->cr_svgid
;
2951 new_cred
->cr_gmuid
= cred
->cr_gmuid
;
2952 new_cred
->cr_ngroups
= cred
->cr_ngroups
;
2953 bcopy(&cred
->cr_groups
[0], &new_cred
->cr_groups
[0], sizeof(new_cred
->cr_groups
));
2955 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
2956 sizeof(new_cred
->cr_audit
));
2957 /* XXX the following bcopy() will go away with cr_au */
2958 bcopy(&cred
->cr_au
, &new_cred
->cr_au
,
2959 sizeof(new_cred
->cr_au
));
2961 new_cred
->cr_flags
= cred
->cr_flags
;
2963 KAUTH_CRED_HASH_LOCK();
2964 err
= kauth_cred_add(new_cred
);
2965 KAUTH_CRED_HASH_UNLOCK();
2967 /* Retry if kauth_cred_add returns non zero value */
2971 mac_cred_label_destroy(new_cred
);
2973 AUDIT_SESSION_UNREF(new_cred
);
2975 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
2985 * kauth_cred_setresuid
2987 * Description: Update the given credential using the UID arguments. The given
2988 * UIDs are used to set the effective UID, real UID, saved UID,
2989 * and GMUID (used for group membership checking).
2991 * Parameters: cred The original credential
2992 * ruid The new real UID
2993 * euid The new effective UID
2994 * svuid The new saved UID
2995 * gmuid KAUTH_UID_NONE -or- the new
2996 * group membership UID
2998 * Returns: (kauth_cred_t) The updated credential
3000 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3001 * setting, so if you don't want it to change, pass it the
3002 * previous value, explicitly.
3004 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3005 * if it returns a credential other than the one it is passed,
3006 * will have dropped the reference on the passed credential. All
3007 * callers should be aware of this, and treat this function as an
3008 * unref + ref, potentially on different credentials.
3010 * Because of this, the caller is expected to take its own
3011 * reference on the credential passed as the first parameter,
3012 * and be prepared to release the reference on the credential
3013 * that is returned to them, if it is not intended to be a
3014 * persistent reference.
3017 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3019 struct ucred temp_cred
;
3021 NULLCRED_CHECK(cred
);
3024 * We don't need to do anything if the UIDs we are changing are
3025 * already the same as the UIDs passed in
3027 if ((euid
== KAUTH_UID_NONE
|| cred
->cr_uid
== euid
) &&
3028 (ruid
== KAUTH_UID_NONE
|| cred
->cr_ruid
== ruid
) &&
3029 (svuid
== KAUTH_UID_NONE
|| cred
->cr_svuid
== svuid
) &&
3030 (cred
->cr_gmuid
== gmuid
)) {
3031 /* no change needed */
3036 * Look up in cred hash table to see if we have a matching credential
3037 * with the new values; this is done by calling kauth_cred_update().
3039 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3040 if (euid
!= KAUTH_UID_NONE
) {
3041 temp_cred
.cr_uid
= euid
;
3043 if (ruid
!= KAUTH_UID_NONE
) {
3044 temp_cred
.cr_ruid
= ruid
;
3046 if (svuid
!= KAUTH_UID_NONE
) {
3047 temp_cred
.cr_svuid
= svuid
;
3051 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3052 * opt out of participation in external group resolution, unless we
3053 * unless we explicitly opt back in later.
3055 if ((temp_cred
.cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3056 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3059 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3064 * kauth_cred_setresgid
3066 * Description: Update the given credential using the GID arguments. The given
3067 * GIDs are used to set the effective GID, real GID, and saved
3070 * Parameters: cred The original credential
3071 * rgid The new real GID
3072 * egid The new effective GID
3073 * svgid The new saved GID
3075 * Returns: (kauth_cred_t) The updated credential
3077 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3078 * if it returns a credential other than the one it is passed,
3079 * will have dropped the reference on the passed credential. All
3080 * callers should be aware of this, and treat this function as an
3081 * unref + ref, potentially on different credentials.
3083 * Because of this, the caller is expected to take its own
3084 * reference on the credential passed as the first parameter,
3085 * and be prepared to release the reference on the credential
3086 * that is returned to them, if it is not intended to be a
3087 * persistent reference.
3090 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3092 struct ucred temp_cred
;
3094 NULLCRED_CHECK(cred
);
3095 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3098 * We don't need to do anything if the given GID are already the
3099 * same as the GIDs in the credential.
3101 if (cred
->cr_groups
[0] == egid
&&
3102 cred
->cr_rgid
== rgid
&&
3103 cred
->cr_svgid
== svgid
) {
3104 /* no change needed */
3109 * Look up in cred hash table to see if we have a matching credential
3110 * with the new values; this is done by calling kauth_cred_update().
3112 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3113 if (egid
!= KAUTH_GID_NONE
) {
3114 /* displacing a supplementary group opts us out of memberd */
3115 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3116 DEBUG_CRED_CHANGE("displaced!\n");
3117 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3118 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3120 DEBUG_CRED_CHANGE("not displaced\n");
3123 if (rgid
!= KAUTH_GID_NONE
) {
3124 temp_cred
.cr_rgid
= rgid
;
3126 if (svgid
!= KAUTH_GID_NONE
) {
3127 temp_cred
.cr_svgid
= svgid
;
3130 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3135 * Update the given credential with the given groups. We only allocate a new
3136 * credential when the given gid actually results in changes to the existing
3138 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3139 * which will be used for group membership checking.
3142 * kauth_cred_setgroups
3144 * Description: Update the given credential using the provide supplementary
3145 * group list and group membership UID
3147 * Parameters: cred The original credential
3148 * groups Pointer to gid_t array which
3149 * contains the new group list
3150 * groupcount The cound of valid groups which
3151 * are contained in 'groups'
3152 * gmuid KAUTH_UID_NONE -or- the new
3153 * group membership UID
3155 * Returns: (kauth_cred_t) The updated credential
3157 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3158 * setting, so if you don't want it to change, pass it the
3159 * previous value, explicitly.
3161 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3162 * if it returns a credential other than the one it is passed,
3163 * will have dropped the reference on the passed credential. All
3164 * callers should be aware of this, and treat this function as an
3165 * unref + ref, potentially on different credentials.
3167 * Because of this, the caller is expected to take its own
3168 * reference on the credential passed as the first parameter,
3169 * and be prepared to release the reference on the credential
3170 * that is returned to them, if it is not intended to be a
3171 * persistent reference.
3173 * XXX: Changes are determined in ordinal order - if the caller pasess
3174 * in the same groups list that is already present in the
3175 * credential, but the members are in a different order, even if
3176 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3177 * is considered a modification to the credential, and a new
3178 * credential is created.
3180 * This should perhaps be better optimized, but it is considered
3181 * to be the caller's problem.
3184 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
3187 struct ucred temp_cred
;
3189 NULLCRED_CHECK(cred
);
3192 * We don't need to do anything if the given list of groups does not
3195 if ((cred
->cr_gmuid
== gmuid
) && (cred
->cr_ngroups
== groupcount
)) {
3196 for (i
= 0; i
< groupcount
; i
++) {
3197 if (cred
->cr_groups
[i
] != groups
[i
])
3200 if (i
== groupcount
) {
3201 /* no change needed */
3207 * Look up in cred hash table to see if we have a matching credential
3208 * with new values. If we are setting or clearing the gmuid, then
3209 * update the cr_flags, since clearing it is sticky. This permits an
3210 * opt-out of memberd processing using setgroups(), and an opt-in
3211 * using initgroups(). This is required for POSIX conformance.
3213 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3214 temp_cred
.cr_ngroups
= groupcount
;
3215 bcopy(groups
, temp_cred
.cr_groups
, sizeof(temp_cred
.cr_groups
));
3216 temp_cred
.cr_gmuid
= gmuid
;
3217 if (gmuid
== KAUTH_UID_NONE
)
3218 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3220 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3222 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3227 * kauth_cred_setuidgid
3229 * Description: Update the given credential using the UID and GID arguments.
3230 * The given UID is used to set the effective UID, real UID, and
3231 * saved UID. The given GID is used to set the effective GID,
3232 * real GID, and saved GID.
3234 * Parameters: cred The original credential
3235 * uid The new UID to use
3236 * gid The new GID to use
3238 * Returns: (kauth_cred_t) The updated credential
3240 * Notes: We set the gmuid to uid if the credential we are inheriting
3241 * from has not opted out of memberd participation; otherwise
3242 * we set it to KAUTH_UID_NONE
3244 * This code is only ever called from the per-thread credential
3245 * code path in the "set per thread credential" case; and in
3246 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3249 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3250 * if it returns a credential other than the one it is passed,
3251 * will have dropped the reference on the passed credential. All
3252 * callers should be aware of this, and treat this function as an
3253 * unref + ref, potentially on different credentials.
3255 * Because of this, the caller is expected to take its own
3256 * reference on the credential passed as the first parameter,
3257 * and be prepared to release the reference on the credential
3258 * that is returned to them, if it is not intended to be a
3259 * persistent reference.
3262 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3264 struct ucred temp_cred
;
3266 NULLCRED_CHECK(cred
);
3269 * We don't need to do anything if the effective, real and saved
3270 * user IDs are already the same as the user ID passed into us.
3272 if (cred
->cr_uid
== uid
&& cred
->cr_ruid
== uid
&& cred
->cr_svuid
== uid
&&
3273 cred
->cr_groups
[0] == gid
&& cred
->cr_rgid
== gid
&& cred
->cr_svgid
== gid
) {
3274 /* no change needed */
3279 * Look up in cred hash table to see if we have a matching credential
3280 * with the new values.
3282 bzero(&temp_cred
, sizeof(temp_cred
));
3283 temp_cred
.cr_uid
= uid
;
3284 temp_cred
.cr_ruid
= uid
;
3285 temp_cred
.cr_svuid
= uid
;
3286 temp_cred
.cr_flags
= cred
->cr_flags
;
3287 /* inherit the opt-out of memberd */
3288 if (cred
->cr_flags
& CRF_NOMEMBERD
) {
3289 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3290 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3292 temp_cred
.cr_gmuid
= uid
;
3293 temp_cred
.cr_flags
&= ~CRF_NOMEMBERD
;
3295 temp_cred
.cr_ngroups
= 1;
3296 /* displacing a supplementary group opts us out of memberd */
3297 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3298 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3299 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3301 temp_cred
.cr_rgid
= gid
;
3302 temp_cred
.cr_svgid
= gid
;
3304 temp_cred
.cr_label
= cred
->cr_label
;
3307 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3312 * kauth_cred_setsvuidgid
3314 * Description: Function used by execve to set the saved uid and gid values
3315 * for suid/sgid programs
3317 * Parameters: cred The credential to update
3318 * uid The saved uid to set
3319 * gid The saved gid to set
3321 * Returns: (kauth_cred_t) The updated credential
3323 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3324 * if it returns a credential other than the one it is passed,
3325 * will have dropped the reference on the passed credential. All
3326 * callers should be aware of this, and treat this function as an
3327 * unref + ref, potentially on different credentials.
3329 * Because of this, the caller is expected to take its own
3330 * reference on the credential passed as the first parameter,
3331 * and be prepared to release the reference on the credential
3332 * that is returned to them, if it is not intended to be a
3333 * persistent reference.
3336 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3338 struct ucred temp_cred
;
3340 NULLCRED_CHECK(cred
);
3341 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
3344 * We don't need to do anything if the effective, real and saved
3345 * uids are already the same as the uid provided. This check is
3346 * likely insufficient.
3348 if (cred
->cr_svuid
== uid
&& cred
->cr_svgid
== gid
) {
3349 /* no change needed */
3352 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3354 /* look up in cred hash table to see if we have a matching credential
3357 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3358 temp_cred
.cr_svuid
= uid
;
3359 temp_cred
.cr_svgid
= gid
;
3361 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3366 * kauth_cred_setauditinfo
3368 * Description: Update the given credential using the given au_session_t.
3370 * Parameters: cred The original credential
3371 * auditinfo_p Pointer to ne audit information
3373 * Returns: (kauth_cred_t) The updated credential
3375 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3376 * if it returns a credential other than the one it is passed,
3377 * will have dropped the reference on the passed credential. All
3378 * callers should be aware of this, and treat this function as an
3379 * unref + ref, potentially on different credentials.
3381 * Because of this, the caller is expected to take its own
3382 * reference on the credential passed as the first parameter,
3383 * and be prepared to release the reference on the credential
3384 * that is returned to them, if it is not intended to be a
3385 * persistent reference.
3388 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
3390 struct ucred temp_cred
;
3392 NULLCRED_CHECK(cred
);
3395 * We don't need to do anything if the audit info is already the
3396 * same as the audit info in the credential provided.
3398 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
3399 /* no change needed */
3403 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3404 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
3405 /* XXX the following will go away with cr_au */
3406 temp_cred
.cr_au
.ai_auid
= auditinfo_p
->as_aia_p
->ai_auid
;
3407 temp_cred
.cr_au
.ai_mask
.am_success
=
3408 auditinfo_p
->as_mask
.am_success
;
3409 temp_cred
.cr_au
.ai_mask
.am_failure
=
3410 auditinfo_p
->as_mask
.am_failure
;
3411 temp_cred
.cr_au
.ai_termid
.port
=
3412 auditinfo_p
->as_aia_p
->ai_termid
.at_port
;
3413 temp_cred
.cr_au
.ai_termid
.machine
=
3414 auditinfo_p
->as_aia_p
->ai_termid
.at_addr
[0];
3415 temp_cred
.cr_au
.ai_asid
= auditinfo_p
->as_aia_p
->ai_asid
;
3418 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
3423 * kauth_cred_label_update
3425 * Description: Update the MAC label associated with a credential
3427 * Parameters: cred The original credential
3428 * label The MAC label to set
3430 * Returns: (kauth_cred_t) The updated credential
3432 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3433 * if it returns a credential other than the one it is passed,
3434 * will have dropped the reference on the passed credential. All
3435 * callers should be aware of this, and treat this function as an
3436 * unref + ref, potentially on different credentials.
3438 * Because of this, the caller is expected to take its own
3439 * reference on the credential passed as the first parameter,
3440 * and be prepared to release the reference on the credential
3441 * that is returned to them, if it is not intended to be a
3442 * persistent reference.
3445 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
3447 kauth_cred_t newcred
;
3448 struct ucred temp_cred
;
3450 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3452 mac_cred_label_init(&temp_cred
);
3453 mac_cred_label_associate(cred
, &temp_cred
);
3454 mac_cred_label_update(&temp_cred
, label
);
3456 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3457 mac_cred_label_destroy(&temp_cred
);
3462 * kauth_cred_label_update_execve
3464 * Description: Update the MAC label associated with a credential as
3467 * Parameters: cred The original credential
3469 * scriptl The script MAC label
3470 * execl The executable MAC label
3471 * disjointp Pointer to flag to set if old
3472 * and returned credentials are
3475 * Returns: (kauth_cred_t) The updated credential
3478 * *disjointp Set to 1 for disjoint creds
3480 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3481 * if it returns a credential other than the one it is passed,
3482 * will have dropped the reference on the passed credential. All
3483 * callers should be aware of this, and treat this function as an
3484 * unref + ref, potentially on different credentials.
3486 * Because of this, the caller is expected to take its own
3487 * reference on the credential passed as the first parameter,
3488 * and be prepared to release the reference on the credential
3489 * that is returned to them, if it is not intended to be a
3490 * persistent reference.
3494 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
3495 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
3498 kauth_cred_t newcred
;
3499 struct ucred temp_cred
;
3501 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3503 mac_cred_label_init(&temp_cred
);
3504 mac_cred_label_associate(cred
, &temp_cred
);
3505 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
3506 vp
, scriptl
, execl
);
3508 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3509 mac_cred_label_destroy(&temp_cred
);
3514 * kauth_proc_label_update
3516 * Description: Update the label inside the credential associated with the process.
3518 * Parameters: p The process to modify
3519 * label The label to place in the process credential
3521 * Notes: The credential associated with the process may change as a result
3522 * of this call. The caller should not assume the process reference to
3523 * the old credential still exists.
3525 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
3527 kauth_cred_t my_cred
, my_new_cred
;
3529 my_cred
= kauth_cred_proc_ref(p
);
3531 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
3533 /* get current credential and take a reference while we muck with it */
3537 * Set the credential with new info. If there is no change,
3538 * we get back the same credential we passed in; if there is
3539 * a change, we drop the reference on the credential we
3540 * passed in. The subsequent compare is safe, because it is
3541 * a pointer compare rather than a contents compare.
3543 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
3544 if (my_cred
!= my_new_cred
) {
3546 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
);
3550 * We need to protect for a race where another thread
3551 * also changed the credential after we took our
3552 * reference. If p_ucred has changed then we should
3553 * restart this again with the new cred.
3555 if (p
->p_ucred
!= my_cred
) {
3557 kauth_cred_unref(&my_new_cred
);
3558 my_cred
= kauth_cred_proc_ref(p
);
3562 p
->p_ucred
= my_new_cred
;
3563 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3568 /* Drop old proc reference or our extra reference */
3569 kauth_cred_unref(&my_cred
);
3575 * kauth_proc_label_update_execve
3577 * Description: Update the label inside the credential associated with the
3578 * process as part of a transitioning execve. The label will
3579 * be updated by the policies as part of this processing, not
3580 * provided up front.
3582 * Parameters: p The process to modify
3583 * ctx The context of the exec
3584 * vp The vnode being exec'ed
3585 * scriptl The script MAC label
3586 * execl The executable MAC label
3588 * Returns: 0 Label update did not make credential
3590 * 1 Label update caused credential to be
3593 * Notes: The credential associated with the process WILL change as a
3594 * result of this call. The caller should not assume the process
3595 * reference to the old credential still exists.
3598 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
3599 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
3601 kauth_cred_t my_cred
, my_new_cred
;
3604 my_cred
= kauth_cred_proc_ref(p
);
3606 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
3608 /* get current credential and take a reference while we muck with it */
3612 * Set the credential with new info. If there is no change,
3613 * we get back the same credential we passed in; if there is
3614 * a change, we drop the reference on the credential we
3615 * passed in. The subsequent compare is safe, because it is
3616 * a pointer compare rather than a contents compare.
3618 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
3619 if (my_cred
!= my_new_cred
) {
3621 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
);
3625 * We need to protect for a race where another thread
3626 * also changed the credential after we took our
3627 * reference. If p_ucred has changed then we should
3628 * restart this again with the new cred.
3630 if (p
->p_ucred
!= my_cred
) {
3632 kauth_cred_unref(&my_new_cred
);
3633 my_cred
= kauth_cred_proc_ref(p
);
3637 p
->p_ucred
= my_new_cred
;
3638 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
3643 /* Drop old proc reference or our extra reference */
3644 kauth_cred_unref(&my_cred
);
3651 * for temporary binary compatibility
3653 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
3655 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
3657 return kauth_cred_label_update(cred
, label
);
3660 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
3662 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
3664 return kauth_proc_label_update(p
, label
);
3670 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
3671 * Since we cannot build our export lists based on the kernel configuration we need
3675 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
3681 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
3688 * for temporary binary compatibility
3690 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
3692 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
3697 int kauth_proc_setlabel(struct proc
*p
, void *label
);
3699 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
3709 * Description: Add a reference to the passed credential
3711 * Parameters: cred The credential to reference
3715 * Notes: This function adds a reference to the provided credential;
3716 * the existing reference on the credential is assumed to be
3717 * held stable over this operation by taking the appropriate
3718 * lock to protect the pointer from which it is being referenced,
3719 * if necessary (e.g. the proc lock is held over the call if the
3720 * credential being referenced is from p_ucred, the vnode lock
3721 * if from the per vnode name cache cred cache, and so on).
3723 * This is safe from the kauth_cred_unref() path, since an atomic
3724 * add is used, and the unref path specifically checks to see that
3725 * the value has not been changed to add a reference between the
3726 * time the credential is unreferenced by another pointer and the
3727 * time it is unreferenced from the cred hash cache.
3730 kauth_cred_ref(kauth_cred_t cred
)
3734 NULLCRED_CHECK(cred
);
3736 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
3739 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
3741 #if 0 // use this to watch a specific credential
3742 if ( is_target_cred( cred
) != 0 ) {
3752 * kauth_cred_unref_hashlocked
3754 * Description: release a credential reference; when the last reference is
3755 * released, the credential will be freed.
3757 * Parameters: credp Pointer to address containing
3758 * credential to be freed
3763 * *credp Set to NOCRED
3765 * Notes: This function assumes the credential hash lock is held.
3767 * This function is internal use only, since the hash lock is
3768 * scoped to this compilation unit.
3770 * This function destroys the contents of the pointer passed by
3771 * the caller to prevent the caller accidently attempting to
3772 * release a given reference twice in error.
3774 * The last reference is considered to be released when a release
3775 * of a credential of a reference count of 2 occurs; this is an
3776 * intended effect, to take into accout the reference held by
3777 * the credential hash, which is released at the same time.
3780 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
3784 KAUTH_CRED_HASH_LOCK_ASSERT();
3785 NULLCRED_CHECK(*credp
);
3787 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
3791 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
3793 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
3796 #if 0 // use this to watch a specific credential
3797 if ( is_target_cred( *credp
) != 0 ) {
3803 * If the old_value is 2, then we have just released the last external
3804 * reference to this credential
3806 if (old_value
< 3) {
3807 /* The last absolute reference is our credential hash table */
3808 kauth_cred_remove(*credp
);
3817 * Description: Release a credential reference while holding the credential
3818 * hash lock; when the last reference is released, the credential
3821 * Parameters: credp Pointer to address containing
3822 * credential to be freed
3827 * *credp Set to NOCRED
3829 * Notes: See kauth_cred_unref_hashlocked() for more information.
3833 kauth_cred_unref(kauth_cred_t
*credp
)
3835 KAUTH_CRED_HASH_LOCK();
3836 kauth_cred_unref_hashlocked(credp
);
3837 KAUTH_CRED_HASH_UNLOCK();
3845 * Description: release a credential reference; when the last reference is
3846 * released, the credential will be freed
3848 * Parameters: cred Credential to release
3852 * DEPRECATED: This interface is obsolete due to a failure to clear out the
3853 * clear the pointer in the caller to avoid multiple releases of
3854 * the same credential. The currently recommended interface is
3855 * kauth_cred_unref().
3858 kauth_cred_rele(kauth_cred_t cred
)
3860 kauth_cred_unref(&cred
);
3862 #endif /* !__LP64__ */
3868 * Description: Duplicate a credential via alloc and copy; the new credential
3871 * Parameters: cred The credential to duplicate
3873 * Returns: (kauth_cred_t) The duplicate credential
3875 * Notes: The typical value to calling this routine is if you are going
3876 * to modify an existing credential, and expect to need a new one
3877 * from the hash cache.
3879 * This should probably not be used in the majority of cases;
3880 * if you are using it instead of kauth_cred_create(), you are
3881 * likely making a mistake.
3883 * The newly allocated credential is copied as part of the
3884 * allocation process, with the exception of the reference
3885 * count, which is set to 1 to indicate a single reference
3886 * held by the caller.
3888 * Since newly allocated credentials have no external pointers
3889 * referencing them, prior to making them visible in an externally
3890 * visible pointer (e.g. by adding them to the credential hash
3891 * cache) is the only legal time in which an existing credential
3892 * can be safely iinitialized or modified directly.
3894 * After initialization, the caller is expected to call the
3895 * function kauth_cred_add() to add the credential to the hash
3896 * cache, after which time it's frozen and becomes publically
3899 * The release protocol depends on kauth_hash_add() being called
3900 * before kauth_cred_rele() (there is a diagnostic panic which
3901 * will trigger if this protocol is not observed).
3905 kauth_cred_dup(kauth_cred_t cred
)
3907 kauth_cred_t newcred
;
3909 struct label
*temp_label
;
3913 if (cred
== NOCRED
|| cred
== FSCRED
)
3914 panic("kauth_cred_dup: bad credential");
3916 newcred
= kauth_cred_alloc();
3917 if (newcred
!= NULL
) {
3919 temp_label
= newcred
->cr_label
;
3921 bcopy(cred
, newcred
, sizeof(*newcred
));
3923 newcred
->cr_label
= temp_label
;
3924 mac_cred_label_associate(cred
, newcred
);
3926 AUDIT_SESSION_REF(cred
);
3927 newcred
->cr_ref
= 1;
3933 * kauth_cred_copy_real
3935 * Description: Returns a credential based on the passed credential but which
3936 * reflects the real rather than effective UID and GID.
3938 * Parameters: cred The credential from which to
3939 * derive the new credential
3941 * Returns: (kauth_cred_t) The copied credential
3943 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
3944 * result, the caller is responsible for dropping BOTH the
3945 * additional reference on the passed cred (if any), and the
3946 * credential returned by this function. The drop should be
3947 * via the satnadr kauth_cred_unref() KPI.
3950 kauth_cred_copy_real(kauth_cred_t cred
)
3952 kauth_cred_t newcred
= NULL
, found_cred
;
3953 struct ucred temp_cred
;
3955 /* if the credential is already 'real', just take a reference */
3956 if ((cred
->cr_ruid
== cred
->cr_uid
) &&
3957 (cred
->cr_rgid
== cred
->cr_gid
)) {
3958 kauth_cred_ref(cred
);
3963 * Look up in cred hash table to see if we have a matching credential
3964 * with the new values.
3966 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3967 temp_cred
.cr_uid
= cred
->cr_ruid
;
3968 /* displacing a supplementary group opts us out of memberd */
3969 if (kauth_cred_change_egid(&temp_cred
, cred
->cr_rgid
)) {
3970 temp_cred
.cr_flags
|= CRF_NOMEMBERD
;
3971 temp_cred
.cr_gmuid
= KAUTH_UID_NONE
;
3974 * If the cred is not opted out, make sure we are using the r/euid
3977 if (temp_cred
.cr_gmuid
!= KAUTH_UID_NONE
)
3978 temp_cred
.cr_gmuid
= cred
->cr_ruid
;
3983 KAUTH_CRED_HASH_LOCK();
3984 found_cred
= kauth_cred_find(&temp_cred
);
3985 if (found_cred
== cred
) {
3986 /* same cred so just bail */
3987 KAUTH_CRED_HASH_UNLOCK();
3990 if (found_cred
!= NULL
) {
3992 * Found a match so we bump reference count on new
3993 * one. We leave the old one alone.
3995 kauth_cred_ref(found_cred
);
3996 KAUTH_CRED_HASH_UNLOCK();
4001 * Must allocate a new credential, copy in old credential
4002 * data and update the real user and group IDs.
4004 newcred
= kauth_cred_dup(&temp_cred
);
4005 err
= kauth_cred_add(newcred
);
4006 KAUTH_CRED_HASH_UNLOCK();
4008 /* Retry if kauth_cred_add() fails */
4012 mac_cred_label_destroy(newcred
);
4014 AUDIT_SESSION_UNREF(newcred
);
4016 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4027 * Description: Common code to update a credential
4029 * Parameters: old_cred Reference counted credential
4031 * model_cred Non-reference counted model
4032 * credential to apply to the
4033 * credential to be updated
4034 * retain_auditinfo Flag as to whether or not the
4035 * audit information should be
4036 * copied from the old_cred into
4039 * Returns: (kauth_cred_t) The updated credential
4041 * IMPORTANT: This function will potentially return a credential other than
4042 * the one it is passed, and if so, it will have dropped the
4043 * reference on the passed credential. All callers should be
4044 * aware of this, and treat this function as an unref + ref,
4045 * potentially on different credentials.
4047 * Because of this, the caller is expected to take its own
4048 * reference on the credential passed as the first parameter,
4049 * and be prepared to release the reference on the credential
4050 * that is returned to them, if it is not intended to be a
4051 * persistent reference.
4054 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4055 boolean_t retain_auditinfo
)
4057 kauth_cred_t found_cred
, new_cred
= NULL
;
4060 * Make sure we carry the auditinfo forward to the new credential
4061 * unless we are actually updating the auditinfo.
4063 if (retain_auditinfo
) {
4064 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
4065 sizeof(model_cred
->cr_audit
));
4066 /* XXX following bcopy will go away with cr_au */
4067 bcopy(&old_cred
->cr_au
, &model_cred
->cr_au
,
4068 sizeof(model_cred
->cr_au
));
4074 KAUTH_CRED_HASH_LOCK();
4075 found_cred
= kauth_cred_find(model_cred
);
4076 if (found_cred
== old_cred
) {
4077 /* same cred so just bail */
4078 KAUTH_CRED_HASH_UNLOCK();
4081 if (found_cred
!= NULL
) {
4082 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
4084 * Found a match so we bump reference count on new
4085 * one and decrement reference count on the old one.
4087 kauth_cred_ref(found_cred
);
4088 kauth_cred_unref_hashlocked(&old_cred
);
4089 KAUTH_CRED_HASH_UNLOCK();
4094 * Must allocate a new credential using the model. also
4095 * adds the new credential to the credential hash table.
4097 new_cred
= kauth_cred_dup(model_cred
);
4098 err
= kauth_cred_add(new_cred
);
4099 KAUTH_CRED_HASH_UNLOCK();
4101 /* retry if kauth_cred_add returns non zero value */
4105 mac_cred_label_destroy(new_cred
);
4107 AUDIT_SESSION_UNREF(new_cred
);
4109 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
4113 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
4114 kauth_cred_unref(&old_cred
);
4122 * Description: Add the given credential to our credential hash table and
4123 * take an additional reference to account for our use of the
4124 * credential in the hash table
4126 * Parameters: new_cred Credential to insert into cred
4129 * Returns: 0 Success
4130 * -1 Hash insertion failed: caller
4133 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4135 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4138 kauth_cred_add(kauth_cred_t new_cred
)
4142 KAUTH_CRED_HASH_LOCK_ASSERT();
4144 hash_key
= kauth_cred_get_hashkey(new_cred
);
4145 hash_key
%= kauth_cred_table_size
;
4147 /* race fix - there is a window where another matching credential
4148 * could have been inserted between the time this one was created and we
4149 * got the hash lock. If we find a match return an error and have the
4152 if (kauth_cred_find(new_cred
) != NULL
) {
4156 /* take a reference for our use in credential hash table */
4157 kauth_cred_ref(new_cred
);
4159 /* insert the credential into the hash table */
4160 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
4169 * Description: Remove the given credential from our credential hash table
4171 * Parameters: cred Credential to remove from cred
4176 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4178 * Notes: The check for the reference increment after entry is generally
4179 * agree to be safe, since we use atomic operations, and the
4180 * following code occurs with the hash lock held; in theory, this
4181 * protects us from the 2->1 reference that gets us here.
4184 kauth_cred_remove(kauth_cred_t cred
)
4187 kauth_cred_t found_cred
;
4189 hash_key
= kauth_cred_get_hashkey(cred
);
4190 hash_key
%= kauth_cred_table_size
;
4193 if (cred
->cr_ref
< 1)
4194 panic("cred reference underflow");
4195 if (cred
->cr_ref
> 1)
4196 return; /* someone else got a ref */
4198 /* Find cred in the credential hash table */
4199 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4200 if (found_cred
== cred
) {
4201 /* found a match, remove it from the hash table */
4202 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
4204 mac_cred_label_destroy(cred
);
4206 AUDIT_SESSION_UNREF(cred
);
4209 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
4210 #if KAUTH_CRED_HASH_DEBUG
4217 /* Did not find a match... this should not happen! XXX Make panic? */
4218 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
4226 * Description: Using the given credential data, look for a match in our
4227 * credential hash table
4229 * Parameters: cred Credential to lookup in cred
4232 * Returns: NULL Not found
4233 * !NULL Matching cedential already in
4236 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4239 kauth_cred_find(kauth_cred_t cred
)
4242 kauth_cred_t found_cred
;
4244 KAUTH_CRED_HASH_LOCK_ASSERT();
4246 #if KAUTH_CRED_HASH_DEBUG
4247 static int test_count
= 0;
4250 if ((test_count
% 200) == 0) {
4251 kauth_cred_hash_print();
4255 hash_key
= kauth_cred_get_hashkey(cred
);
4256 hash_key
%= kauth_cred_table_size
;
4258 /* Find cred in the credential hash table */
4259 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4263 * don't worry about the label unless the flags in
4264 * either credential tell us to.
4266 if ((found_cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0 ||
4267 (cred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) {
4268 /* include the label pointer in the compare */
4269 match
= (bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4270 (sizeof(struct ucred
) -
4271 offsetof(struct ucred
, cr_uid
))) == 0);
4273 /* flags have to match, but skip the label in bcmp */
4274 match
= (found_cred
->cr_flags
== cred
->cr_flags
&&
4275 bcmp(&found_cred
->cr_uid
, &cred
->cr_uid
,
4276 (offsetof(struct ucred
, cr_label
) -
4277 offsetof(struct ucred
, cr_uid
))) == 0);
4284 /* No match found */
4293 * Description: Generates a hash key using data that makes up a credential;
4296 * Parameters: datap Pointer to data to hash
4297 * data_len Count of bytes to hash
4298 * start_key Start key value
4300 * Returns: (u_long) Returned hash key
4302 static inline u_long
4303 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4305 u_long hash_key
= start_key
;
4308 while (data_len
> 0) {
4309 hash_key
= (hash_key
<< 4) + *datap
++;
4310 temp
= hash_key
& 0xF0000000;
4312 hash_key
^= temp
>> 24;
4322 * kauth_cred_get_hashkey
4324 * Description: Generate a hash key using data that makes up a credential;
4325 * based on ElfHash. We hash on the entire credential data,
4326 * not including the ref count or the TAILQ, which are mutable;
4327 * everything else isn't.
4329 * We also avoid the label (if the flag is not set saying the
4330 * label is actually enforced).
4332 * Parameters: cred Credential for which hash is
4335 * Returns: (u_long) Returned hash key
4338 kauth_cred_get_hashkey(kauth_cred_t cred
)
4340 u_long hash_key
= 0;
4342 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_uid
,
4343 ((cred
->cr_flags
& CRF_MAC_ENFORCE
) ?
4344 sizeof(struct ucred
) : offsetof(struct ucred
, cr_label
)) -
4345 offsetof(struct ucred
, cr_uid
),
4351 #if KAUTH_CRED_HASH_DEBUG
4353 * kauth_cred_hash_print
4355 * Description: Print out cred hash cache table information for debugging
4356 * purposes, including the credential contents
4358 * Parameters: (void)
4362 * Implicit returns: Results in console output
4365 kauth_cred_hash_print(void)
4368 kauth_cred_t found_cred
;
4370 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
4371 /* count slot hits, misses, collisions, and max depth */
4372 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4373 printf("[%02d] ", i
);
4375 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4380 kauth_cred_print(found_cred
);
4384 printf("NOCRED \n");
4388 #endif /* KAUTH_CRED_HASH_DEBUG */
4391 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4395 * Description: Print out an individual credential's contents for debugging
4398 * Parameters: cred The credential to print out
4402 * Implicit returns: Results in console output
4405 kauth_cred_print(kauth_cred_t cred
)
4409 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
);
4410 printf("group count %d gids ", cred
->cr_ngroups
);
4411 for (i
= 0; i
< NGROUPS
; i
++) {
4414 printf("%d ", cred
->cr_groups
[i
]);
4416 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
4417 printf("auditinfo_addr %d %d %d %d %d %d\n",
4418 cred
->cr_audit
.s_aia_p
->ai_auid
,
4419 cred
->cr_audit
.as_mask
.am_success
,
4420 cred
->cr_audit
.as_mask
.am_failure
,
4421 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
4422 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
4423 cred
->cr_audit
.as_aia_p
->ai_asid
);
4426 int is_target_cred( kauth_cred_t the_cred
)
4428 if ( the_cred
->cr_uid
!= 0 )
4430 if ( the_cred
->cr_ruid
!= 0 )
4432 if ( the_cred
->cr_svuid
!= 0 )
4434 if ( the_cred
->cr_ngroups
!= 11 )
4436 if ( the_cred
->cr_groups
[0] != 11 )
4438 if ( the_cred
->cr_groups
[1] != 81 )
4440 if ( the_cred
->cr_groups
[2] != 63947 )
4442 if ( the_cred
->cr_groups
[3] != 80288 )
4444 if ( the_cred
->cr_groups
[4] != 89006 )
4446 if ( the_cred
->cr_groups
[5] != 52173 )
4448 if ( the_cred
->cr_groups
[6] != 84524 )
4450 if ( the_cred
->cr_groups
[7] != 79 )
4452 if ( the_cred
->cr_groups
[8] != 80292 )
4454 if ( the_cred
->cr_groups
[9] != 80 )
4456 if ( the_cred
->cr_groups
[10] != 90824 )
4458 if ( the_cred
->cr_rgid
!= 11 )
4460 if ( the_cred
->cr_svgid
!= 11 )
4462 if ( the_cred
->cr_gmuid
!= 3475 )
4464 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
4467 if ( the_cred->cr_audit.as_mask.am_success != 0 )
4469 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
4471 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
4473 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
4475 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
4477 if ( the_cred->cr_flags != 0 )
4480 return( -1 ); // found target cred
4483 void get_backtrace( void )
4486 void * my_stack
[ MAX_STACK_DEPTH
];
4489 if ( cred_debug_buf_p
== NULL
) {
4490 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
4491 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
4494 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
4495 /* buffer is full */
4499 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
4500 if ( my_depth
== 0 ) {
4501 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
4505 /* fill new backtrace */
4506 my_slot
= cred_debug_buf_p
->next_slot
;
4507 cred_debug_buf_p
->next_slot
++;
4508 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
4509 for ( i
= 0; i
< my_depth
; i
++ ) {
4510 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
4517 /* subset of struct ucred for use in sysctl_dump_creds */
4518 struct debug_ucred
{
4520 u_long cr_ref
; /* reference count */
4521 uid_t cr_uid
; /* effective user id */
4522 uid_t cr_ruid
; /* real user id */
4523 uid_t cr_svuid
; /* saved user id */
4524 short cr_ngroups
; /* number of groups in advisory list */
4525 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
4526 gid_t cr_rgid
; /* real group id */
4527 gid_t cr_svgid
; /* saved group id */
4528 uid_t cr_gmuid
; /* UID for group membership purposes */
4529 struct auditinfo_addr cr_audit
; /* user auditing data. */
4530 void *cr_label
; /* MACF label */
4531 int cr_flags
; /* flags on credential */
4533 typedef struct debug_ucred debug_ucred
;
4535 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
4536 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
4539 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
4543 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4545 int i
, j
, counter
= 0;
4548 kauth_cred_t found_cred
;
4549 debug_ucred
* cred_listp
;
4550 debug_ucred
* nextp
;
4552 /* This is a readonly node. */
4553 if (req
->newptr
!= USER_ADDR_NULL
)
4556 /* calculate space needed */
4557 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4558 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4563 /* they are querying us so just return the space required. */
4564 if (req
->oldptr
== USER_ADDR_NULL
) {
4565 counter
+= 10; // add in some padding;
4566 req
->oldidx
= counter
* sizeof(debug_ucred
);
4570 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4571 if ( cred_listp
== NULL
) {
4575 /* fill in creds to send back */
4578 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4579 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4580 nextp
->credp
= found_cred
;
4581 nextp
->cr_ref
= found_cred
->cr_ref
;
4582 nextp
->cr_uid
= found_cred
->cr_uid
;
4583 nextp
->cr_ruid
= found_cred
->cr_ruid
;
4584 nextp
->cr_svuid
= found_cred
->cr_svuid
;
4585 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
4586 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
4587 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
4589 nextp
->cr_rgid
= found_cred
->cr_rgid
;
4590 nextp
->cr_svgid
= found_cred
->cr_svgid
;
4591 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
4592 nextp
->cr_audit
.ai_auid
=
4593 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
4594 nextp
->cr_audit
.ai_mask
.am_success
=
4595 found_cred
->cr_audit
.as_mask
.am_success
;
4596 nextp
->cr_audit
.ai_mask
.am_failure
=
4597 found_cred
->cr_audit
.as_mask
.am_failure
;
4598 nextp
->cr_audit
.ai_termid
.at_port
=
4599 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
4600 nextp
->cr_audit
.ai_termid
.at_type
=
4601 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
4602 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
4603 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
4604 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
4605 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
4606 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
4607 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
4608 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
4609 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
4610 nextp
->cr_audit
.ai_asid
=
4611 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
4612 nextp
->cr_audit
.ai_flags
=
4613 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
4614 nextp
->cr_label
= found_cred
->cr_label
;
4615 nextp
->cr_flags
= found_cred
->cr_flags
;
4617 space
+= sizeof(debug_ucred
);
4618 if ( space
> req
->oldlen
) {
4619 FREE(cred_listp
, M_TEMP
);
4624 req
->oldlen
= space
;
4625 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
4626 FREE(cred_listp
, M_TEMP
);
4631 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
4632 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
4635 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
4639 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
4644 cred_debug_buffer
* bt_bufp
;
4645 cred_backtrace
* nextp
;
4647 /* This is a readonly node. */
4648 if (req
->newptr
!= USER_ADDR_NULL
)
4651 if ( cred_debug_buf_p
== NULL
) {
4655 /* calculate space needed */
4656 space
= sizeof( cred_debug_buf_p
->next_slot
);
4657 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
4659 /* they are querying us so just return the space required. */
4660 if (req
->oldptr
== USER_ADDR_NULL
) {
4661 req
->oldidx
= space
;
4665 if ( space
> req
->oldlen
) {
4669 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
4670 if ( bt_bufp
== NULL
) {
4674 /* fill in backtrace info to send back */
4675 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
4676 space
= sizeof(bt_bufp
->next_slot
);
4678 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
4679 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
4680 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
4681 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
4682 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
4684 space
+= sizeof(*nextp
);
4687 req
->oldlen
= space
;
4688 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
4689 FREE(bt_bufp
, M_TEMP
);
4693 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */