2 * Copyright (c) 2004-2010 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/stat.h> /* For manifest constants in posix_cred_access */
56 #include <sys/sysproto.h>
57 #include <sys/kern_callout.h>
58 #include <mach/message.h>
59 #include <mach/host_security.h>
61 /* mach_absolute_time() */
62 #include <mach/clock_types.h>
63 #include <mach/mach_types.h>
64 #include <mach/mach_time.h>
66 #include <libkern/OSAtomic.h>
68 #include <kern/task.h>
69 #include <kern/lock.h>
73 #define MACH_ASSERT 1 /* XXX so bogus */
74 #include <kern/assert.h>
77 #include <security/mac.h>
78 #include <security/mac_framework.h>
81 void mach_kauth_cred_uthread_update( void );
83 #define CRED_DIAGNOSTIC 0
85 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
88 * Credential debugging; we can track entry into a function that might
89 * change a credential, and we can track actual credential changes that
92 * Note: Does *NOT* currently include per-thread credential changes
96 #define DEBUG_CRED_ENTER printf
97 #define DEBUG_CRED_CHANGE printf
98 extern void kauth_cred_print(kauth_cred_t cred
);
100 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
102 int is_target_cred( kauth_cred_t the_cred
);
103 void get_backtrace( void );
105 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
106 __unused
int arg2
, struct sysctl_req
*req
);
108 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
109 __unused
int arg2
, struct sysctl_req
*req
);
111 #define MAX_STACK_DEPTH 8
112 struct cred_backtrace
{
114 void * stack
[ MAX_STACK_DEPTH
];
116 typedef struct cred_backtrace cred_backtrace
;
118 #define MAX_CRED_BUFFER_SLOTS 200
119 struct cred_debug_buffer
{
121 cred_backtrace stack_buffer
[ MAX_CRED_BUFFER_SLOTS
];
123 typedef struct cred_debug_buffer cred_debug_buffer
;
124 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
126 #else /* !DEBUG_CRED */
128 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
129 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
131 #endif /* !DEBUG_CRED */
134 * Interface to external identity resolver.
136 * The architecture of the interface is simple; the external resolver calls
137 * in to get work, then calls back with completed work. It also calls us
138 * to let us know that it's (re)started, so that we can resubmit work if it
142 static lck_mtx_t
*kauth_resolver_mtx
;
143 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
144 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
146 static volatile pid_t kauth_resolver_identity
;
147 static int kauth_resolver_registered
;
148 static uint32_t kauth_resolver_sequence
;
149 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
151 struct kauth_resolver_work
{
152 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
153 struct kauth_identity_extlookup kr_work
;
156 uint64_t kr_subtime
; /* submission time */
159 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
160 #define KAUTH_REQUEST_SUBMITTED (1<<1)
161 #define KAUTH_REQUEST_DONE (1<<2)
165 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
166 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
167 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
169 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
170 static int kauth_resolver_complete(user_addr_t message
);
171 static int kauth_resolver_getwork(user_addr_t message
);
172 static int kauth_resolver_getwork2(user_addr_t message
);
174 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
175 static int kauth_cred_primes_index
= 0;
176 static int kauth_cred_table_size
= 0;
178 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
179 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
181 /* Weighted moving average for resolver response time */
182 static struct kco_moving_average resolver_ma
;
184 #define KAUTH_CRED_HASH_DEBUG 0
186 static int kauth_cred_add(kauth_cred_t new_cred
);
187 static void kauth_cred_remove(kauth_cred_t cred
);
188 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
189 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
190 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
191 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
193 #if KAUTH_CRED_HASH_DEBUG
194 static int kauth_cred_count
= 0;
195 static void kauth_cred_hash_print(void);
196 static void kauth_cred_print(kauth_cred_t cred
);
201 * kauth_resolver_init
203 * Description: Initialize the daemon side of the credential identity resolver
209 * Notes: Intialize the credential identity resolver for use; the
210 * credential identity resolver is the KPI used by the user
211 * space credential identity resolver daemon to communicate
212 * with the kernel via the identitysvc() system call..
214 * This is how membership in more than 16 groups (1 effective
215 * and 15 supplementary) is supported, and also how UID's,
216 * UUID's, and so on, are translated to/from POSIX credential
219 * The credential identity resolver operates by attempting to
220 * determine identity first from the credential, then from
221 * the kernel credential identity cache, and finally by
222 * enqueueing a request to a user space daemon.
224 * This function is called from kauth_init() in the file
225 * kern_authorization.c.
228 kauth_resolver_init(void)
230 TAILQ_INIT(&kauth_resolver_unsubmitted
);
231 TAILQ_INIT(&kauth_resolver_submitted
);
232 TAILQ_INIT(&kauth_resolver_done
);
233 kauth_resolver_sequence
= 31337;
234 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
237 * 110% of average response time is "too long" and should be reported
239 kco_ma_init(&resolver_ma
, 110, KCO_MA_F_WMA
);
244 * kauth_resolver_submit
246 * Description: Submit an external credential identity resolution request to
247 * the user space daemon.
249 * Parameters: lkp A pointer to an external
251 * extend_data extended data for kr_extend
254 * EWOULDBLOCK No resolver registered
255 * EINTR Operation interrupted (e.g. by
257 * ENOMEM Could not allocate work item
258 * copyinstr:EFAULT Bad message from user space
259 * workp->kr_result:??? An error from the user space
260 * daemon (includes ENOENT!)
265 * Notes: Allocate a work queue entry, submit the work and wait for
266 * the operation to either complete or time out. Outstanding
267 * operations may also be cancelled.
269 * Submission is by means of placing the item on a work queue
270 * which is serviced by an external resolver thread calling
271 * into the kernel. The caller then sleeps until timeout,
272 * cancellation, or an external resolver thread calls in with
273 * a result message to kauth_resolver_complete(). All of these
274 * events wake the caller back up.
276 * This code is called from either kauth_cred_ismember_gid()
277 * for a group membership request, or it is called from
278 * kauth_cred_cache_lookup() when we get a cache miss.
281 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
283 struct kauth_resolver_work
*workp
, *killp
;
285 int error
, shouldfree
;
288 /* no point actually blocking if the resolver isn't up yet */
289 if (kauth_resolver_identity
== 0) {
291 * We've already waited an initial <kauth_resolver_timeout>
292 * seconds with no result.
294 * Sleep on a stack address so no one wakes us before timeout;
295 * we sleep a half a second in case we are a high priority
296 * process, so that memberd doesn't starve while we are in a
297 * tight loop between user and kernel, eating all the CPU.
299 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
300 if (kauth_resolver_identity
== 0) {
302 * if things haven't changed while we were asleep,
303 * tell the caller we couldn't get an authoritative
310 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
314 workp
->kr_work
= *lkp
;
315 workp
->kr_extend
= extend_data
;
317 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
318 workp
->kr_result
= 0;
321 * We insert the request onto the unsubmitted queue, the call in from
322 * the resolver will it to the submitted thread when appropriate.
324 KAUTH_RESOLVER_LOCK();
325 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
326 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
329 * XXX We *MUST NOT* attempt to coelesce identical work items due to
330 * XXX the inability to ensure order of update of the request item
331 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
332 * XXX for each item repeat the update when they wake up.
334 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
337 * Wake up an external resolver thread to deal with the new work; one
338 * may not be available, and if not, then the request will be grabed
339 * when a resolver thread comes back into the kernel to request new
342 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
344 /* we could compute a better timeout here */
345 ts
.tv_sec
= kauth_resolver_timeout
;
347 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
348 /* request has been completed? */
349 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
351 /* woken because the resolver has died? */
352 if (kauth_resolver_identity
== 0) {
362 * Update the moving average of how long the request took; if it
363 * took longer than the time threshold, then we complain about it
366 duration
= mach_absolute_time() - workp
->kr_subtime
;
367 if (kco_ma_addsample(&resolver_ma
, duration
)) {
369 uint64_t old_average
;
373 /* If we can't get information, don't log anything */
374 if (kco_ma_info(&resolver_ma
, KCO_MA_F_WMA
, &average
, &old_average
, &threshold
, &count
)) {
375 char pname
[MAXCOMLEN
+1] = "(NULL)";
376 proc_name(kauth_resolver_identity
, pname
, sizeof(pname
));
377 // <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);
381 /* if the request was processed, copy the result */
383 *lkp
= workp
->kr_work
;
386 * If the request timed out and was never collected, the resolver
387 * is dead and probably not coming back anytime soon. In this
388 * case we revert to no-resolver behaviour, and punt all the other
389 * sleeping requests to clear the backlog.
391 if ((error
== EWOULDBLOCK
) && (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
)) {
392 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
395 * Make the current resolver non-authoritative, and mark it as
396 * no longer registered to prevent kauth_cred_ismember_gid()
397 * enqueueing more work until a new one is registered. This
398 * mitigates the damage a crashing resolver may inflict.
400 kauth_resolver_identity
= 0;
401 kauth_resolver_registered
= 0;
403 /* kill all the other requestes that are waiting as well */
404 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
406 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
408 /* Cause all waiting-for-work threads to return EIO */
409 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
413 * drop our reference on the work item, and note whether we should
416 if (--workp
->kr_refs
<= 0) {
417 /* work out which list we have to remove it from */
418 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
419 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
420 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
421 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
422 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
423 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
425 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
429 /* someone else still has a reference on this request */
433 /* collect request result */
435 error
= workp
->kr_result
;
437 KAUTH_RESOLVER_UNLOCK();
440 * If we dropped the last reference, free the request.
443 FREE(workp
, M_KAUTH
);
446 KAUTH_DEBUG("RESOLVER - returning %d", error
);
454 * Description: System call interface for the external identity resolver.
456 * Parameters: uap->message Message from daemon to kernel
458 * Returns: 0 Successfully became resolver
459 * EPERM Not the resolver process
460 * kauth_authorize_generic:EPERM Not root user
461 * kauth_resolver_complete:EIO
462 * kauth_resolver_complete:EFAULT
463 * kauth_resolver_getwork:EINTR
464 * kauth_resolver_getwork:EFAULT
466 * Notes: This system call blocks until there is work enqueued, at
467 * which time the kernel wakes it up, and a message from the
468 * kernel is copied out to the identity resolution daemon, which
469 * proceed to attempt to resolve it. When the resolution has
470 * completed (successfully or not), the daemon called back into
471 * this system call to give the result to the kernel, and wait
472 * for the next request.
475 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
477 int opcode
= uap
->opcode
;
478 user_addr_t message
= uap
->message
;
479 struct kauth_resolver_work
*workp
;
484 * New server registering itself.
486 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
487 new_id
= current_proc()->p_pid
;
488 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
489 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
492 KAUTH_RESOLVER_LOCK();
493 if (kauth_resolver_identity
!= new_id
) {
494 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
496 * We have a new server, so assume that all the old requests have been lost.
498 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
499 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
500 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
501 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
502 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
505 * Allow user space resolver to override the
506 * external resolution timeout
508 if (message
> 30 && message
< 10000) {
509 kauth_resolver_timeout
= message
;
510 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
512 kauth_resolver_identity
= new_id
;
513 kauth_resolver_registered
= 1;
514 wakeup(&kauth_resolver_unsubmitted
);
516 KAUTH_RESOLVER_UNLOCK();
521 * Beyond this point, we must be the resolver process.
523 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
524 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
528 if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
530 * Terminate outstanding requests; without an authoritative
531 * resolver, we are now back on our own authority.
533 struct kauth_resolver_work
*killp
;
535 KAUTH_RESOLVER_LOCK();
538 * Clear the identity, but also mark it as unregistered so
539 * there is no explicit future expectation of us getting a
540 * new resolver any time soon.
542 kauth_resolver_identity
= 0;
543 kauth_resolver_registered
= 0;
545 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
547 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
549 /* Cause all waiting-for-work threads to return EIO */
550 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
551 KAUTH_RESOLVER_UNLOCK();
555 * Got a result returning?
557 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
558 if ((error
= kauth_resolver_complete(message
)) != 0)
563 * Caller wants to take more work?
565 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
566 if ((error
= kauth_resolver_getwork(message
)) != 0)
575 * kauth_resolver_getwork_continue
577 * Description: Continuation for kauth_resolver_getwork
579 * Parameters: result Error code or 0 for the sleep
580 * that got us to this function
583 * EINTR Interrupted (e.g. by signal)
584 * kauth_resolver_getwork2:EFAULT
586 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
590 kauth_resolver_getwork_continue(int result
)
597 KAUTH_RESOLVER_UNLOCK();
602 * If we lost a race with another thread/memberd restarting, then we
603 * need to go back to sleep to look for more work. If it was memberd
604 * restarting, then the msleep0() will error out here, as our thread
605 * will already be "dead".
607 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
610 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
612 * If this is a wakeup from another thread in the resolver
613 * deregistering it, error out the request-for-work thread
615 if (!kauth_resolver_identity
)
617 KAUTH_RESOLVER_UNLOCK();
621 thread
= current_thread();
622 ut
= get_bsdthread_info(thread
);
623 message
= ut
->uu_kauth
.message
;
624 return(kauth_resolver_getwork2(message
));
629 * kauth_resolver_getwork2
631 * Decription: Common utility function to copy out a identity resolver work
632 * item from the kernel to user space as part of the user space
633 * identity resolver requesting work.
635 * Parameters: message message to user space
638 * EFAULT Bad user space message address
640 * Notes: This common function exists to permit the use of continuations
641 * in the identity resoultion process. This frees up the stack
642 * while we are waiting for the user space resolver to complete
643 * a request. This is specifically used so that our per thread
644 * cost can be small, and we will therefore be willing to run a
645 * larger number of threads in the user space identity resolver.
648 kauth_resolver_getwork2(user_addr_t message
)
650 struct kauth_resolver_work
*workp
;
654 * Note: We depend on the caller protecting us from a NULL work item
655 * queue, since we must have the kauth resolver lock on entry to this
658 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
661 * Copy out the external lookup structure for the request, not
662 * including the el_extend field, which contains the address of the
663 * external buffer provided by the external resolver into which we
664 * copy the extension request information.
667 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
668 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
672 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
673 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
674 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
675 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
680 * Handle extended requests here; if we have a request of a type where
681 * the kernel wants a translation of extended information, then we need
682 * to copy it out into the extended buffer, assuming the buffer is
683 * valid; we only attempt to get the buffer address if we have request
684 * data to copy into it.
688 * translate a user@domain string into a uid/gid/whatever
690 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
693 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
695 size_t actual
; /* not used */
697 * Use copyoutstr() to reduce the copy size; we let
698 * this catch a NULL uaddr because we shouldn't be
699 * asking in that case anyway.
701 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
704 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
708 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
709 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
710 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
711 workp
->kr_subtime
= mach_absolute_time();
712 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
715 KAUTH_RESOLVER_UNLOCK();
721 * kauth_resolver_getwork
723 * Description: Get a work item from the enqueued requests from the kernel and
724 * give it to the user space daemon.
726 * Parameters: message message to user space
729 * EINTR Interrupted (e.g. by signal)
730 * kauth_resolver_getwork2:EFAULT
732 * Notes: This function blocks in a continuation if there are no work
733 * items available for processing at the time the user space
734 * identity resolution daemon makes a request for work. This
735 * permits a large number of threads to be used by the daemon,
736 * without using a lot of wired kernel memory when there are no
737 * acctual request outstanding.
740 kauth_resolver_getwork(user_addr_t message
)
742 struct kauth_resolver_work
*workp
;
745 KAUTH_RESOLVER_LOCK();
747 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
748 thread_t thread
= current_thread();
749 struct uthread
*ut
= get_bsdthread_info(thread
);
751 ut
->uu_kauth
.message
= message
;
752 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
753 KAUTH_RESOLVER_UNLOCK();
755 * If this is a wakeup from another thread in the resolver
756 * deregistering it, error out the request-for-work thread
758 if (!kauth_resolver_identity
)
762 return kauth_resolver_getwork2(message
);
767 * kauth_resolver_complete
769 * Description: Return a result from userspace.
771 * Parameters: message message from user space
774 * EIO The resolver is dead
775 * copyin:EFAULT Bad message from user space
778 kauth_resolver_complete(user_addr_t message
)
780 struct kauth_identity_extlookup extl
;
781 struct kauth_resolver_work
*workp
;
782 struct kauth_resolver_work
*killp
;
786 * Copy in the mesage, including the extension field, since we are
787 * copying into a local variable.
789 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
790 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
794 KAUTH_RESOLVER_LOCK();
798 switch (extl
.el_result
) {
799 case KAUTH_EXTLOOKUP_INPROG
:
803 /* XXX this should go away once memberd is updated */
805 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
811 case KAUTH_EXTLOOKUP_SUCCESS
:
814 case KAUTH_EXTLOOKUP_FATAL
:
815 /* fatal error means the resolver is dead */
816 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
818 * Terminate outstanding requests; without an authoritative
819 * resolver, we are now back on our own authority. Tag the
820 * resolver unregistered to prevent kauth_cred_ismember_gid()
821 * enqueueing more work until a new one is registered. This
822 * mitigates the damage a crashing resolver may inflict.
824 kauth_resolver_identity
= 0;
825 kauth_resolver_registered
= 0;
827 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
829 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
831 /* Cause all waiting-for-work threads to return EIO */
832 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
833 /* and return EIO to the caller */
837 case KAUTH_EXTLOOKUP_BADRQ
:
838 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
842 case KAUTH_EXTLOOKUP_FAILURE
:
843 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
848 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
854 * In the case of a fatal error, we assume that the resolver will
855 * restart quickly and re-collect all of the outstanding requests.
856 * Thus, we don't complete the request which returned the fatal
859 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
860 /* scan our list for this request */
861 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
863 if (workp
->kr_seqno
== extl
.el_seqno
) {
866 * Get the request of the submitted queue so
867 * that it is not cleaned up out from under
870 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
871 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
872 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
873 workp
->kr_result
= result
;
875 /* Copy the result message to the work item. */
876 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
879 * Check if we have a result in the extension
880 * field; if we do, then we need to separately
881 * copy the data from the message el_extend
882 * into the request buffer that's in the work
883 * item. We have to do it here because we do
884 * not want to wake up the waiter until the
885 * data is in their buffer, and because the
886 * actual request response may be destroyed
887 * by the time the requester wakes up, and they
888 * do not have access to the user space buffer
891 * It is safe to drop and reacquire the lock
892 * here because we've already removed the item
893 * from the submission queue, but have not yet
894 * moved it to the completion queue. Note that
895 * near simultaneous requests may result in
896 * duplication of requests for items in this
897 * window. This should not be a performance
898 * issue and is easily detectable by comparing
899 * time to live on last response vs. time of
900 * next request in the resolver logs.
902 if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
903 size_t actual
; /* notused */
905 KAUTH_RESOLVER_UNLOCK();
906 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
907 KAUTH_RESOLVER_LOCK();
911 * Move the completed work item to the
912 * completion queue and wake up requester(s)
914 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
921 * Note that it's OK for us not to find anything; if the request has
922 * timed out the work record will be gone.
924 KAUTH_RESOLVER_UNLOCK();
934 struct kauth_identity
{
935 TAILQ_ENTRY(kauth_identity
) ki_link
;
937 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
938 #define KI_VALID_GID (1<<1)
939 #define KI_VALID_GUID (1<<2)
940 #define KI_VALID_NTSID (1<<3)
941 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
942 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
947 const char *ki_name
; /* string name from string cache */
949 * Expiry times are the earliest time at which we will disregard the
950 * cached state and go to userland. Before then if the valid bit is
951 * set, we will return the cached value. If it's not set, we will
952 * not go to userland to resolve, just assume that there is no answer
955 time_t ki_guid_expiry
;
956 time_t ki_ntsid_expiry
;
959 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
960 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
961 static int kauth_identity_count
;
963 static lck_mtx_t
*kauth_identity_mtx
;
964 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
965 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
968 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
969 ntsid_t
*ntsidp
, time_t ntsid_expiry
, const char *name
, int nametype
);
970 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
971 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
972 static void kauth_identity_lru(struct kauth_identity
*kip
);
973 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
974 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
975 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
976 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
977 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
978 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
979 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
983 * kauth_identity_init
985 * Description: Initialize the kernel side of the credential identity resolver
991 * Notes: Intialize the credential identity resolver for use; the
992 * credential identity resolver is the KPI used to communicate
993 * with a user space credential identity resolver daemon.
995 * This function is called from kauth_init() in the file
996 * kern_authorization.c.
999 kauth_identity_init(void)
1001 TAILQ_INIT(&kauth_identities
);
1002 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1007 * kauth_identity_alloc
1009 * Description: Allocate and fill out a kauth_identity structure for
1010 * translation between {UID|GID}/GUID/NTSID
1014 * Returns: NULL Insufficient memory to satisfy
1016 * !NULL A pointer to the applocated
1017 * structure, filled in
1019 * Notes: It is illegal to translate between UID and GID; any given UUID
1020 * or NTSID can only refer to an NTSID or UUID (respectively),
1021 * and *either* a UID *or* a GID, but not both.
1023 static struct kauth_identity
*
1024 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
, const char *name
, int nametype
)
1026 struct kauth_identity
*kip
;
1028 /* get and fill in a new identity */
1029 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1031 if (gid
!= KAUTH_GID_NONE
) {
1033 kip
->ki_valid
= KI_VALID_GID
;
1035 if (uid
!= KAUTH_UID_NONE
) {
1036 if (kip
->ki_valid
& KI_VALID_GID
)
1037 panic("can't allocate kauth identity with both uid and gid");
1039 kip
->ki_valid
= KI_VALID_UID
;
1041 if (guidp
!= NULL
) {
1042 kip
->ki_guid
= *guidp
;
1043 kip
->ki_valid
|= KI_VALID_GUID
;
1045 kip
->ki_guid_expiry
= guid_expiry
;
1046 if (ntsidp
!= NULL
) {
1047 kip
->ki_ntsid
= *ntsidp
;
1048 kip
->ki_valid
|= KI_VALID_NTSID
;
1050 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1052 kip
->ki_name
= name
;
1053 kip
->ki_valid
|= nametype
;
1061 * kauth_identity_register_and_free
1063 * Description: Register an association between identity tokens. The passed
1064 * 'kip' is consumed by this function.
1066 * Parameters: kip Pointer to kauth_identity
1067 * structure to register
1071 * Notes: The memory pointer to by 'kip' is assumed to have been
1072 * previously allocated via kauth_identity_alloc().
1075 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1077 struct kauth_identity
*ip
;
1080 * We search the cache for the UID listed in the incoming association.
1081 * If we already have an entry, the new information is merged.
1084 KAUTH_IDENTITY_LOCK();
1085 if (kip
->ki_valid
& KI_VALID_UID
) {
1086 if (kip
->ki_valid
& KI_VALID_GID
)
1087 panic("kauth_identity: can't insert record with both UID and GID as key");
1088 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1089 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1091 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1092 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1093 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1096 panic("kauth_identity: can't insert record without UID or GID as key");
1100 /* we already have an entry, merge/overwrite */
1101 if (kip
->ki_valid
& KI_VALID_GUID
) {
1102 ip
->ki_guid
= kip
->ki_guid
;
1103 ip
->ki_valid
|= KI_VALID_GUID
;
1105 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1106 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1107 ip
->ki_ntsid
= kip
->ki_ntsid
;
1108 ip
->ki_valid
|= KI_VALID_NTSID
;
1110 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1111 /* a valid ki_name field overwrites the previous name field */
1112 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1113 /* if there's an old one, discard it */
1114 const char *oname
= NULL
;
1115 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1116 oname
= ip
->ki_name
;
1117 ip
->ki_name
= kip
->ki_name
;
1118 kip
->ki_name
= oname
;
1120 /* and discard the incoming entry */
1124 * if we don't have any information on this identity, add it;
1125 * if it pushes us over our limit, discard the oldest one.
1127 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1128 if (++kauth_identity_count
> KAUTH_IDENTITY_CACHEMAX
) {
1129 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1130 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1131 kauth_identity_count
--;
1134 KAUTH_IDENTITY_UNLOCK();
1135 /* have to drop lock before freeing expired entry (it may be in use) */
1137 /* if the ki_name field is used, clear it first */
1138 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1139 vfs_removename(ip
->ki_name
);
1140 /* free the expired entry */
1147 * kauth_identity_updatecache
1149 * Description: Given a lookup result, add any associations that we don't
1150 * currently have; replace ones which have changed.
1152 * Parameters: elp External lookup result from
1153 * user space daemon to kernel
1154 * rkip pointer to returned kauth
1156 * extend_data Extended data (can vary)
1161 * *rkip Modified (if non-NULL)
1163 * Notes: For extended information requests, this code relies on the fact
1164 * that elp->el_flags is never used as an rvalue, and is only
1165 * ever bit-tested for valid lookup information we are willing
1168 * XXX: We may have to do the same in the case that extended data was
1169 * passed out to user space to ensure that the request string
1170 * gets cached; we may also be able to use the rkip as an
1171 * input to avoid this. The jury is still out.
1173 * XXX: This codes performance could be improved for multiple valid
1174 * results by combining the loop iteration in a single loop.
1177 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1180 struct kauth_identity
*kip
;
1181 const char *speculative_name
= NULL
;
1186 * If there is extended data, and that data represents a name rather
1187 * than something else, speculatively create an entry for it in the
1188 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1189 * over the allocation later.
1191 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1192 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1193 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1196 /* user identity? */
1197 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1198 KAUTH_IDENTITY_LOCK();
1199 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1200 /* matching record */
1201 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1202 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1203 kip
->ki_guid
= elp
->el_uguid
;
1204 kip
->ki_valid
|= KI_VALID_GUID
;
1206 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_uguid_valid
;
1207 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1208 kip
->ki_ntsid
= elp
->el_usid
;
1209 kip
->ki_valid
|= KI_VALID_NTSID
;
1211 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_usid_valid
;
1212 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1213 const char *oname
= kip
->ki_name
;
1214 kip
->ki_name
= speculative_name
;
1215 speculative_name
= NULL
;
1216 kip
->ki_valid
|= KI_VALID_PWNAM
;
1219 * free oname (if any) outside
1222 speculative_name
= oname
;
1225 kauth_identity_lru(kip
);
1228 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1232 KAUTH_IDENTITY_UNLOCK();
1233 /* not found in cache, add new record */
1235 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1236 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1237 tv
.tv_sec
+ elp
->el_uguid_valid
,
1238 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1239 tv
.tv_sec
+ elp
->el_usid_valid
,
1240 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1245 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1246 speculative_name
= NULL
;
1247 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1248 kauth_identity_register_and_free(kip
);
1253 /* group identity? (ignore, if we already processed it as a user) */
1254 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1255 KAUTH_IDENTITY_LOCK();
1256 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1257 /* matching record */
1258 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1259 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1260 kip
->ki_guid
= elp
->el_gguid
;
1261 kip
->ki_valid
|= KI_VALID_GUID
;
1263 kip
->ki_guid_expiry
= tv
.tv_sec
+ elp
->el_gguid_valid
;
1264 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1265 kip
->ki_ntsid
= elp
->el_gsid
;
1266 kip
->ki_valid
|= KI_VALID_NTSID
;
1268 kip
->ki_ntsid_expiry
= tv
.tv_sec
+ elp
->el_gsid_valid
;
1269 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1270 const char *oname
= kip
->ki_name
;
1271 kip
->ki_name
= speculative_name
;
1272 speculative_name
= NULL
;
1273 kip
->ki_valid
|= KI_VALID_GRNAM
;
1276 * free oname (if any) outside
1279 speculative_name
= oname
;
1282 kauth_identity_lru(kip
);
1285 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1289 KAUTH_IDENTITY_UNLOCK();
1290 /* not found in cache, add new record */
1292 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1293 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1294 tv
.tv_sec
+ elp
->el_gguid_valid
,
1295 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1296 tv
.tv_sec
+ elp
->el_gsid_valid
,
1297 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1302 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1303 speculative_name
= NULL
;
1304 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1305 kauth_identity_register_and_free(kip
);
1310 /* If we have a name reference to drop, drop it here */
1311 if (speculative_name
!= NULL
) {
1312 vfs_removename(speculative_name
);
1318 * kauth_identity_lru
1320 * Description: Promote the entry to the head of the LRU, assumes the cache
1323 * Parameters: kip kauth identity to move to the
1324 * head of the LRU list, if it's
1329 * Notes: This is called even if the entry has expired; typically an
1330 * expired entry that's been looked up is about to be revalidated,
1331 * and having it closer to the head of the LRU means finding it
1332 * quickly again when the revalidation comes through.
1335 kauth_identity_lru(struct kauth_identity
*kip
)
1337 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1338 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1339 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1345 * kauth_identity_guid_expired
1347 * Description: Handle lazy expiration of GUID translations.
1349 * Parameters: kip kauth identity to check for
1352 * Returns: 1 Expired
1356 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1361 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
1362 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1367 * kauth_identity_ntsid_expired
1369 * Description: Handle lazy expiration of NTSID translations.
1371 * Parameters: kip kauth identity to check for
1374 * Returns: 1 Expired
1378 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1383 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1384 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1389 * kauth_identity_find_uid
1391 * Description: Search for an entry by UID
1393 * Parameters: uid UID to find
1394 * kir Pointer to return area
1395 * getname Name buffer, if ki_name wanted
1401 * *klr Modified, if found
1404 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1406 struct kauth_identity
*kip
;
1408 KAUTH_IDENTITY_LOCK();
1409 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1410 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1411 kauth_identity_lru(kip
);
1412 /* Copy via structure assignment */
1414 /* If a name is wanted and one exists, copy it out */
1415 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1416 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1420 KAUTH_IDENTITY_UNLOCK();
1421 return((kip
== NULL
) ? ENOENT
: 0);
1426 * kauth_identity_find_gid
1428 * Description: Search for an entry by GID
1430 * Parameters: gid GID to find
1431 * kir Pointer to return area
1432 * getname Name buffer, if ki_name wanted
1438 * *klr Modified, if found
1441 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1443 struct kauth_identity
*kip
;
1445 KAUTH_IDENTITY_LOCK();
1446 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1447 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1448 kauth_identity_lru(kip
);
1449 /* Copy via structure assignment */
1451 /* If a name is wanted and one exists, copy it out */
1452 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1453 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1457 KAUTH_IDENTITY_UNLOCK();
1458 return((kip
== NULL
) ? ENOENT
: 0);
1463 * kauth_identity_find_guid
1465 * Description: Search for an entry by GUID
1467 * Parameters: guidp Pointer to GUID to find
1468 * kir Pointer to return area
1469 * getname Name buffer, if ki_name wanted
1475 * *klr Modified, if found
1477 * Note: The association may be expired, in which case the caller
1478 * may elect to call out to userland to revalidate.
1481 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1483 struct kauth_identity
*kip
;
1485 KAUTH_IDENTITY_LOCK();
1486 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1487 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1488 kauth_identity_lru(kip
);
1489 /* Copy via structure assignment */
1491 /* If a name is wanted and one exists, copy it out */
1492 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1493 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1497 KAUTH_IDENTITY_UNLOCK();
1498 return((kip
== NULL
) ? ENOENT
: 0);
1502 * kauth_identity_find_nam
1504 * Description: Search for an entry by name
1506 * Parameters: name Pointer to name to find
1507 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1508 * kir Pointer to return aread
1514 * *klr Modified, if found
1517 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1519 struct kauth_identity
*kip
;
1521 KAUTH_IDENTITY_LOCK();
1522 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1523 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1524 kauth_identity_lru(kip
);
1525 /* Copy via structure assignment */
1530 KAUTH_IDENTITY_UNLOCK();
1531 return((kip
== NULL
) ? ENOENT
: 0);
1536 * kauth_identity_find_ntsid
1538 * Description: Search for an entry by NTSID
1540 * Parameters: ntsid Pointer to NTSID to find
1541 * kir Pointer to return area
1542 * getname Name buffer, if ki_name wanted
1548 * *klr Modified, if found
1550 * Note: The association may be expired, in which case the caller
1551 * may elect to call out to userland to revalidate.
1554 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1556 struct kauth_identity
*kip
;
1558 KAUTH_IDENTITY_LOCK();
1559 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1560 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1561 kauth_identity_lru(kip
);
1562 /* Copy via structure assignment */
1564 /* If a name is wanted and one exists, copy it out */
1565 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1566 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1570 KAUTH_IDENTITY_UNLOCK();
1571 return((kip
== NULL
) ? ENOENT
: 0);
1578 guid_t kauth_null_guid
;
1584 * Description: Determine the equality of two GUIDs
1586 * Parameters: guid1 Pointer to first GUID
1587 * guid2 Pointer to second GUID
1589 * Returns: 0 If GUIDs are inequal
1590 * !0 If GUIDs are equal
1593 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1595 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1600 * kauth_wellknown_guid
1602 * Description: Determine if a GUID is a well-known GUID
1604 * Parameters: guid Pointer to GUID to check
1606 * Returns: KAUTH_WKG_NOT Not a wel known GUID
1607 * KAUTH_WKG_EVERYBODY "Everybody"
1608 * KAUTH_WKG_NOBODY "Nobody"
1609 * KAUTH_WKG_OWNER "Other"
1610 * KAUTH_WKG_GROUP "Group"
1613 kauth_wellknown_guid(guid_t
*guid
)
1615 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1618 * All WKGs begin with the same 12 bytes.
1620 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1622 * The final 4 bytes are our code (in network byte order).
1624 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1627 return(KAUTH_WKG_EVERYBODY
);
1629 return(KAUTH_WKG_NOBODY
);
1631 return(KAUTH_WKG_OWNER
);
1633 return(KAUTH_WKG_GROUP
);
1636 return(KAUTH_WKG_NOT
);
1643 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1645 * Paramters: sid1 Pointer to first NTSID
1646 * sid2 Pointer to second NTSID
1648 * Returns: 0 If GUIDs are inequal
1649 * !0 If GUIDs are equal
1652 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1654 /* check sizes for equality, also sanity-check size while we're at it */
1655 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1656 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1657 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1666 * We support four tokens representing identity:
1667 * - Credential reference
1670 * - NT security identifier
1672 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1676 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
1680 * kauth_cred_change_egid
1682 * Description: Set EGID by changing the first element of cr_groups for the
1683 * passed credential; if the new EGID exists in the list of
1684 * groups already, then rotate the old EGID into its position,
1685 * otherwise replace it
1687 * Parameters: cred Pointer to the credential to modify
1688 * new_egid The new EGID to set
1690 * Returns: 0 The egid did not displace a member of
1691 * the supplementary group list
1692 * 1 The egid being set displaced a member
1693 * of the supplementary groups list
1695 * Note: Utility function; internal use only because of locking.
1697 * This function operates on the credential passed; the caller
1698 * must operate either on a newly allocated credential (one for
1699 * which there is no hash cache reference and no externally
1700 * visible pointer reference), or a template credential.
1703 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1709 #endif /* radar_4600026 */
1710 gid_t old_egid
= kauth_cred_getgid(cred
);
1711 posix_cred_t pcred
= posix_cred_get(cred
);
1713 /* Ignoring the first entry, scan for a match for the new egid */
1714 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1716 * If we find a match, swap them so we don't lose overall
1719 if (pcred
->cr_groups
[i
] == new_egid
) {
1720 pcred
->cr_groups
[i
] = old_egid
;
1721 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1728 #error Fix radar 4600026 first!!!
1731 This is correct for memberd behaviour, but incorrect for POSIX; to address
1732 this, we would need to automatically opt-out any SUID/SGID binary, and force
1733 it to use initgroups to opt back in. We take the approach of considering it
1734 opt'ed out in any group of 16 displacement instead, since it's a much more
1735 conservative approach (i.e. less likely to cause things to break).
1739 * If we displaced a member of the supplementary groups list of the
1740 * credential, and we have not opted out of memberd, then if memberd
1741 * says that the credential is a member of the group, then it has not
1742 * actually been displaced.
1744 * NB: This is typically a cold code path.
1746 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1747 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1750 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1752 #endif /* radar_4600026 */
1754 /* set the new EGID into the old spot */
1755 pcred
->cr_groups
[0] = new_egid
;
1764 * Description: Fetch UID from credential
1766 * Parameters: cred Credential to examine
1768 * Returns: (uid_t) UID associated with credential
1771 kauth_cred_getuid(kauth_cred_t cred
)
1773 NULLCRED_CHECK(cred
);
1774 return(posix_cred_get(cred
)->cr_uid
);
1779 * kauth_cred_getruid
1781 * Description: Fetch RUID from credential
1783 * Parameters: cred Credential to examine
1785 * Returns: (uid_t) RUID associated with credential
1788 kauth_cred_getruid(kauth_cred_t cred
)
1790 NULLCRED_CHECK(cred
);
1791 return(posix_cred_get(cred
)->cr_ruid
);
1796 * kauth_cred_getsvuid
1798 * Description: Fetch SVUID from credential
1800 * Parameters: cred Credential to examine
1802 * Returns: (uid_t) SVUID associated with credential
1805 kauth_cred_getsvuid(kauth_cred_t cred
)
1807 NULLCRED_CHECK(cred
);
1808 return(posix_cred_get(cred
)->cr_svuid
);
1815 * Description: Fetch GID from credential
1817 * Parameters: cred Credential to examine
1819 * Returns: (gid_t) GID associated with credential
1822 kauth_cred_getgid(kauth_cred_t cred
)
1824 NULLCRED_CHECK(cred
);
1825 return(posix_cred_get(cred
)->cr_gid
);
1830 * kauth_cred_getrgid
1832 * Description: Fetch RGID from credential
1834 * Parameters: cred Credential to examine
1836 * Returns: (gid_t) RGID associated with credential
1839 kauth_cred_getrgid(kauth_cred_t cred
)
1841 NULLCRED_CHECK(cred
);
1842 return(posix_cred_get(cred
)->cr_rgid
);
1847 * kauth_cred_getsvgid
1849 * Description: Fetch SVGID from credential
1851 * Parameters: cred Credential to examine
1853 * Returns: (gid_t) SVGID associated with credential
1856 kauth_cred_getsvgid(kauth_cred_t cred
)
1858 NULLCRED_CHECK(cred
);
1859 return(posix_cred_get(cred
)->cr_svgid
);
1864 * kauth_cred_guid2pwnam
1866 * Description: Fetch PWNAM from GUID
1868 * Parameters: guidp Pointer to GUID to examine
1869 * pwnam Pointer to user@domain buffer
1871 * Returns: 0 Success
1872 * kauth_cred_cache_lookup:EINVAL
1875 * *pwnam Modified, if successful
1877 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
1880 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
1882 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
1887 * kauth_cred_guid2grnam
1889 * Description: Fetch GRNAM from GUID
1891 * Parameters: guidp Pointer to GUID to examine
1892 * grnam Pointer to group@domain buffer
1894 * Returns: 0 Success
1895 * kauth_cred_cache_lookup:EINVAL
1898 * *grnam Modified, if successful
1900 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
1903 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
1905 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
1910 * kauth_cred_pwnam2guid
1912 * Description: Fetch PWNAM from GUID
1914 * Parameters: pwnam String containing user@domain
1915 * guidp Pointer to buffer for GUID
1917 * Returns: 0 Success
1918 * kauth_cred_cache_lookup:EINVAL
1921 * *guidp Modified, if successful
1923 * Notes: pwnam should not point to a request larger than MAXPATHLEN
1924 * bytes in size, including the NUL termination of the string.
1927 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
1929 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
1934 * kauth_cred_grnam2guid
1936 * Description: Fetch GRNAM from GUID
1938 * Parameters: grnam String containing group@domain
1939 * guidp Pointer to buffer for GUID
1941 * Returns: 0 Success
1942 * kauth_cred_cache_lookup:EINVAL
1945 * *guidp Modified, if successful
1947 * Notes: grnam should not point to a request larger than MAXPATHLEN
1948 * bytes in size, including the NUL termination of the string.
1951 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
1953 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
1958 * kauth_cred_guid2uid
1960 * Description: Fetch UID from GUID
1962 * Parameters: guidp Pointer to GUID to examine
1963 * uidp Pointer to buffer for UID
1965 * Returns: 0 Success
1966 * kauth_cred_cache_lookup:EINVAL
1969 * *uidp Modified, if successful
1972 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
1974 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
1979 * kauth_cred_guid2gid
1981 * Description: Fetch GID from GUID
1983 * Parameters: guidp Pointer to GUID to examine
1984 * gidp Pointer to buffer for GID
1986 * Returns: 0 Success
1987 * kauth_cred_cache_lookup:EINVAL
1990 * *gidp Modified, if successful
1993 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
1995 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2000 * kauth_cred_ntsid2uid
2002 * Description: Fetch UID from NTSID
2004 * Parameters: sidp Pointer to NTSID to examine
2005 * uidp Pointer to buffer for UID
2007 * Returns: 0 Success
2008 * kauth_cred_cache_lookup:EINVAL
2011 * *uidp Modified, if successful
2014 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2016 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2021 * kauth_cred_ntsid2gid
2023 * Description: Fetch GID from NTSID
2025 * Parameters: sidp Pointer to NTSID to examine
2026 * gidp Pointer to buffer for GID
2028 * Returns: 0 Success
2029 * kauth_cred_cache_lookup:EINVAL
2032 * *gidp Modified, if successful
2035 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2037 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2042 * kauth_cred_ntsid2guid
2044 * Description: Fetch GUID from NTSID
2046 * Parameters: sidp Pointer to NTSID to examine
2047 * guidp Pointer to buffer for GUID
2049 * Returns: 0 Success
2050 * kauth_cred_cache_lookup:EINVAL
2053 * *guidp Modified, if successful
2056 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2058 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2063 * kauth_cred_uid2guid
2065 * Description: Fetch GUID from UID
2067 * Parameters: uid UID to examine
2068 * guidp Pointer to buffer for GUID
2070 * Returns: 0 Success
2071 * kauth_cred_cache_lookup:EINVAL
2074 * *guidp Modified, if successful
2077 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2079 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2084 * kauth_cred_getguid
2086 * Description: Fetch GUID from credential
2088 * Parameters: cred Credential to examine
2089 * guidp Pointer to buffer for GUID
2091 * Returns: 0 Success
2092 * kauth_cred_cache_lookup:EINVAL
2095 * *guidp Modified, if successful
2098 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2100 NULLCRED_CHECK(cred
);
2101 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2106 * kauth_cred_getguid
2108 * Description: Fetch GUID from GID
2110 * Parameters: gid GID to examine
2111 * guidp Pointer to buffer for GUID
2113 * Returns: 0 Success
2114 * kauth_cred_cache_lookup:EINVAL
2117 * *guidp Modified, if successful
2120 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2122 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2127 * kauth_cred_uid2ntsid
2129 * Description: Fetch NTSID from UID
2131 * Parameters: uid UID to examine
2132 * sidp Pointer to buffer for NTSID
2134 * Returns: 0 Success
2135 * kauth_cred_cache_lookup:EINVAL
2138 * *sidp Modified, if successful
2141 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2143 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2148 * kauth_cred_getntsid
2150 * Description: Fetch NTSID from credential
2152 * Parameters: cred Credential to examine
2153 * sidp Pointer to buffer for NTSID
2155 * Returns: 0 Success
2156 * kauth_cred_cache_lookup:EINVAL
2159 * *sidp Modified, if successful
2162 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2164 NULLCRED_CHECK(cred
);
2165 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2170 * kauth_cred_gid2ntsid
2172 * Description: Fetch NTSID from GID
2174 * Parameters: gid GID to examine
2175 * sidp Pointer to buffer for NTSID
2177 * Returns: 0 Success
2178 * kauth_cred_cache_lookup:EINVAL
2181 * *sidp Modified, if successful
2184 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2186 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2191 * kauth_cred_guid2ntsid
2193 * Description: Fetch NTSID from GUID
2195 * Parameters: guidp Pointer to GUID to examine
2196 * sidp Pointer to buffer for NTSID
2198 * Returns: 0 Success
2199 * kauth_cred_cache_lookup:EINVAL
2202 * *sidp Modified, if successful
2205 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2207 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2212 * kauth_cred_cache_lookup
2214 * Description: Lookup a translation in the cache; if one is not found, and
2215 * the attempt was not fatal, submit the request to the resolver
2216 * instead, and wait for it to complete or be aborted.
2218 * Parameters: from Identity information we have
2219 * to Identity information we want
2220 * src Pointer to buffer containing
2221 * the source identity
2222 * dst Pointer to buffer to receive
2223 * the target identity
2225 * Returns: 0 Success
2226 * EINVAL Unknown source identity type
2229 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2231 struct kauth_identity ki
;
2232 struct kauth_identity_extlookup el
;
2234 uint64_t extend_data
= 0ULL;
2235 int (* expired
)(struct kauth_identity
*kip
);
2236 char *namebuf
= NULL
;
2238 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2241 * Look for an existing cache entry for this association.
2242 * If the entry has not expired, return the cached information.
2243 * We do not cache user@domain translations here; they use too
2244 * much memory to hold onto forever, and can not be updated
2247 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2253 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2256 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2259 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2261 case KI_VALID_NTSID
:
2262 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2264 case KI_VALID_PWNAM
:
2265 case KI_VALID_GRNAM
:
2266 /* Names are unique in their 'from' space */
2267 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2272 /* lookup failure or error */
2274 /* any other error is fatal */
2275 if (error
!= ENOENT
) {
2276 /* XXX bogus check - this is not possible */
2277 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2281 /* found a valid cached entry, check expiry */
2284 expired
= kauth_identity_guid_expired
;
2286 case KI_VALID_NTSID
:
2287 expired
= kauth_identity_ntsid_expired
;
2292 expired
= kauth_identity_guid_expired
;
2294 case KI_VALID_NTSID
:
2295 expired
= kauth_identity_ntsid_expired
;
2303 * If no expiry function, or not expired, we have found
2307 if (!expired(&ki
)) {
2308 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2309 expired
= NULL
; /* must clear it is used as a flag */
2312 * We leave ki_valid set here; it contains a
2313 * translation but the TTL has expired. If we can't
2314 * get a result from the resolver, we will use it as
2315 * a better-than nothing alternative.
2318 KAUTH_DEBUG("CACHE - expired entry found");
2321 KAUTH_DEBUG("CACHE - no expiry function");
2325 /* do we have a translation? */
2326 if (ki
.ki_valid
& to
) {
2327 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2331 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2332 * If we went looking for a translation from GUID or NTSID and
2333 * found a translation that wasn't for our desired type, then
2334 * don't bother calling the resolver. We know that this
2335 * GUID/NTSID can't translate to our desired type.
2339 case KI_VALID_NTSID
:
2342 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2343 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2348 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2349 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2361 * We failed to find a cache entry; call the resolver.
2363 * Note: We ask for as much non-extended data as we can get,
2364 * and only provide (or ask for) extended information if
2365 * we have a 'from' (or 'to') which requires it. This
2366 * way we don't pay for the extra transfer overhead for
2367 * data we don't need.
2369 bzero(&el
, sizeof(el
));
2370 el
.el_info_pid
= current_proc()->p_pid
;
2373 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2374 el
.el_uid
= *(uid_t
*)src
;
2377 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2378 el
.el_gid
= *(gid_t
*)src
;
2381 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2382 el
.el_uguid
= *(guid_t
*)src
;
2383 el
.el_gguid
= *(guid_t
*)src
;
2385 case KI_VALID_NTSID
:
2386 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2387 el
.el_usid
= *(ntsid_t
*)src
;
2388 el
.el_gsid
= *(ntsid_t
*)src
;
2390 case KI_VALID_PWNAM
:
2391 /* extra overhead */
2392 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2393 extend_data
= CAST_USER_ADDR_T(src
);
2395 case KI_VALID_GRNAM
:
2396 /* extra overhead */
2397 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2398 extend_data
= CAST_USER_ADDR_T(src
);
2404 * Here we ask for everything all at once, to avoid having to work
2405 * out what we really want now, or might want soon.
2407 * Asking for SID translations when we don't know we need them right
2408 * now is going to cause excess work to be done if we're connected
2409 * to a network that thinks it can translate them. This list needs
2410 * to get smaller/smarter.
2412 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2413 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2414 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2415 if (to
== KI_VALID_PWNAM
) {
2416 /* extra overhead */
2417 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2418 extend_data
= CAST_USER_ADDR_T(dst
);
2420 if (to
== KI_VALID_GRNAM
) {
2421 /* extra overhead */
2422 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2423 extend_data
= CAST_USER_ADDR_T(dst
);
2427 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2428 error
= kauth_resolver_submit(&el
, extend_data
);
2429 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2431 /* was the external lookup successful? */
2434 * Save the results from the lookup - we may have other
2435 * information, even if we didn't get a guid or the
2438 * If we came from a name, we know the extend_data is valid.
2440 if (from
== KI_VALID_PWNAM
)
2441 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2442 else if (from
== KI_VALID_GRNAM
)
2443 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2445 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2448 * Check to see if we have a valid cache entry
2449 * originating from the result.
2451 if (!(ki
.ki_valid
& to
)) {
2459 * Copy from the appropriate struct kauth_identity cache entry
2460 * structure into the destination buffer area.
2464 *(uid_t
*)dst
= ki
.ki_uid
;
2467 *(gid_t
*)dst
= ki
.ki_gid
;
2470 *(guid_t
*)dst
= ki
.ki_guid
;
2472 case KI_VALID_NTSID
:
2473 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2475 case KI_VALID_PWNAM
:
2476 case KI_VALID_GRNAM
:
2477 /* handled in kauth_resolver_complete() */
2482 KAUTH_DEBUG("CACHE - returned successfully");
2488 * Group membership cache.
2490 * XXX the linked-list implementation here needs to be optimized.
2493 struct kauth_group_membership
{
2494 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
2495 uid_t gm_uid
; /* the identity whose membership we're recording */
2496 gid_t gm_gid
; /* group of which they are a member */
2497 time_t gm_expiry
; /* TTL for the membership */
2499 #define KAUTH_GROUP_ISMEMBER (1<<0)
2502 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
2503 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
2504 static int kauth_groups_count
;
2506 static lck_mtx_t
*kauth_groups_mtx
;
2507 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
2508 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
2510 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
2511 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
2512 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
2518 * Description: Initialize the groups cache
2520 * Parameters: (void)
2524 * Notes: Intialize the groups cache for use; the group cache is used
2525 * to avoid unnecessary calls out to user space.
2527 * This function is called from kauth_init() in the file
2528 * kern_authorization.c.
2531 kauth_groups_init(void)
2533 TAILQ_INIT(&kauth_groups
);
2534 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2539 * kauth_groups_expired
2541 * Description: Handle lazy expiration of group membership cache entries
2543 * Parameters: gm group membership entry to
2544 * check for expiration
2546 * Returns: 1 Expired
2550 kauth_groups_expired(struct kauth_group_membership
*gm
)
2555 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2562 * Description: Promote the entry to the head of the LRU, assumes the cache
2565 * Parameters: kip group membership entry to move
2566 * to the head of the LRU list,
2567 * if it's not already there
2571 * Notes: This is called even if the entry has expired; typically an
2572 * expired entry that's been looked up is about to be revalidated,
2573 * and having it closer to the head of the LRU means finding it
2574 * quickly again when the revalidation comes through.
2577 kauth_groups_lru(struct kauth_group_membership
*gm
)
2579 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2580 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2581 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2587 * kauth_groups_updatecache
2589 * Description: Given a lookup result, add any group cache associations that
2590 * we don't currently have.
2592 * Parameters: elp External lookup result from
2593 * user space daemon to kernel
2594 * rkip pointer to returned kauth
2600 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2602 struct kauth_group_membership
*gm
;
2605 /* need a valid response if we are to cache anything */
2607 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2608 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2614 * Search for an existing record for this association before inserting
2615 * a new one; if we find one, update it instead of creating a new one
2617 KAUTH_GROUPS_LOCK();
2618 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2619 if ((el
->el_uid
== gm
->gm_uid
) &&
2620 (el
->el_gid
== gm
->gm_gid
)) {
2621 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2622 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2624 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2626 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2627 kauth_groups_lru(gm
);
2631 KAUTH_GROUPS_UNLOCK();
2633 /* if we found an entry to update, stop here */
2637 /* allocate a new record */
2638 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2640 gm
->gm_uid
= el
->el_uid
;
2641 gm
->gm_gid
= el
->el_gid
;
2642 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2643 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2645 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2647 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2651 * Insert the new entry. Note that it's possible to race ourselves
2652 * here and end up with duplicate entries in the list. Wasteful, but
2653 * harmless since the first into the list will never be looked up,
2654 * and thus will eventually just fall off the end.
2656 KAUTH_GROUPS_LOCK();
2657 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2658 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
2659 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2660 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2661 kauth_groups_count
--;
2665 KAUTH_GROUPS_UNLOCK();
2667 /* free expired cache entry */
2674 * Group membership KPI
2678 * kauth_cred_ismember_gid
2680 * Description: Given a credential and a GID, determine if the GID is a member
2681 * of one of the supplementary groups associated with the given
2684 * Parameters: cred Credential to check in
2685 * gid GID to check for membership
2686 * resultp Pointer to int to contain the
2687 * result of the call
2689 * Returns: 0 Success
2690 * ENOENT Could not proform lookup
2691 * kauth_resolver_submit:EWOULDBLOCK
2692 * kauth_resolver_submit:EINTR
2693 * kauth_resolver_submit:ENOMEM
2694 * kauth_resolver_submit:ENOENT User space daemon did not vend
2696 * kauth_resolver_submit:??? Unlikely error from user space
2699 * *resultp (modified) 1 Is member
2702 * Notes: This function guarantees not to modify resultp when returning
2705 * This function effectively checkes the EGID as well, since the
2706 * EGID is cr_groups[0] as an implementation detail.
2709 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2711 posix_cred_t pcred
= posix_cred_get(cred
);
2712 struct kauth_group_membership
*gm
;
2713 struct kauth_identity_extlookup el
;
2717 * Check the per-credential list of override groups.
2719 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2720 * the cache should be used for that case.
2722 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
2723 if (gid
== pcred
->cr_groups
[i
]) {
2730 * If we don't have a UID for group membership checks, the in-cred list
2731 * was authoritative and we can stop here.
2733 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
2740 * If the resolver hasn't checked in yet, we are early in the boot
2741 * phase and the local group list is complete and authoritative.
2743 if (!kauth_resolver_registered
) {
2749 /* XXX check supplementary groups */
2750 /* XXX check whiteout groups */
2751 /* XXX nesting of supplementary/whiteout groups? */
2754 * Check the group cache.
2756 KAUTH_GROUPS_LOCK();
2757 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2758 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2759 kauth_groups_lru(gm
);
2764 /* did we find a membership entry? */
2766 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2767 KAUTH_GROUPS_UNLOCK();
2769 /* if we did, we can return now */
2773 /* nothing in the cache, need to go to userland */
2774 bzero(&el
, sizeof(el
));
2775 el
.el_info_pid
= current_proc()->p_pid
;
2776 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2777 el
.el_uid
= pcred
->cr_gmuid
;
2779 el
.el_member_valid
= 0; /* XXX set by resolver? */
2780 error
= kauth_resolver_submit(&el
, 0ULL);
2783 /* save the results from the lookup */
2784 kauth_groups_updatecache(&el
);
2786 /* if we successfully ascertained membership, report */
2787 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2788 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2797 * kauth_cred_ismember_guid
2799 * Description: Determine whether the supplied credential is a member of the
2800 * group nominated by GUID.
2802 * Parameters: cred Credential to check in
2803 * guidp Pointer to GUID whose group
2804 * we are testing for membership
2805 * resultp Pointer to int to contain the
2806 * result of the call
2808 * Returns: 0 Success
2809 * kauth_cred_guid2gid:EINVAL
2810 * kauth_cred_ismember_gid:ENOENT
2811 * kauth_resolver_submit:ENOENT User space daemon did not vend
2813 * kauth_cred_ismember_gid:EWOULDBLOCK
2814 * kauth_cred_ismember_gid:EINTR
2815 * kauth_cred_ismember_gid:ENOMEM
2816 * kauth_cred_ismember_gid:??? Unlikely error from user space
2819 * *resultp (modified) 1 Is member
2823 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2825 struct kauth_identity ki
;
2830 wkg
= kauth_wellknown_guid(guidp
);
2832 case KAUTH_WKG_NOBODY
:
2835 case KAUTH_WKG_EVERYBODY
:
2841 * Grovel the identity cache looking for this GUID.
2842 * If we find it, and it is for a user record, return
2843 * false because it's not a group.
2845 * This is necessary because we don't have -ve caching
2846 * of group memberships, and we really want to avoid
2847 * calling out to the resolver if at all possible.
2849 * Because we're called by the ACL evaluator, and the
2850 * ACL evaluator is likely to encounter ACEs for users,
2851 * this is expected to be a common case.
2854 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
2855 !kauth_identity_guid_expired(&ki
)) {
2856 if (ki
.ki_valid
& KI_VALID_GID
) {
2857 /* It's a group after all... */
2861 if (ki
.ki_valid
& KI_VALID_UID
) {
2866 #endif /* 6603280 */
2868 * Attempt to translate the GUID to a GID. Even if
2869 * this fails, we will have primed the cache if it is
2870 * a user record and we'll see it above the next time
2873 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2875 * If we have no guid -> gid translation, it's not a group and
2876 * thus the cred can't be a member.
2878 if (error
== ENOENT
) {
2884 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
2891 * kauth_cred_gid_subset
2893 * Description: Given two credentials, determine if all GIDs associated with
2894 * the first are also associated with the second
2896 * Parameters: cred1 Credential to check for
2897 * cred2 Credential to check in
2898 * resultp Pointer to int to contain the
2899 * result of the call
2901 * Returns: 0 Success
2902 * non-zero See kauth_cred_ismember_gid for
2906 * *resultp (modified) 1 Is subset
2909 * Notes: This function guarantees not to modify resultp when returning
2913 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
2915 int i
, err
, res
= 1;
2917 posix_cred_t pcred1
= posix_cred_get(cred1
);
2918 posix_cred_t pcred2
= posix_cred_get(cred2
);
2920 /* First, check the local list of groups */
2921 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
2922 gid
= pcred1
->cr_groups
[i
];
2923 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
2927 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
2933 /* Check real gid */
2934 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
2938 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
2939 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
2944 /* Finally, check saved gid */
2945 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
2949 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
2950 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
2961 * kauth_cred_issuser
2963 * Description: Fast replacement for issuser()
2965 * Parameters: cred Credential to check for super
2968 * Returns: 0 Not super user
2971 * Notes: This function uses a magic number which is not a manifest
2972 * constant; this is bad practice.
2975 kauth_cred_issuser(kauth_cred_t cred
)
2977 return(kauth_cred_getuid(cred
) == 0);
2985 /* lock protecting credential hash table */
2986 static lck_mtx_t
*kauth_cred_hash_mtx
;
2987 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2988 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2989 #if KAUTH_CRED_HASH_DEBUG
2990 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2991 #else /* !KAUTH_CRED_HASH_DEBUG */
2992 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2993 #endif /* !KAUTH_CRED_HASH_DEBUG */
2999 * Description: Initialize the credential hash cache
3001 * Parameters: (void)
3005 * Notes: Intialize the credential hash cache for use; the credential
3006 * hash cache is used convert duplicate credentials into a
3007 * single reference counted credential in order to save wired
3008 * kernel memory. In practice, this generally means a desktop
3009 * system runs with a few tens of credentials, instead of one
3010 * per process, one per thread, one per vnode cache entry, and
3011 * so on. This generally results in savings of 200K or more
3012 * (potentially much more on server systems).
3014 * The hash cache internally has a reference on the credential
3015 * for itself as a means of avoiding a reclaim race for a
3016 * credential in the process of having it's last non-hash
3017 * reference released. This would otherwise result in the
3018 * possibility of a freed credential that was still in uses due
3019 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3021 * On final release, the hash reference is droped, and the
3022 * credential is freed back to the system.
3024 * This function is called from kauth_init() in the file
3025 * kern_authorization.c.
3028 kauth_cred_init(void)
3032 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3033 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
3035 /*allocate credential hash table */
3036 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3037 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
3038 M_KAUTH
, M_WAITOK
| M_ZERO
);
3039 if (kauth_cred_table_anchor
== NULL
)
3040 panic("startup: kauth_cred_init");
3041 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
3042 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3050 * Description: Get the current thread's effective UID.
3052 * Parameters: (void)
3054 * Returns: (uid_t) The effective UID of the
3060 return(kauth_cred_getuid(kauth_cred_get()));
3067 * Description: Get the current thread's real UID.
3069 * Parameters: (void)
3071 * Returns: (uid_t) The real UID of the current
3077 return(kauth_cred_getruid(kauth_cred_get()));
3084 * Description: Get the current thread's effective GID.
3086 * Parameters: (void)
3088 * Returns: (gid_t) The effective GID of the
3094 return(kauth_cred_getgid(kauth_cred_get()));
3101 * Description: Get the current thread's real GID.
3103 * Parameters: (void)
3105 * Returns: (gid_t) The real GID of the current
3111 return(kauth_cred_getrgid(kauth_cred_get()));
3118 * Description: Returns a pointer to the current thread's credential
3120 * Parameters: (void)
3122 * Returns: (kauth_cred_t) Pointer to the current thread's
3125 * Notes: This function does not take a reference; because of this, the
3126 * caller MUST NOT do anything that would let the thread's
3127 * credential change while using the returned value, without
3128 * first explicitly taking their own reference.
3130 * If a caller intends to take a reference on the resulting
3131 * credential pointer from calling this function, it is strongly
3132 * recommended that the caller use kauth_cred_get_with_ref()
3133 * instead, to protect against any future changes to the cred
3134 * locking protocols; such changes could otherwise potentially
3135 * introduce race windows in the callers code.
3138 kauth_cred_get(void)
3141 struct uthread
*uthread
;
3143 uthread
= get_bsdthread_info(current_thread());
3145 if (uthread
== NULL
)
3146 panic("thread wants credential but has no BSD thread info");
3148 * We can lazy-bind credentials to threads, as long as their processes
3151 * XXX If we later inline this function, the code in this block
3152 * XXX should probably be called out in a function.
3154 if (uthread
->uu_ucred
== NOCRED
) {
3155 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3156 panic("thread wants credential but has no BSD process");
3157 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3159 return(uthread
->uu_ucred
);
3163 mach_kauth_cred_uthread_update(void)
3168 uthread
= get_bsdthread_info(current_thread());
3169 proc
= current_proc();
3171 kauth_cred_uthread_update(uthread
, proc
);
3175 * kauth_cred_uthread_update
3177 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3178 * late-bind the uthread cred to the proc cred.
3180 * Parameters: uthread_t The uthread to update
3181 * proc_t The process to update to
3185 * Notes: This code is common code called from system call or trap entry
3186 * in the case that the process thread may have been changed
3187 * since the last time the thread entered the kernel. It is
3188 * generally only called with the current uthread and process as
3192 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3194 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3195 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3196 kauth_cred_t old
= uthread
->uu_ucred
;
3197 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3198 if (IS_VALID_CRED(old
))
3199 kauth_cred_unref(&old
);
3205 * kauth_cred_get_with_ref
3207 * Description: Takes a reference on the current thread's credential, and then
3208 * returns a pointer to it to the caller.
3210 * Parameters: (void)
3212 * Returns: (kauth_cred_t) Pointer to the current thread's
3213 * newly referenced credential
3215 * Notes: This function takes a reference on the credential before
3216 * returning it to the caller.
3218 * It is the responsibility of the calling code to release this
3219 * reference when the credential is no longer in use.
3221 * Since the returned reference may be a persistent reference
3222 * (e.g. one cached in another data structure with a lifetime
3223 * longer than the calling function), this release may be delayed
3224 * until such time as the persistent reference is to be destroyed.
3225 * An example of this would be the per vnode credential cache used
3226 * to accelerate lookup operations.
3229 kauth_cred_get_with_ref(void)
3232 struct uthread
*uthread
;
3234 uthread
= get_bsdthread_info(current_thread());
3236 if (uthread
== NULL
)
3237 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3238 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3239 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3242 * We can lazy-bind credentials to threads, as long as their processes
3245 * XXX If we later inline this function, the code in this block
3246 * XXX should probably be called out in a function.
3248 if (uthread
->uu_ucred
== NOCRED
) {
3249 /* take reference for new cred in thread */
3250 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3252 /* take a reference for our caller */
3253 kauth_cred_ref(uthread
->uu_ucred
);
3254 return(uthread
->uu_ucred
);
3259 * kauth_cred_proc_ref
3261 * Description: Takes a reference on the current process's credential, and
3262 * then returns a pointer to it to the caller.
3264 * Parameters: procp Process whose credential we
3265 * intend to take a reference on
3267 * Returns: (kauth_cred_t) Pointer to the process's
3268 * newly referenced credential
3270 * Locks: PROC_LOCK is held before taking the reference and released
3271 * after the refeence is taken to protect the p_ucred field of
3272 * the process referred to by procp.
3274 * Notes: This function takes a reference on the credential before
3275 * returning it to the caller.
3277 * It is the responsibility of the calling code to release this
3278 * reference when the credential is no longer in use.
3280 * Since the returned reference may be a persistent reference
3281 * (e.g. one cached in another data structure with a lifetime
3282 * longer than the calling function), this release may be delayed
3283 * until such time as the persistent reference is to be destroyed.
3284 * An example of this would be the per vnode credential cache used
3285 * to accelerate lookup operations.
3288 kauth_cred_proc_ref(proc_t procp
)
3293 cred
= proc_ucred(procp
);
3294 kauth_cred_ref(cred
);
3303 * Description: Allocate a new credential
3305 * Parameters: (void)
3307 * Returns: !NULL Newly allocated credential
3308 * NULL Insufficient memory
3310 * Notes: The newly allocated credential is zero'ed as part of the
3311 * allocation process, with the exception of the reference
3312 * count, which is set to 1 to indicate a single reference
3313 * held by the caller.
3315 * Since newly allocated credentials have no external pointers
3316 * referencing them, prior to making them visible in an externally
3317 * visible pointer (e.g. by adding them to the credential hash
3318 * cache) is the only legal time in which an existing credential
3319 * can be safely iinitialized or modified directly.
3321 * After initialization, the caller is expected to call the
3322 * function kauth_cred_add() to add the credential to the hash
3323 * cache, after which time it's frozen and becomes publically
3326 * The release protocol depends on kauth_hash_add() being called
3327 * before kauth_cred_rele() (there is a diagnostic panic which
3328 * will trigger if this protocol is not observed).
3330 * XXX: This function really ought to be static, rather than being
3331 * exported as KPI, since a failure of kauth_cred_add() can only
3332 * be handled by an explicit free of the credential; such frees
3333 * depend on knowlegdge of the allocation method used, which is
3334 * permitted to change between kernel revisions.
3336 * XXX: In the insufficient resource case, this code panic's rather
3337 * than returning a NULL pointer; the code that calls this
3338 * function needs to be audited before this can be changed.
3341 kauth_cred_alloc(void)
3343 kauth_cred_t newcred
;
3345 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3347 posix_cred_t newpcred
= posix_cred_get(newcred
);
3348 bzero(newcred
, sizeof(*newcred
));
3349 newcred
->cr_ref
= 1;
3350 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3351 /* must do this, or cred has same group membership as uid 0 */
3352 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3355 panic("kauth_cred_alloc: couldn't allocate credential");
3359 #if KAUTH_CRED_HASH_DEBUG
3364 mac_cred_label_init(newcred
);
3374 * Description: Look to see if we already have a known credential in the hash
3375 * cache; if one is found, bump the reference count and return
3376 * it. If there are no credentials that match the given
3377 * credential, then allocate a new credential.
3379 * Parameters: cred Template for credential to
3382 * Returns: (kauth_cred_t) The credential that was found
3383 * in the hash or created
3384 * NULL kauth_cred_add() failed, or
3385 * there was not an egid specified
3387 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3388 * maintain this field, we can't expect callers to know how it
3389 * needs to be set. Callers should be prepared for this field
3390 * to be overwritten.
3392 * XXX: This code will tight-loop if memory for a new credential is
3393 * persistently unavailable; this is perhaps not the wisest way
3394 * to handle this condition, but current callers do not expect
3398 kauth_cred_create(kauth_cred_t cred
)
3400 kauth_cred_t found_cred
, new_cred
= NULL
;
3401 posix_cred_t pcred
= posix_cred_get(cred
);
3404 KAUTH_CRED_HASH_LOCK_ASSERT();
3406 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3407 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3410 * If the template credential is not opting out of external
3411 * group membership resolution, then we need to check that
3412 * the UID we will be using is resolvable by the external
3413 * resolver. If it's not, then we opt it out anyway, since
3414 * all future external resolution requests will be failing
3415 * anyway, and potentially taking a long time to do it. We
3416 * use gid 0 because we always know it will exist and not
3417 * trigger additional lookups. This is OK, because we end up
3418 * precatching the information here as a result.
3420 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3422 * It's a recognized value; we don't really care about
3423 * the answer, so long as it's something the external
3424 * resolver could have vended.
3426 pcred
->cr_gmuid
= pcred
->cr_uid
;
3429 * It's not something the external resolver could
3430 * have vended, so we don't want to ask it more
3431 * questions about the credential in the future. This
3432 * speeds up future lookups, as long as the caller
3433 * caches results; otherwise, it the same recurring
3434 * cost. Since most credentials are used multiple
3435 * times, we still get some performance win from this.
3437 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3438 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3442 /* Caller *must* specify at least the egid in cr_groups[0] */
3443 if (pcred
->cr_ngroups
< 1)
3447 KAUTH_CRED_HASH_LOCK();
3448 found_cred
= kauth_cred_find(cred
);
3449 if (found_cred
!= NULL
) {
3451 * Found an existing credential so we'll bump
3452 * reference count and return
3454 kauth_cred_ref(found_cred
);
3455 KAUTH_CRED_HASH_UNLOCK();
3458 KAUTH_CRED_HASH_UNLOCK();
3461 * No existing credential found. Create one and add it to
3464 new_cred
= kauth_cred_alloc();
3465 if (new_cred
!= NULL
) {
3467 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3468 new_pcred
->cr_uid
= pcred
->cr_uid
;
3469 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3470 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3471 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3472 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3473 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3474 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3475 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3477 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3478 sizeof(new_cred
->cr_audit
));
3480 new_pcred
->cr_flags
= pcred
->cr_flags
;
3482 KAUTH_CRED_HASH_LOCK();
3483 err
= kauth_cred_add(new_cred
);
3484 KAUTH_CRED_HASH_UNLOCK();
3486 /* Retry if kauth_cred_add returns non zero value */
3490 mac_cred_label_destroy(new_cred
);
3492 AUDIT_SESSION_UNREF(new_cred
);
3494 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3504 * kauth_cred_setresuid
3506 * Description: Update the given credential using the UID arguments. The given
3507 * UIDs are used to set the effective UID, real UID, saved UID,
3508 * and GMUID (used for group membership checking).
3510 * Parameters: cred The original credential
3511 * ruid The new real UID
3512 * euid The new effective UID
3513 * svuid The new saved UID
3514 * gmuid KAUTH_UID_NONE -or- the new
3515 * group membership UID
3517 * Returns: (kauth_cred_t) The updated credential
3519 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3520 * setting, so if you don't want it to change, pass it the
3521 * previous value, explicitly.
3523 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3524 * if it returns a credential other than the one it is passed,
3525 * will have dropped the reference on the passed credential. All
3526 * callers should be aware of this, and treat this function as an
3527 * unref + ref, potentially on different credentials.
3529 * Because of this, the caller is expected to take its own
3530 * reference on the credential passed as the first parameter,
3531 * and be prepared to release the reference on the credential
3532 * that is returned to them, if it is not intended to be a
3533 * persistent reference.
3536 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3538 struct ucred temp_cred
;
3539 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3540 posix_cred_t pcred
= posix_cred_get(cred
);
3542 NULLCRED_CHECK(cred
);
3545 * We don't need to do anything if the UIDs we are changing are
3546 * already the same as the UIDs passed in
3548 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3549 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3550 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3551 (pcred
->cr_gmuid
== gmuid
)) {
3552 /* no change needed */
3557 * Look up in cred hash table to see if we have a matching credential
3558 * with the new values; this is done by calling kauth_cred_update().
3560 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3561 if (euid
!= KAUTH_UID_NONE
) {
3562 temp_pcred
->cr_uid
= euid
;
3564 if (ruid
!= KAUTH_UID_NONE
) {
3565 temp_pcred
->cr_ruid
= ruid
;
3567 if (svuid
!= KAUTH_UID_NONE
) {
3568 temp_pcred
->cr_svuid
= svuid
;
3572 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3573 * opt out of participation in external group resolution, unless we
3574 * unless we explicitly opt back in later.
3576 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3577 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3580 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3585 * kauth_cred_setresgid
3587 * Description: Update the given credential using the GID arguments. The given
3588 * GIDs are used to set the effective GID, real GID, and saved
3591 * Parameters: cred The original credential
3592 * rgid The new real GID
3593 * egid The new effective GID
3594 * svgid The new saved GID
3596 * Returns: (kauth_cred_t) The updated credential
3598 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3599 * if it returns a credential other than the one it is passed,
3600 * will have dropped the reference on the passed credential. All
3601 * callers should be aware of this, and treat this function as an
3602 * unref + ref, potentially on different credentials.
3604 * Because of this, the caller is expected to take its own
3605 * reference on the credential passed as the first parameter,
3606 * and be prepared to release the reference on the credential
3607 * that is returned to them, if it is not intended to be a
3608 * persistent reference.
3611 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3613 struct ucred temp_cred
;
3614 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3615 posix_cred_t pcred
= posix_cred_get(cred
);
3617 NULLCRED_CHECK(cred
);
3618 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3621 * We don't need to do anything if the given GID are already the
3622 * same as the GIDs in the credential.
3624 if (pcred
->cr_groups
[0] == egid
&&
3625 pcred
->cr_rgid
== rgid
&&
3626 pcred
->cr_svgid
== svgid
) {
3627 /* no change needed */
3632 * Look up in cred hash table to see if we have a matching credential
3633 * with the new values; this is done by calling kauth_cred_update().
3635 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3636 if (egid
!= KAUTH_GID_NONE
) {
3637 /* displacing a supplementary group opts us out of memberd */
3638 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3639 DEBUG_CRED_CHANGE("displaced!\n");
3640 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3641 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3643 DEBUG_CRED_CHANGE("not displaced\n");
3646 if (rgid
!= KAUTH_GID_NONE
) {
3647 temp_pcred
->cr_rgid
= rgid
;
3649 if (svgid
!= KAUTH_GID_NONE
) {
3650 temp_pcred
->cr_svgid
= svgid
;
3653 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3658 * Update the given credential with the given groups. We only allocate a new
3659 * credential when the given gid actually results in changes to the existing
3661 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3662 * which will be used for group membership checking.
3665 * kauth_cred_setgroups
3667 * Description: Update the given credential using the provide supplementary
3668 * group list and group membership UID
3670 * Parameters: cred The original credential
3671 * groups Pointer to gid_t array which
3672 * contains the new group list
3673 * groupcount The cound of valid groups which
3674 * are contained in 'groups'
3675 * gmuid KAUTH_UID_NONE -or- the new
3676 * group membership UID
3678 * Returns: (kauth_cred_t) The updated credential
3680 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3681 * setting, so if you don't want it to change, pass it the
3682 * previous value, explicitly.
3684 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3685 * if it returns a credential other than the one it is passed,
3686 * will have dropped the reference on the passed credential. All
3687 * callers should be aware of this, and treat this function as an
3688 * unref + ref, potentially on different credentials.
3690 * Because of this, the caller is expected to take its own
3691 * reference on the credential passed as the first parameter,
3692 * and be prepared to release the reference on the credential
3693 * that is returned to them, if it is not intended to be a
3694 * persistent reference.
3696 * XXX: Changes are determined in ordinal order - if the caller pasess
3697 * in the same groups list that is already present in the
3698 * credential, but the members are in a different order, even if
3699 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3700 * is considered a modification to the credential, and a new
3701 * credential is created.
3703 * This should perhaps be better optimized, but it is considered
3704 * to be the caller's problem.
3707 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
3710 struct ucred temp_cred
;
3711 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3714 NULLCRED_CHECK(cred
);
3716 pcred
= posix_cred_get(cred
);
3719 * We don't need to do anything if the given list of groups does not
3722 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
3723 for (i
= 0; i
< groupcount
; i
++) {
3724 if (pcred
->cr_groups
[i
] != groups
[i
])
3727 if (i
== groupcount
) {
3728 /* no change needed */
3734 * Look up in cred hash table to see if we have a matching credential
3735 * with new values. If we are setting or clearing the gmuid, then
3736 * update the cr_flags, since clearing it is sticky. This permits an
3737 * opt-out of memberd processing using setgroups(), and an opt-in
3738 * using initgroups(). This is required for POSIX conformance.
3740 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3741 temp_pcred
->cr_ngroups
= groupcount
;
3742 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
3743 temp_pcred
->cr_gmuid
= gmuid
;
3744 if (gmuid
== KAUTH_UID_NONE
)
3745 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3747 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3749 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3753 * XXX temporary, for NFS support until we can come up with a better
3754 * XXX enumeration/comparison mechanism
3756 * Notes: The return value exists to account for the possbility of a
3757 * kauth_cred_t without a POSIX label. This will be the case in
3758 * the future (see posix_cred_get() below, for more details).
3761 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
3763 int limit
= NGROUPS
;
3766 * If they just want a copy of the groups list, they may not care
3767 * about the actual count. If they specify an input count, however,
3768 * treat it as an indicator of the buffer size available in grouplist,
3769 * and limit the returned list to that size.
3772 limit
= MIN(*countp
, cred
->cr_posix
.cr_ngroups
);
3776 memcpy(grouplist
, cred
->cr_posix
.cr_groups
, sizeof(gid_t
) * limit
);
3783 * kauth_cred_setuidgid
3785 * Description: Update the given credential using the UID and GID arguments.
3786 * The given UID is used to set the effective UID, real UID, and
3787 * saved UID. The given GID is used to set the effective GID,
3788 * real GID, and saved GID.
3790 * Parameters: cred The original credential
3791 * uid The new UID to use
3792 * gid The new GID to use
3794 * Returns: (kauth_cred_t) The updated credential
3796 * Notes: We set the gmuid to uid if the credential we are inheriting
3797 * from has not opted out of memberd participation; otherwise
3798 * we set it to KAUTH_UID_NONE
3800 * This code is only ever called from the per-thread credential
3801 * code path in the "set per thread credential" case; and in
3802 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3805 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3806 * if it returns a credential other than the one it is passed,
3807 * will have dropped the reference on the passed credential. All
3808 * callers should be aware of this, and treat this function as an
3809 * unref + ref, potentially on different credentials.
3811 * Because of this, the caller is expected to take its own
3812 * reference on the credential passed as the first parameter,
3813 * and be prepared to release the reference on the credential
3814 * that is returned to them, if it is not intended to be a
3815 * persistent reference.
3818 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3820 struct ucred temp_cred
;
3821 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3824 NULLCRED_CHECK(cred
);
3826 pcred
= posix_cred_get(cred
);
3829 * We don't need to do anything if the effective, real and saved
3830 * user IDs are already the same as the user ID passed into us.
3832 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
3833 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
3834 /* no change needed */
3839 * Look up in cred hash table to see if we have a matching credential
3840 * with the new values.
3842 bzero(&temp_cred
, sizeof(temp_cred
));
3843 temp_pcred
->cr_uid
= uid
;
3844 temp_pcred
->cr_ruid
= uid
;
3845 temp_pcred
->cr_svuid
= uid
;
3846 temp_pcred
->cr_flags
= pcred
->cr_flags
;
3847 /* inherit the opt-out of memberd */
3848 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3849 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3850 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3852 temp_pcred
->cr_gmuid
= uid
;
3853 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3855 temp_pcred
->cr_ngroups
= 1;
3856 /* displacing a supplementary group opts us out of memberd */
3857 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3858 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3859 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3861 temp_pcred
->cr_rgid
= gid
;
3862 temp_pcred
->cr_svgid
= gid
;
3864 temp_cred
.cr_label
= cred
->cr_label
;
3867 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3872 * kauth_cred_setsvuidgid
3874 * Description: Function used by execve to set the saved uid and gid values
3875 * for suid/sgid programs
3877 * Parameters: cred The credential to update
3878 * uid The saved uid to set
3879 * gid The saved gid to set
3881 * Returns: (kauth_cred_t) The updated credential
3883 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3884 * if it returns a credential other than the one it is passed,
3885 * will have dropped the reference on the passed credential. All
3886 * callers should be aware of this, and treat this function as an
3887 * unref + ref, potentially on different credentials.
3889 * Because of this, the caller is expected to take its own
3890 * reference on the credential passed as the first parameter,
3891 * and be prepared to release the reference on the credential
3892 * that is returned to them, if it is not intended to be a
3893 * persistent reference.
3896 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3898 struct ucred temp_cred
;
3899 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3902 NULLCRED_CHECK(cred
);
3904 pcred
= posix_cred_get(cred
);
3906 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
3909 * We don't need to do anything if the effective, real and saved
3910 * uids are already the same as the uid provided. This check is
3911 * likely insufficient.
3913 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
3914 /* no change needed */
3917 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3919 /* look up in cred hash table to see if we have a matching credential
3922 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3923 temp_pcred
->cr_svuid
= uid
;
3924 temp_pcred
->cr_svgid
= gid
;
3926 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3931 * kauth_cred_setauditinfo
3933 * Description: Update the given credential using the given au_session_t.
3935 * Parameters: cred The original credential
3936 * auditinfo_p Pointer to ne audit information
3938 * Returns: (kauth_cred_t) The updated credential
3940 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3941 * if it returns a credential other than the one it is passed,
3942 * will have dropped the reference on the passed credential. All
3943 * callers should be aware of this, and treat this function as an
3944 * unref + ref, potentially on different credentials.
3946 * Because of this, the caller is expected to take its own
3947 * reference on the credential passed as the first parameter,
3948 * and be prepared to release the reference on the credential
3949 * that is returned to them, if it is not intended to be a
3950 * persistent reference.
3953 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
3955 struct ucred temp_cred
;
3957 NULLCRED_CHECK(cred
);
3960 * We don't need to do anything if the audit info is already the
3961 * same as the audit info in the credential provided.
3963 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
3964 /* no change needed */
3968 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3969 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
3971 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
3976 * kauth_cred_label_update
3978 * Description: Update the MAC label associated with a credential
3980 * Parameters: cred The original credential
3981 * label The MAC label to set
3983 * Returns: (kauth_cred_t) The updated credential
3985 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3986 * if it returns a credential other than the one it is passed,
3987 * will have dropped the reference on the passed credential. All
3988 * callers should be aware of this, and treat this function as an
3989 * unref + ref, potentially on different credentials.
3991 * Because of this, the caller is expected to take its own
3992 * reference on the credential passed as the first parameter,
3993 * and be prepared to release the reference on the credential
3994 * that is returned to them, if it is not intended to be a
3995 * persistent reference.
3998 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4000 kauth_cred_t newcred
;
4001 struct ucred temp_cred
;
4003 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4005 mac_cred_label_init(&temp_cred
);
4006 mac_cred_label_associate(cred
, &temp_cred
);
4007 mac_cred_label_update(&temp_cred
, label
);
4009 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4010 mac_cred_label_destroy(&temp_cred
);
4015 * kauth_cred_label_update_execve
4017 * Description: Update the MAC label associated with a credential as
4020 * Parameters: cred The original credential
4022 * scriptl The script MAC label
4023 * execl The executable MAC label
4024 * disjointp Pointer to flag to set if old
4025 * and returned credentials are
4028 * Returns: (kauth_cred_t) The updated credential
4031 * *disjointp Set to 1 for disjoint creds
4033 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4034 * if it returns a credential other than the one it is passed,
4035 * will have dropped the reference on the passed credential. All
4036 * callers should be aware of this, and treat this function as an
4037 * unref + ref, potentially on different credentials.
4039 * Because of this, the caller is expected to take its own
4040 * reference on the credential passed as the first parameter,
4041 * and be prepared to release the reference on the credential
4042 * that is returned to them, if it is not intended to be a
4043 * persistent reference.
4047 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4048 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
4051 kauth_cred_t newcred
;
4052 struct ucred temp_cred
;
4054 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4056 mac_cred_label_init(&temp_cred
);
4057 mac_cred_label_associate(cred
, &temp_cred
);
4058 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
4059 vp
, scriptl
, execl
);
4061 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4062 mac_cred_label_destroy(&temp_cred
);
4067 * kauth_proc_label_update
4069 * Description: Update the label inside the credential associated with the process.
4071 * Parameters: p The process to modify
4072 * label The label to place in the process credential
4074 * Notes: The credential associated with the process may change as a result
4075 * of this call. The caller should not assume the process reference to
4076 * the old credential still exists.
4078 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4080 kauth_cred_t my_cred
, my_new_cred
;
4082 my_cred
= kauth_cred_proc_ref(p
);
4084 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4086 /* get current credential and take a reference while we muck with it */
4090 * Set the credential with new info. If there is no change,
4091 * we get back the same credential we passed in; if there is
4092 * a change, we drop the reference on the credential we
4093 * passed in. The subsequent compare is safe, because it is
4094 * a pointer compare rather than a contents compare.
4096 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4097 if (my_cred
!= my_new_cred
) {
4099 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
);
4103 * We need to protect for a race where another thread
4104 * also changed the credential after we took our
4105 * reference. If p_ucred has changed then we should
4106 * restart this again with the new cred.
4108 if (p
->p_ucred
!= my_cred
) {
4110 kauth_cred_unref(&my_new_cred
);
4111 my_cred
= kauth_cred_proc_ref(p
);
4115 p
->p_ucred
= my_new_cred
;
4116 /* update cred on proc */
4117 PROC_UPDATE_CREDS_ONPROC(p
);
4119 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4124 /* Drop old proc reference or our extra reference */
4125 kauth_cred_unref(&my_cred
);
4131 * kauth_proc_label_update_execve
4133 * Description: Update the label inside the credential associated with the
4134 * process as part of a transitioning execve. The label will
4135 * be updated by the policies as part of this processing, not
4136 * provided up front.
4138 * Parameters: p The process to modify
4139 * ctx The context of the exec
4140 * vp The vnode being exec'ed
4141 * scriptl The script MAC label
4142 * execl The executable MAC label
4144 * Returns: 0 Label update did not make credential
4146 * 1 Label update caused credential to be
4149 * Notes: The credential associated with the process WILL change as a
4150 * result of this call. The caller should not assume the process
4151 * reference to the old credential still exists.
4154 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4155 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
4157 kauth_cred_t my_cred
, my_new_cred
;
4160 my_cred
= kauth_cred_proc_ref(p
);
4162 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4164 /* get current credential and take a reference while we muck with it */
4168 * Set the credential with new info. If there is no change,
4169 * we get back the same credential we passed in; if there is
4170 * a change, we drop the reference on the credential we
4171 * passed in. The subsequent compare is safe, because it is
4172 * a pointer compare rather than a contents compare.
4174 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
4175 if (my_cred
!= my_new_cred
) {
4177 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
);
4181 * We need to protect for a race where another thread
4182 * also changed the credential after we took our
4183 * reference. If p_ucred has changed then we should
4184 * restart this again with the new cred.
4186 if (p
->p_ucred
!= my_cred
) {
4188 kauth_cred_unref(&my_new_cred
);
4189 my_cred
= kauth_cred_proc_ref(p
);
4193 p
->p_ucred
= my_new_cred
;
4194 /* update cred on proc */
4195 PROC_UPDATE_CREDS_ONPROC(p
);
4196 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4201 /* Drop old proc reference or our extra reference */
4202 kauth_cred_unref(&my_cred
);
4209 * for temporary binary compatibility
4211 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4213 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4215 return kauth_cred_label_update(cred
, label
);
4218 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4220 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4222 return kauth_proc_label_update(p
, label
);
4228 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4229 * Since we cannot build our export lists based on the kernel configuration we need
4233 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4239 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4246 * for temporary binary compatibility
4248 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4250 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4255 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4257 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4267 * Description: Add a reference to the passed credential
4269 * Parameters: cred The credential to reference
4273 * Notes: This function adds a reference to the provided credential;
4274 * the existing reference on the credential is assumed to be
4275 * held stable over this operation by taking the appropriate
4276 * lock to protect the pointer from which it is being referenced,
4277 * if necessary (e.g. the proc lock is held over the call if the
4278 * credential being referenced is from p_ucred, the vnode lock
4279 * if from the per vnode name cache cred cache, and so on).
4281 * This is safe from the kauth_cred_unref() path, since an atomic
4282 * add is used, and the unref path specifically checks to see that
4283 * the value has not been changed to add a reference between the
4284 * time the credential is unreferenced by another pointer and the
4285 * time it is unreferenced from the cred hash cache.
4288 kauth_cred_ref(kauth_cred_t cred
)
4292 NULLCRED_CHECK(cred
);
4294 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4297 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4299 #if 0 // use this to watch a specific credential
4300 if ( is_target_cred( cred
) != 0 ) {
4310 * kauth_cred_unref_hashlocked
4312 * Description: release a credential reference; when the last reference is
4313 * released, the credential will be freed.
4315 * Parameters: credp Pointer to address containing
4316 * credential to be freed
4321 * *credp Set to NOCRED
4323 * Notes: This function assumes the credential hash lock is held.
4325 * This function is internal use only, since the hash lock is
4326 * scoped to this compilation unit.
4328 * This function destroys the contents of the pointer passed by
4329 * the caller to prevent the caller accidently attempting to
4330 * release a given reference twice in error.
4332 * The last reference is considered to be released when a release
4333 * of a credential of a reference count of 2 occurs; this is an
4334 * intended effect, to take into accout the reference held by
4335 * the credential hash, which is released at the same time.
4338 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4342 KAUTH_CRED_HASH_LOCK_ASSERT();
4343 NULLCRED_CHECK(*credp
);
4345 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4349 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4351 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4354 #if 0 // use this to watch a specific credential
4355 if ( is_target_cred( *credp
) != 0 ) {
4361 * If the old_value is 2, then we have just released the last external
4362 * reference to this credential
4364 if (old_value
< 3) {
4365 /* The last absolute reference is our credential hash table */
4366 kauth_cred_remove(*credp
);
4375 * Description: Release a credential reference while holding the credential
4376 * hash lock; when the last reference is released, the credential
4379 * Parameters: credp Pointer to address containing
4380 * credential to be freed
4385 * *credp Set to NOCRED
4387 * Notes: See kauth_cred_unref_hashlocked() for more information.
4391 kauth_cred_unref(kauth_cred_t
*credp
)
4393 KAUTH_CRED_HASH_LOCK();
4394 kauth_cred_unref_hashlocked(credp
);
4395 KAUTH_CRED_HASH_UNLOCK();
4403 * Description: release a credential reference; when the last reference is
4404 * released, the credential will be freed
4406 * Parameters: cred Credential to release
4410 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4411 * clear the pointer in the caller to avoid multiple releases of
4412 * the same credential. The currently recommended interface is
4413 * kauth_cred_unref().
4416 kauth_cred_rele(kauth_cred_t cred
)
4418 kauth_cred_unref(&cred
);
4420 #endif /* !__LP64__ */
4426 * Description: Duplicate a credential via alloc and copy; the new credential
4429 * Parameters: cred The credential to duplicate
4431 * Returns: (kauth_cred_t) The duplicate credential
4433 * Notes: The typical value to calling this routine is if you are going
4434 * to modify an existing credential, and expect to need a new one
4435 * from the hash cache.
4437 * This should probably not be used in the majority of cases;
4438 * if you are using it instead of kauth_cred_create(), you are
4439 * likely making a mistake.
4441 * The newly allocated credential is copied as part of the
4442 * allocation process, with the exception of the reference
4443 * count, which is set to 1 to indicate a single reference
4444 * held by the caller.
4446 * Since newly allocated credentials have no external pointers
4447 * referencing them, prior to making them visible in an externally
4448 * visible pointer (e.g. by adding them to the credential hash
4449 * cache) is the only legal time in which an existing credential
4450 * can be safely iinitialized or modified directly.
4452 * After initialization, the caller is expected to call the
4453 * function kauth_cred_add() to add the credential to the hash
4454 * cache, after which time it's frozen and becomes publically
4457 * The release protocol depends on kauth_hash_add() being called
4458 * before kauth_cred_rele() (there is a diagnostic panic which
4459 * will trigger if this protocol is not observed).
4463 kauth_cred_dup(kauth_cred_t cred
)
4465 kauth_cred_t newcred
;
4467 struct label
*temp_label
;
4471 if (cred
== NOCRED
|| cred
== FSCRED
)
4472 panic("kauth_cred_dup: bad credential");
4474 newcred
= kauth_cred_alloc();
4475 if (newcred
!= NULL
) {
4477 temp_label
= newcred
->cr_label
;
4479 bcopy(cred
, newcred
, sizeof(*newcred
));
4481 newcred
->cr_label
= temp_label
;
4482 mac_cred_label_associate(cred
, newcred
);
4484 AUDIT_SESSION_REF(cred
);
4485 newcred
->cr_ref
= 1;
4491 * kauth_cred_copy_real
4493 * Description: Returns a credential based on the passed credential but which
4494 * reflects the real rather than effective UID and GID.
4496 * Parameters: cred The credential from which to
4497 * derive the new credential
4499 * Returns: (kauth_cred_t) The copied credential
4501 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4502 * result, the caller is responsible for dropping BOTH the
4503 * additional reference on the passed cred (if any), and the
4504 * credential returned by this function. The drop should be
4505 * via the satnadr kauth_cred_unref() KPI.
4508 kauth_cred_copy_real(kauth_cred_t cred
)
4510 kauth_cred_t newcred
= NULL
, found_cred
;
4511 struct ucred temp_cred
;
4512 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4513 posix_cred_t pcred
= posix_cred_get(cred
);
4515 /* if the credential is already 'real', just take a reference */
4516 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4517 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4518 kauth_cred_ref(cred
);
4523 * Look up in cred hash table to see if we have a matching credential
4524 * with the new values.
4526 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4527 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4528 /* displacing a supplementary group opts us out of memberd */
4529 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4530 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4531 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4534 * If the cred is not opted out, make sure we are using the r/euid
4537 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4538 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4543 KAUTH_CRED_HASH_LOCK();
4544 found_cred
= kauth_cred_find(&temp_cred
);
4545 if (found_cred
== cred
) {
4546 /* same cred so just bail */
4547 KAUTH_CRED_HASH_UNLOCK();
4550 if (found_cred
!= NULL
) {
4552 * Found a match so we bump reference count on new
4553 * one. We leave the old one alone.
4555 kauth_cred_ref(found_cred
);
4556 KAUTH_CRED_HASH_UNLOCK();
4561 * Must allocate a new credential, copy in old credential
4562 * data and update the real user and group IDs.
4564 newcred
= kauth_cred_dup(&temp_cred
);
4565 err
= kauth_cred_add(newcred
);
4566 KAUTH_CRED_HASH_UNLOCK();
4568 /* Retry if kauth_cred_add() fails */
4572 mac_cred_label_destroy(newcred
);
4574 AUDIT_SESSION_UNREF(newcred
);
4576 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4587 * Description: Common code to update a credential
4589 * Parameters: old_cred Reference counted credential
4591 * model_cred Non-reference counted model
4592 * credential to apply to the
4593 * credential to be updated
4594 * retain_auditinfo Flag as to whether or not the
4595 * audit information should be
4596 * copied from the old_cred into
4599 * Returns: (kauth_cred_t) The updated credential
4601 * IMPORTANT: This function will potentially return a credential other than
4602 * the one it is passed, and if so, it will have dropped the
4603 * reference on the passed credential. All callers should be
4604 * aware of this, and treat this function as an unref + ref,
4605 * potentially on different credentials.
4607 * Because of this, the caller is expected to take its own
4608 * reference on the credential passed as the first parameter,
4609 * and be prepared to release the reference on the credential
4610 * that is returned to them, if it is not intended to be a
4611 * persistent reference.
4614 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4615 boolean_t retain_auditinfo
)
4617 kauth_cred_t found_cred
, new_cred
= NULL
;
4620 * Make sure we carry the auditinfo forward to the new credential
4621 * unless we are actually updating the auditinfo.
4623 if (retain_auditinfo
) {
4624 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
4625 sizeof(model_cred
->cr_audit
));
4631 KAUTH_CRED_HASH_LOCK();
4632 found_cred
= kauth_cred_find(model_cred
);
4633 if (found_cred
== old_cred
) {
4634 /* same cred so just bail */
4635 KAUTH_CRED_HASH_UNLOCK();
4638 if (found_cred
!= NULL
) {
4639 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
4641 * Found a match so we bump reference count on new
4642 * one and decrement reference count on the old one.
4644 kauth_cred_ref(found_cred
);
4645 kauth_cred_unref_hashlocked(&old_cred
);
4646 KAUTH_CRED_HASH_UNLOCK();
4651 * Must allocate a new credential using the model. also
4652 * adds the new credential to the credential hash table.
4654 new_cred
= kauth_cred_dup(model_cred
);
4655 err
= kauth_cred_add(new_cred
);
4656 KAUTH_CRED_HASH_UNLOCK();
4658 /* retry if kauth_cred_add returns non zero value */
4662 mac_cred_label_destroy(new_cred
);
4664 AUDIT_SESSION_UNREF(new_cred
);
4666 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
4670 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
4671 kauth_cred_unref(&old_cred
);
4679 * Description: Add the given credential to our credential hash table and
4680 * take an additional reference to account for our use of the
4681 * credential in the hash table
4683 * Parameters: new_cred Credential to insert into cred
4686 * Returns: 0 Success
4687 * -1 Hash insertion failed: caller
4690 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4692 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4695 kauth_cred_add(kauth_cred_t new_cred
)
4699 KAUTH_CRED_HASH_LOCK_ASSERT();
4701 hash_key
= kauth_cred_get_hashkey(new_cred
);
4702 hash_key
%= kauth_cred_table_size
;
4704 /* race fix - there is a window where another matching credential
4705 * could have been inserted between the time this one was created and we
4706 * got the hash lock. If we find a match return an error and have the
4709 if (kauth_cred_find(new_cred
) != NULL
) {
4713 /* take a reference for our use in credential hash table */
4714 kauth_cred_ref(new_cred
);
4716 /* insert the credential into the hash table */
4717 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
4726 * Description: Remove the given credential from our credential hash table
4728 * Parameters: cred Credential to remove from cred
4733 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4735 * Notes: The check for the reference increment after entry is generally
4736 * agree to be safe, since we use atomic operations, and the
4737 * following code occurs with the hash lock held; in theory, this
4738 * protects us from the 2->1 reference that gets us here.
4741 kauth_cred_remove(kauth_cred_t cred
)
4744 kauth_cred_t found_cred
;
4746 hash_key
= kauth_cred_get_hashkey(cred
);
4747 hash_key
%= kauth_cred_table_size
;
4750 if (cred
->cr_ref
< 1)
4751 panic("cred reference underflow");
4752 if (cred
->cr_ref
> 1)
4753 return; /* someone else got a ref */
4755 /* Find cred in the credential hash table */
4756 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4757 if (found_cred
== cred
) {
4758 /* found a match, remove it from the hash table */
4759 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
4761 mac_cred_label_destroy(cred
);
4763 AUDIT_SESSION_UNREF(cred
);
4766 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
4767 #if KAUTH_CRED_HASH_DEBUG
4774 /* Did not find a match... this should not happen! XXX Make panic? */
4775 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
4783 * Description: Using the given credential data, look for a match in our
4784 * credential hash table
4786 * Parameters: cred Credential to lookup in cred
4789 * Returns: NULL Not found
4790 * !NULL Matching cedential already in
4793 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4796 kauth_cred_find(kauth_cred_t cred
)
4799 kauth_cred_t found_cred
;
4800 posix_cred_t pcred
= posix_cred_get(cred
);
4802 KAUTH_CRED_HASH_LOCK_ASSERT();
4804 #if KAUTH_CRED_HASH_DEBUG
4805 static int test_count
= 0;
4808 if ((test_count
% 200) == 0) {
4809 kauth_cred_hash_print();
4813 hash_key
= kauth_cred_get_hashkey(cred
);
4814 hash_key
%= kauth_cred_table_size
;
4816 /* Find cred in the credential hash table */
4817 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4819 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
4822 * don't worry about the label unless the flags in
4823 * either credential tell us to.
4825 if ((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0 ||
4826 (pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) {
4827 /* include the label pointer in the compare */
4828 match
= (bcmp(&found_pcred
->cr_uid
, &pcred
->cr_uid
,
4829 (sizeof(struct ucred
) -
4830 offsetof(struct ucred
, cr_posix
))) == 0);
4832 /* flags have to match, but skip the label in bcmp */
4833 match
= (found_pcred
->cr_flags
== pcred
->cr_flags
&&
4834 bcmp(&found_pcred
->cr_uid
, &pcred
->cr_uid
,
4835 sizeof(struct posix_cred
)) == 0 &&
4836 bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
4837 sizeof(cred
->cr_audit
)) == 0);
4845 /* No match found */
4854 * Description: Generates a hash key using data that makes up a credential;
4857 * Parameters: datap Pointer to data to hash
4858 * data_len Count of bytes to hash
4859 * start_key Start key value
4861 * Returns: (u_long) Returned hash key
4863 static inline u_long
4864 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4866 u_long hash_key
= start_key
;
4869 while (data_len
> 0) {
4870 hash_key
= (hash_key
<< 4) + *datap
++;
4871 temp
= hash_key
& 0xF0000000;
4873 hash_key
^= temp
>> 24;
4883 * kauth_cred_get_hashkey
4885 * Description: Generate a hash key using data that makes up a credential;
4886 * based on ElfHash. We hash on the entire credential data,
4887 * not including the ref count or the TAILQ, which are mutable;
4888 * everything else isn't.
4890 * Parameters: cred Credential for which hash is
4893 * Returns: (u_long) Returned hash key
4895 * Notes: When actually moving the POSIX credential into a real label,
4896 * remember to update this hash computation.
4899 kauth_cred_get_hashkey(kauth_cred_t cred
)
4901 posix_cred_t pcred
= posix_cred_get(cred
);
4902 u_long hash_key
= 0;
4904 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
4905 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
4906 sizeof(struct ucred
) - offsetof(struct ucred
, cr_posix
),
4910 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
4911 sizeof(struct posix_cred
),
4913 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
4914 sizeof(struct au_session
),
4921 #if KAUTH_CRED_HASH_DEBUG
4923 * kauth_cred_hash_print
4925 * Description: Print out cred hash cache table information for debugging
4926 * purposes, including the credential contents
4928 * Parameters: (void)
4932 * Implicit returns: Results in console output
4935 kauth_cred_hash_print(void)
4938 kauth_cred_t found_cred
;
4940 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
4941 /* count slot hits, misses, collisions, and max depth */
4942 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4943 printf("[%02d] ", i
);
4945 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4950 kauth_cred_print(found_cred
);
4954 printf("NOCRED \n");
4958 #endif /* KAUTH_CRED_HASH_DEBUG */
4961 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4965 * Description: Print out an individual credential's contents for debugging
4968 * Parameters: cred The credential to print out
4972 * Implicit returns: Results in console output
4975 kauth_cred_print(kauth_cred_t cred
)
4979 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
);
4980 printf("group count %d gids ", cred
->cr_ngroups
);
4981 for (i
= 0; i
< NGROUPS
; i
++) {
4984 printf("%d ", cred
->cr_groups
[i
]);
4986 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
4987 printf("auditinfo_addr %d %d %d %d %d %d\n",
4988 cred
->cr_audit
.s_aia_p
->ai_auid
,
4989 cred
->cr_audit
.as_mask
.am_success
,
4990 cred
->cr_audit
.as_mask
.am_failure
,
4991 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
4992 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
4993 cred
->cr_audit
.as_aia_p
->ai_asid
);
4996 int is_target_cred( kauth_cred_t the_cred
)
4998 if ( the_cred
->cr_uid
!= 0 )
5000 if ( the_cred
->cr_ruid
!= 0 )
5002 if ( the_cred
->cr_svuid
!= 0 )
5004 if ( the_cred
->cr_ngroups
!= 11 )
5006 if ( the_cred
->cr_groups
[0] != 11 )
5008 if ( the_cred
->cr_groups
[1] != 81 )
5010 if ( the_cred
->cr_groups
[2] != 63947 )
5012 if ( the_cred
->cr_groups
[3] != 80288 )
5014 if ( the_cred
->cr_groups
[4] != 89006 )
5016 if ( the_cred
->cr_groups
[5] != 52173 )
5018 if ( the_cred
->cr_groups
[6] != 84524 )
5020 if ( the_cred
->cr_groups
[7] != 79 )
5022 if ( the_cred
->cr_groups
[8] != 80292 )
5024 if ( the_cred
->cr_groups
[9] != 80 )
5026 if ( the_cred
->cr_groups
[10] != 90824 )
5028 if ( the_cred
->cr_rgid
!= 11 )
5030 if ( the_cred
->cr_svgid
!= 11 )
5032 if ( the_cred
->cr_gmuid
!= 3475 )
5034 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5037 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5039 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5041 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5043 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5045 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5047 if ( the_cred->cr_flags != 0 )
5050 return( -1 ); // found target cred
5053 void get_backtrace( void )
5056 void * my_stack
[ MAX_STACK_DEPTH
];
5059 if ( cred_debug_buf_p
== NULL
) {
5060 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5061 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5064 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5065 /* buffer is full */
5069 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5070 if ( my_depth
== 0 ) {
5071 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5075 /* fill new backtrace */
5076 my_slot
= cred_debug_buf_p
->next_slot
;
5077 cred_debug_buf_p
->next_slot
++;
5078 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5079 for ( i
= 0; i
< my_depth
; i
++ ) {
5080 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5087 /* subset of struct ucred for use in sysctl_dump_creds */
5088 struct debug_ucred
{
5090 u_long cr_ref
; /* reference count */
5091 uid_t cr_uid
; /* effective user id */
5092 uid_t cr_ruid
; /* real user id */
5093 uid_t cr_svuid
; /* saved user id */
5094 short cr_ngroups
; /* number of groups in advisory list */
5095 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5096 gid_t cr_rgid
; /* real group id */
5097 gid_t cr_svgid
; /* saved group id */
5098 uid_t cr_gmuid
; /* UID for group membership purposes */
5099 struct auditinfo_addr cr_audit
; /* user auditing data. */
5100 void *cr_label
; /* MACF label */
5101 int cr_flags
; /* flags on credential */
5103 typedef struct debug_ucred debug_ucred
;
5105 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5106 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5109 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5113 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5115 int i
, j
, counter
= 0;
5118 kauth_cred_t found_cred
;
5119 debug_ucred
* cred_listp
;
5120 debug_ucred
* nextp
;
5122 /* This is a readonly node. */
5123 if (req
->newptr
!= USER_ADDR_NULL
)
5126 /* calculate space needed */
5127 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5128 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5133 /* they are querying us so just return the space required. */
5134 if (req
->oldptr
== USER_ADDR_NULL
) {
5135 counter
+= 10; // add in some padding;
5136 req
->oldidx
= counter
* sizeof(debug_ucred
);
5140 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5141 if ( cred_listp
== NULL
) {
5145 /* fill in creds to send back */
5148 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5149 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5150 nextp
->credp
= found_cred
;
5151 nextp
->cr_ref
= found_cred
->cr_ref
;
5152 nextp
->cr_uid
= found_cred
->cr_uid
;
5153 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5154 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5155 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5156 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5157 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5159 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5160 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5161 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5162 nextp
->cr_audit
.ai_auid
=
5163 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5164 nextp
->cr_audit
.ai_mask
.am_success
=
5165 found_cred
->cr_audit
.as_mask
.am_success
;
5166 nextp
->cr_audit
.ai_mask
.am_failure
=
5167 found_cred
->cr_audit
.as_mask
.am_failure
;
5168 nextp
->cr_audit
.ai_termid
.at_port
=
5169 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5170 nextp
->cr_audit
.ai_termid
.at_type
=
5171 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5172 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5173 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5174 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5175 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5176 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5177 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5178 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5179 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5180 nextp
->cr_audit
.ai_asid
=
5181 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5182 nextp
->cr_audit
.ai_flags
=
5183 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5184 nextp
->cr_label
= found_cred
->cr_label
;
5185 nextp
->cr_flags
= found_cred
->cr_flags
;
5187 space
+= sizeof(debug_ucred
);
5188 if ( space
> req
->oldlen
) {
5189 FREE(cred_listp
, M_TEMP
);
5194 req
->oldlen
= space
;
5195 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5196 FREE(cred_listp
, M_TEMP
);
5201 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5202 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5205 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5209 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5214 cred_debug_buffer
* bt_bufp
;
5215 cred_backtrace
* nextp
;
5217 /* This is a readonly node. */
5218 if (req
->newptr
!= USER_ADDR_NULL
)
5221 if ( cred_debug_buf_p
== NULL
) {
5225 /* calculate space needed */
5226 space
= sizeof( cred_debug_buf_p
->next_slot
);
5227 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5229 /* they are querying us so just return the space required. */
5230 if (req
->oldptr
== USER_ADDR_NULL
) {
5231 req
->oldidx
= space
;
5235 if ( space
> req
->oldlen
) {
5239 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5240 if ( bt_bufp
== NULL
) {
5244 /* fill in backtrace info to send back */
5245 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5246 space
= sizeof(bt_bufp
->next_slot
);
5248 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5249 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5250 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5251 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5252 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5254 space
+= sizeof(*nextp
);
5257 req
->oldlen
= space
;
5258 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5259 FREE(bt_bufp
, M_TEMP
);
5263 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5267 **********************************************************************
5268 * The following routines will be moved to a policy_posix.c module at
5269 * some future point.
5270 **********************************************************************
5276 * Description: Helper function to create a kauth_cred_t credential that is
5277 * initally labelled with a specific POSIX credential label
5279 * Parameters: pcred The posix_cred_t to use as the initial
5282 * Returns: (kauth_cred_t) The credential that was found in the
5284 * NULL kauth_cred_add() failed, or there was
5285 * no egid specified, or we failed to
5286 * attach a label to the new credential
5288 * Notes: This function currently wraps kauth_cred_create(), and is the
5289 * only consume of tht ill-fated function, apart from bsd_init().
5290 * It exists solely to support the NFS server code creation of
5291 * credentials based on the over-the-wire RPC cals containing
5292 * traditional POSIX credential information being tunneled to
5293 * the server host from the client machine.
5295 * In the future, we hope this function goes away.
5297 * In the short term, it creates a temporary credential, puts
5298 * the POSIX information from NFS into it, and then calls
5299 * kauth_cred_create(), as an internal implementaiton detail.
5301 * If we have to keep it around in the medium term, it will
5302 * create a new kauth_cred_t, then label it with a POSIX label
5303 * corresponding to the contents of the kauth_cred_t. If the
5304 * policy_posix MACF module is not loaded, it will instead
5305 * substitute a posix_cred_t which GRANTS all access (effectively
5306 * a "root" credential) in order to not prevent NFS from working
5307 * in the case that we are not supporting POSIX credentials.
5310 posix_cred_create(posix_cred_t pcred
)
5312 struct ucred temp_cred
;
5314 bzero(&temp_cred
, sizeof(temp_cred
));
5315 temp_cred
.cr_posix
= *pcred
;
5317 return kauth_cred_create(&temp_cred
);
5324 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5325 * any, which is associated with it.
5327 * Parameters: cred The credential to obtain the label from
5329 * Returns: posix_cred_t The POSIX credential label
5331 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5332 * this function will return a pointer to a posix_cred_t which
5333 * GRANTS all access (effectively, a "root" credential). This is
5334 * necessary to support legacy code which insists on tightly
5335 * integrating POSIX credentails into its APIs, including, but
5336 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5337 * NFSv3, signals, dtrace, and a large number of kauth routines
5338 * used to implement POSIX permissions related system calls.
5340 * In the event that the policy_posix MACF module IS loaded, and
5341 * there is no POSIX label on the kauth_cred_t credential, this
5342 * function will return a pointer to a posix_cred_t which DENIES
5343 * all access (effectively, a "deny rights granted by POSIX"
5344 * credential). This is necessary to support the concept of a
5345 * transiently loaded POSIX policy, or kauth_cred_t credentials
5346 * which can not be used in conjunctions with POSIX permissions
5349 * This function currently returns the address of the cr_posix
5350 * field of the supplied kauth_cred_t credential, and as such
5351 * currently can not fail. In the future, this will not be the
5355 posix_cred_get(kauth_cred_t cred
)
5357 return(&cred
->cr_posix
);
5364 * Description: Label a kauth_cred_t with a POSIX credential label
5366 * Parameters: cred The credential to label
5367 * pcred The POSIX credential t label it with
5371 * Notes: This function is currently void in order to permit it to fit
5372 * in with the currrent MACF framework label methods which allow
5373 * labelling to fail silently. This is like acceptable for
5374 * mandatory access controls, but not for POSIX, since those
5375 * access controls are advisory. We will need to consider a
5376 * return value in a future version of the MACF API.
5378 * This operation currenty can not fail, as currently the POSIX
5379 * credential is a subfield of the kauth_cred_t (ucred), which
5380 * MUST be valid. In the future, this will not be the case.
5383 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5385 cred
->cr_posix
= *pcred
; /* structure assign for now */
5392 * Description: Perform a POSIX access check for a protected object
5394 * Parameters: cred The credential to check
5395 * object_uid The POSIX UID of the protected object
5396 * object_gid The POSIX GID of the protected object
5397 * object_mode The POSIX mode of the protected object
5398 * mode_req The requested POSIX access rights
5400 * Returns 0 Access is granted
5401 * EACCES Access is denied
5403 * Notes: This code optimizes the case where the world and group rights
5404 * would both grant the requested rights to avoid making a group
5405 * membership query. This is a big performance win in the case
5406 * where this is true.
5409 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5412 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5413 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5414 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5417 * Check first for owner rights
5419 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5423 * Combined group and world rights check, if we don't have owner rights
5425 * OPTIMIZED: If group and world rights would grant the same bits, and
5426 * they set of requested bits is in both, then we can simply check the
5427 * world rights, avoiding a group membership check, which is expensive.
5429 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5433 * NON-OPTIMIZED: requires group membership check.
5435 if ((mode_req
& mode_group
) != mode_req
) {
5437 * exclusion group : treat errors as "is a member"
5439 * NON-OPTIMIZED: +group would deny; must check group
5441 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5443 * DENY: +group denies
5447 if ((mode_req
& mode_world
) != mode_req
) {
5449 * DENY: both -group & world would deny
5454 * ALLOW: allowed by -group and +world
5461 * inclusion group; treat errors as "not a member"
5463 * NON-OPTIMIZED: +group allows, world denies; must
5466 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5468 * ALLOW: allowed by +group
5472 if ((mode_req
& mode_world
) != mode_req
) {
5474 * DENY: both -group & world would deny
5479 * ALLOW: allowed by -group and +world