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 /* do we have a translation? */
2282 if (ki
.ki_valid
& to
) {
2283 /* found a valid cached entry, check expiry */
2286 expired
= kauth_identity_guid_expired
;
2288 case KI_VALID_NTSID
:
2289 expired
= kauth_identity_ntsid_expired
;
2294 expired
= kauth_identity_guid_expired
;
2296 case KI_VALID_NTSID
:
2297 expired
= kauth_identity_ntsid_expired
;
2303 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2305 * If no expiry function, or not expired, we have found
2309 KAUTH_DEBUG("CACHE - no expiry function");
2312 if (!expired(&ki
)) {
2313 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2317 * We leave ki_valid set here; it contains a
2318 * translation but the TTL has expired. If we can't
2319 * get a result from the resolver, we will use it as
2320 * a better-than nothing alternative.
2322 KAUTH_DEBUG("CACHE - expired entry found");
2325 * A guid can't both match a uid and a gid, so if we
2326 * found a cache entry while looking for one or the
2327 * other from a guid, the 'from' is KI_VALID_GUID,
2328 * and the 'to' is one, and the other one is valid,
2329 * then we immediately return ENOENT without calling
2330 * the resolver again.
2332 if (from
== KI_VALID_GUID
&&
2333 (((ki
.ki_valid
& KI_VALID_UID
) &&
2334 to
== KI_VALID_GID
) ||
2335 ((ki
.ki_valid
& KI_VALID_GID
) &&
2336 to
== KI_VALID_UID
))) {
2343 * We failed to find a cache entry; call the resolver.
2345 * Note: We ask for as much non-extended data as we can get,
2346 * and only provide (or ask for) extended information if
2347 * we have a 'from' (or 'to') which requires it. This
2348 * way we don't pay for the extra transfer overhead for
2349 * data we don't need.
2351 bzero(&el
, sizeof(el
));
2352 el
.el_info_pid
= current_proc()->p_pid
;
2355 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2356 el
.el_uid
= *(uid_t
*)src
;
2359 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2360 el
.el_gid
= *(gid_t
*)src
;
2363 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2364 el
.el_uguid
= *(guid_t
*)src
;
2365 el
.el_gguid
= *(guid_t
*)src
;
2367 case KI_VALID_NTSID
:
2368 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2369 el
.el_usid
= *(ntsid_t
*)src
;
2370 el
.el_gsid
= *(ntsid_t
*)src
;
2372 case KI_VALID_PWNAM
:
2373 /* extra overhead */
2374 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2375 extend_data
= CAST_USER_ADDR_T(src
);
2377 case KI_VALID_GRNAM
:
2378 /* extra overhead */
2379 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2380 extend_data
= CAST_USER_ADDR_T(src
);
2386 * Here we ask for everything all at once, to avoid having to work
2387 * out what we really want now, or might want soon.
2389 * Asking for SID translations when we don't know we need them right
2390 * now is going to cause excess work to be done if we're connected
2391 * to a network that thinks it can translate them. This list needs
2392 * to get smaller/smarter.
2394 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2395 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2396 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2397 if (to
== KI_VALID_PWNAM
) {
2398 /* extra overhead */
2399 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2400 extend_data
= CAST_USER_ADDR_T(dst
);
2402 if (to
== KI_VALID_GRNAM
) {
2403 /* extra overhead */
2404 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2405 extend_data
= CAST_USER_ADDR_T(dst
);
2409 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2410 error
= kauth_resolver_submit(&el
, extend_data
);
2411 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2413 /* was the external lookup successful? */
2416 * Save the results from the lookup - we may have other
2417 * information, even if we didn't get a guid or the
2420 * If we came from a name, we know the extend_data is valid.
2422 if (from
== KI_VALID_PWNAM
)
2423 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2424 else if (from
== KI_VALID_GRNAM
)
2425 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2427 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2430 * Check to see if we have a valid cache entry
2431 * originating from the result.
2433 if (!(ki
.ki_valid
& to
)) {
2441 * Copy from the appropriate struct kauth_identity cache entry
2442 * structure into the destination buffer area.
2446 *(uid_t
*)dst
= ki
.ki_uid
;
2449 *(gid_t
*)dst
= ki
.ki_gid
;
2452 *(guid_t
*)dst
= ki
.ki_guid
;
2454 case KI_VALID_NTSID
:
2455 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2457 case KI_VALID_PWNAM
:
2458 case KI_VALID_GRNAM
:
2459 /* handled in kauth_resolver_complete() */
2464 KAUTH_DEBUG("CACHE - returned successfully");
2470 * Group membership cache.
2472 * XXX the linked-list implementation here needs to be optimized.
2475 struct kauth_group_membership
{
2476 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
2477 uid_t gm_uid
; /* the identity whose membership we're recording */
2478 gid_t gm_gid
; /* group of which they are a member */
2479 time_t gm_expiry
; /* TTL for the membership */
2481 #define KAUTH_GROUP_ISMEMBER (1<<0)
2484 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
2485 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
2486 static int kauth_groups_count
;
2488 static lck_mtx_t
*kauth_groups_mtx
;
2489 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
2490 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
2492 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
2493 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
2494 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
2500 * Description: Initialize the groups cache
2502 * Parameters: (void)
2506 * Notes: Intialize the groups cache for use; the group cache is used
2507 * to avoid unnecessary calls out to user space.
2509 * This function is called from kauth_init() in the file
2510 * kern_authorization.c.
2513 kauth_groups_init(void)
2515 TAILQ_INIT(&kauth_groups
);
2516 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2521 * kauth_groups_expired
2523 * Description: Handle lazy expiration of group membership cache entries
2525 * Parameters: gm group membership entry to
2526 * check for expiration
2528 * Returns: 1 Expired
2532 kauth_groups_expired(struct kauth_group_membership
*gm
)
2537 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2544 * Description: Promote the entry to the head of the LRU, assumes the cache
2547 * Parameters: kip group membership entry to move
2548 * to the head of the LRU list,
2549 * if it's not already there
2553 * Notes: This is called even if the entry has expired; typically an
2554 * expired entry that's been looked up is about to be revalidated,
2555 * and having it closer to the head of the LRU means finding it
2556 * quickly again when the revalidation comes through.
2559 kauth_groups_lru(struct kauth_group_membership
*gm
)
2561 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2562 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2563 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2569 * kauth_groups_updatecache
2571 * Description: Given a lookup result, add any group cache associations that
2572 * we don't currently have.
2574 * Parameters: elp External lookup result from
2575 * user space daemon to kernel
2576 * rkip pointer to returned kauth
2582 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2584 struct kauth_group_membership
*gm
;
2587 /* need a valid response if we are to cache anything */
2589 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2590 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2596 * Search for an existing record for this association before inserting
2597 * a new one; if we find one, update it instead of creating a new one
2599 KAUTH_GROUPS_LOCK();
2600 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2601 if ((el
->el_uid
== gm
->gm_uid
) &&
2602 (el
->el_gid
== gm
->gm_gid
)) {
2603 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2604 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2606 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2608 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2609 kauth_groups_lru(gm
);
2613 KAUTH_GROUPS_UNLOCK();
2615 /* if we found an entry to update, stop here */
2619 /* allocate a new record */
2620 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2622 gm
->gm_uid
= el
->el_uid
;
2623 gm
->gm_gid
= el
->el_gid
;
2624 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2625 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2627 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2629 gm
->gm_expiry
= el
->el_member_valid
+ tv
.tv_sec
;
2633 * Insert the new entry. Note that it's possible to race ourselves
2634 * here and end up with duplicate entries in the list. Wasteful, but
2635 * harmless since the first into the list will never be looked up,
2636 * and thus will eventually just fall off the end.
2638 KAUTH_GROUPS_LOCK();
2639 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2640 if (kauth_groups_count
++ > KAUTH_GROUPS_CACHEMAX
) {
2641 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2642 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2643 kauth_groups_count
--;
2647 KAUTH_GROUPS_UNLOCK();
2649 /* free expired cache entry */
2656 * Group membership KPI
2660 * kauth_cred_ismember_gid
2662 * Description: Given a credential and a GID, determine if the GID is a member
2663 * of one of the supplementary groups associated with the given
2666 * Parameters: cred Credential to check in
2667 * gid GID to check for membership
2668 * resultp Pointer to int to contain the
2669 * result of the call
2671 * Returns: 0 Success
2672 * ENOENT Could not proform lookup
2673 * kauth_resolver_submit:EWOULDBLOCK
2674 * kauth_resolver_submit:EINTR
2675 * kauth_resolver_submit:ENOMEM
2676 * kauth_resolver_submit:ENOENT User space daemon did not vend
2678 * kauth_resolver_submit:??? Unlikely error from user space
2681 * *resultp (modified) 1 Is member
2684 * Notes: This function guarantees not to modify resultp when returning
2687 * This function effectively checkes the EGID as well, since the
2688 * EGID is cr_groups[0] as an implementation detail.
2691 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2693 posix_cred_t pcred
= posix_cred_get(cred
);
2694 struct kauth_group_membership
*gm
;
2695 struct kauth_identity_extlookup el
;
2699 * Check the per-credential list of override groups.
2701 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2702 * the cache should be used for that case.
2704 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
2705 if (gid
== pcred
->cr_groups
[i
]) {
2712 * If we don't have a UID for group membership checks, the in-cred list
2713 * was authoritative and we can stop here.
2715 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
2722 * If the resolver hasn't checked in yet, we are early in the boot
2723 * phase and the local group list is complete and authoritative.
2725 if (!kauth_resolver_registered
) {
2731 /* XXX check supplementary groups */
2732 /* XXX check whiteout groups */
2733 /* XXX nesting of supplementary/whiteout groups? */
2736 * Check the group cache.
2738 KAUTH_GROUPS_LOCK();
2739 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2740 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2741 kauth_groups_lru(gm
);
2746 /* did we find a membership entry? */
2748 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2749 KAUTH_GROUPS_UNLOCK();
2751 /* if we did, we can return now */
2755 /* nothing in the cache, need to go to userland */
2756 bzero(&el
, sizeof(el
));
2757 el
.el_info_pid
= current_proc()->p_pid
;
2758 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2759 el
.el_uid
= pcred
->cr_gmuid
;
2761 el
.el_member_valid
= 0; /* XXX set by resolver? */
2762 error
= kauth_resolver_submit(&el
, 0ULL);
2765 /* save the results from the lookup */
2766 kauth_groups_updatecache(&el
);
2768 /* if we successfully ascertained membership, report */
2769 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2770 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2779 * kauth_cred_ismember_guid
2781 * Description: Determine whether the supplied credential is a member of the
2782 * group nominated by GUID.
2784 * Parameters: cred Credential to check in
2785 * guidp Pointer to GUID whose group
2786 * we are testing for membership
2787 * resultp Pointer to int to contain the
2788 * result of the call
2790 * Returns: 0 Success
2791 * kauth_cred_guid2gid:EINVAL
2792 * kauth_cred_ismember_gid:ENOENT
2793 * kauth_resolver_submit:ENOENT User space daemon did not vend
2795 * kauth_cred_ismember_gid:EWOULDBLOCK
2796 * kauth_cred_ismember_gid:EINTR
2797 * kauth_cred_ismember_gid:ENOMEM
2798 * kauth_cred_ismember_gid:??? Unlikely error from user space
2801 * *resultp (modified) 1 Is member
2805 kauth_cred_ismember_guid(kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2807 struct kauth_identity ki
;
2812 wkg
= kauth_wellknown_guid(guidp
);
2814 case KAUTH_WKG_NOBODY
:
2817 case KAUTH_WKG_EVERYBODY
:
2823 * Grovel the identity cache looking for this GUID.
2824 * If we find it, and it is for a user record, return
2825 * false because it's not a group.
2827 * This is necessary because we don't have -ve caching
2828 * of group memberships, and we really want to avoid
2829 * calling out to the resolver if at all possible.
2831 * Because we're called by the ACL evaluator, and the
2832 * ACL evaluator is likely to encounter ACEs for users,
2833 * this is expected to be a common case.
2836 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
2837 !kauth_identity_guid_expired(&ki
)) {
2838 if (ki
.ki_valid
& KI_VALID_GID
) {
2839 /* It's a group after all... */
2843 if (ki
.ki_valid
& KI_VALID_UID
) {
2848 #endif /* 6603280 */
2850 * Attempt to translate the GUID to a GID. Even if
2851 * this fails, we will have primed the cache if it is
2852 * a user record and we'll see it above the next time
2855 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2857 * If we have no guid -> gid translation, it's not a group and
2858 * thus the cred can't be a member.
2860 if (error
== ENOENT
) {
2866 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
2873 * kauth_cred_gid_subset
2875 * Description: Given two credentials, determine if all GIDs associated with
2876 * the first are also associated with the second
2878 * Parameters: cred1 Credential to check for
2879 * cred2 Credential to check in
2880 * resultp Pointer to int to contain the
2881 * result of the call
2883 * Returns: 0 Success
2884 * non-zero See kauth_cred_ismember_gid for
2888 * *resultp (modified) 1 Is subset
2891 * Notes: This function guarantees not to modify resultp when returning
2895 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
2897 int i
, err
, res
= 1;
2899 posix_cred_t pcred1
= posix_cred_get(cred1
);
2900 posix_cred_t pcred2
= posix_cred_get(cred2
);
2902 /* First, check the local list of groups */
2903 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
2904 gid
= pcred1
->cr_groups
[i
];
2905 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
2909 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
2915 /* Check real gid */
2916 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
2920 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
2921 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
2926 /* Finally, check saved gid */
2927 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
2931 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
2932 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
2943 * kauth_cred_issuser
2945 * Description: Fast replacement for issuser()
2947 * Parameters: cred Credential to check for super
2950 * Returns: 0 Not super user
2953 * Notes: This function uses a magic number which is not a manifest
2954 * constant; this is bad practice.
2957 kauth_cred_issuser(kauth_cred_t cred
)
2959 return(kauth_cred_getuid(cred
) == 0);
2967 /* lock protecting credential hash table */
2968 static lck_mtx_t
*kauth_cred_hash_mtx
;
2969 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2970 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2971 #if KAUTH_CRED_HASH_DEBUG
2972 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2973 #else /* !KAUTH_CRED_HASH_DEBUG */
2974 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2975 #endif /* !KAUTH_CRED_HASH_DEBUG */
2981 * Description: Initialize the credential hash cache
2983 * Parameters: (void)
2987 * Notes: Intialize the credential hash cache for use; the credential
2988 * hash cache is used convert duplicate credentials into a
2989 * single reference counted credential in order to save wired
2990 * kernel memory. In practice, this generally means a desktop
2991 * system runs with a few tens of credentials, instead of one
2992 * per process, one per thread, one per vnode cache entry, and
2993 * so on. This generally results in savings of 200K or more
2994 * (potentially much more on server systems).
2996 * The hash cache internally has a reference on the credential
2997 * for itself as a means of avoiding a reclaim race for a
2998 * credential in the process of having it's last non-hash
2999 * reference released. This would otherwise result in the
3000 * possibility of a freed credential that was still in uses due
3001 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3003 * On final release, the hash reference is droped, and the
3004 * credential is freed back to the system.
3006 * This function is called from kauth_init() in the file
3007 * kern_authorization.c.
3010 kauth_cred_init(void)
3014 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3015 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
3017 /*allocate credential hash table */
3018 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3019 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
3020 M_KAUTH
, M_WAITOK
| M_ZERO
);
3021 if (kauth_cred_table_anchor
== NULL
)
3022 panic("startup: kauth_cred_init");
3023 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
3024 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3032 * Description: Get the current thread's effective UID.
3034 * Parameters: (void)
3036 * Returns: (uid_t) The effective UID of the
3042 return(kauth_cred_getuid(kauth_cred_get()));
3049 * Description: Get the current thread's real UID.
3051 * Parameters: (void)
3053 * Returns: (uid_t) The real UID of the current
3059 return(kauth_cred_getruid(kauth_cred_get()));
3066 * Description: Get the current thread's effective GID.
3068 * Parameters: (void)
3070 * Returns: (gid_t) The effective GID of the
3076 return(kauth_cred_getgid(kauth_cred_get()));
3083 * Description: Get the current thread's real GID.
3085 * Parameters: (void)
3087 * Returns: (gid_t) The real GID of the current
3093 return(kauth_cred_getrgid(kauth_cred_get()));
3100 * Description: Returns a pointer to the current thread's credential
3102 * Parameters: (void)
3104 * Returns: (kauth_cred_t) Pointer to the current thread's
3107 * Notes: This function does not take a reference; because of this, the
3108 * caller MUST NOT do anything that would let the thread's
3109 * credential change while using the returned value, without
3110 * first explicitly taking their own reference.
3112 * If a caller intends to take a reference on the resulting
3113 * credential pointer from calling this function, it is strongly
3114 * recommended that the caller use kauth_cred_get_with_ref()
3115 * instead, to protect against any future changes to the cred
3116 * locking protocols; such changes could otherwise potentially
3117 * introduce race windows in the callers code.
3120 kauth_cred_get(void)
3123 struct uthread
*uthread
;
3125 uthread
= get_bsdthread_info(current_thread());
3127 if (uthread
== NULL
)
3128 panic("thread wants credential but has no BSD thread info");
3130 * We can lazy-bind credentials to threads, as long as their processes
3133 * XXX If we later inline this function, the code in this block
3134 * XXX should probably be called out in a function.
3136 if (uthread
->uu_ucred
== NOCRED
) {
3137 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3138 panic("thread wants credential but has no BSD process");
3139 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3141 return(uthread
->uu_ucred
);
3145 mach_kauth_cred_uthread_update(void)
3150 uthread
= get_bsdthread_info(current_thread());
3151 proc
= current_proc();
3153 kauth_cred_uthread_update(uthread
, proc
);
3157 * kauth_cred_uthread_update
3159 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3160 * late-bind the uthread cred to the proc cred.
3162 * Parameters: uthread_t The uthread to update
3163 * proc_t The process to update to
3167 * Notes: This code is common code called from system call or trap entry
3168 * in the case that the process thread may have been changed
3169 * since the last time the thread entered the kernel. It is
3170 * generally only called with the current uthread and process as
3174 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3176 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3177 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3178 kauth_cred_t old
= uthread
->uu_ucred
;
3179 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3180 if (IS_VALID_CRED(old
))
3181 kauth_cred_unref(&old
);
3187 * kauth_cred_get_with_ref
3189 * Description: Takes a reference on the current thread's credential, and then
3190 * returns a pointer to it to the caller.
3192 * Parameters: (void)
3194 * Returns: (kauth_cred_t) Pointer to the current thread's
3195 * newly referenced credential
3197 * Notes: This function takes a reference on the credential before
3198 * returning it to the caller.
3200 * It is the responsibility of the calling code to release this
3201 * reference when the credential is no longer in use.
3203 * Since the returned reference may be a persistent reference
3204 * (e.g. one cached in another data structure with a lifetime
3205 * longer than the calling function), this release may be delayed
3206 * until such time as the persistent reference is to be destroyed.
3207 * An example of this would be the per vnode credential cache used
3208 * to accelerate lookup operations.
3211 kauth_cred_get_with_ref(void)
3214 struct uthread
*uthread
;
3216 uthread
= get_bsdthread_info(current_thread());
3218 if (uthread
== NULL
)
3219 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3220 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3221 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3224 * We can lazy-bind credentials to threads, as long as their processes
3227 * XXX If we later inline this function, the code in this block
3228 * XXX should probably be called out in a function.
3230 if (uthread
->uu_ucred
== NOCRED
) {
3231 /* take reference for new cred in thread */
3232 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3234 /* take a reference for our caller */
3235 kauth_cred_ref(uthread
->uu_ucred
);
3236 return(uthread
->uu_ucred
);
3241 * kauth_cred_proc_ref
3243 * Description: Takes a reference on the current process's credential, and
3244 * then returns a pointer to it to the caller.
3246 * Parameters: procp Process whose credential we
3247 * intend to take a reference on
3249 * Returns: (kauth_cred_t) Pointer to the process's
3250 * newly referenced credential
3252 * Locks: PROC_LOCK is held before taking the reference and released
3253 * after the refeence is taken to protect the p_ucred field of
3254 * the process referred to by procp.
3256 * Notes: This function takes a reference on the credential before
3257 * returning it to the caller.
3259 * It is the responsibility of the calling code to release this
3260 * reference when the credential is no longer in use.
3262 * Since the returned reference may be a persistent reference
3263 * (e.g. one cached in another data structure with a lifetime
3264 * longer than the calling function), this release may be delayed
3265 * until such time as the persistent reference is to be destroyed.
3266 * An example of this would be the per vnode credential cache used
3267 * to accelerate lookup operations.
3270 kauth_cred_proc_ref(proc_t procp
)
3275 cred
= proc_ucred(procp
);
3276 kauth_cred_ref(cred
);
3285 * Description: Allocate a new credential
3287 * Parameters: (void)
3289 * Returns: !NULL Newly allocated credential
3290 * NULL Insufficient memory
3292 * Notes: The newly allocated credential is zero'ed as part of the
3293 * allocation process, with the exception of the reference
3294 * count, which is set to 1 to indicate a single reference
3295 * held by the caller.
3297 * Since newly allocated credentials have no external pointers
3298 * referencing them, prior to making them visible in an externally
3299 * visible pointer (e.g. by adding them to the credential hash
3300 * cache) is the only legal time in which an existing credential
3301 * can be safely iinitialized or modified directly.
3303 * After initialization, the caller is expected to call the
3304 * function kauth_cred_add() to add the credential to the hash
3305 * cache, after which time it's frozen and becomes publically
3308 * The release protocol depends on kauth_hash_add() being called
3309 * before kauth_cred_rele() (there is a diagnostic panic which
3310 * will trigger if this protocol is not observed).
3312 * XXX: This function really ought to be static, rather than being
3313 * exported as KPI, since a failure of kauth_cred_add() can only
3314 * be handled by an explicit free of the credential; such frees
3315 * depend on knowlegdge of the allocation method used, which is
3316 * permitted to change between kernel revisions.
3318 * XXX: In the insufficient resource case, this code panic's rather
3319 * than returning a NULL pointer; the code that calls this
3320 * function needs to be audited before this can be changed.
3323 kauth_cred_alloc(void)
3325 kauth_cred_t newcred
;
3327 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3329 posix_cred_t newpcred
= posix_cred_get(newcred
);
3330 bzero(newcred
, sizeof(*newcred
));
3331 newcred
->cr_ref
= 1;
3332 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3333 /* must do this, or cred has same group membership as uid 0 */
3334 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3337 panic("kauth_cred_alloc: couldn't allocate credential");
3341 #if KAUTH_CRED_HASH_DEBUG
3346 mac_cred_label_init(newcred
);
3356 * Description: Look to see if we already have a known credential in the hash
3357 * cache; if one is found, bump the reference count and return
3358 * it. If there are no credentials that match the given
3359 * credential, then allocate a new credential.
3361 * Parameters: cred Template for credential to
3364 * Returns: (kauth_cred_t) The credential that was found
3365 * in the hash or created
3366 * NULL kauth_cred_add() failed, or
3367 * there was not an egid specified
3369 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3370 * maintain this field, we can't expect callers to know how it
3371 * needs to be set. Callers should be prepared for this field
3372 * to be overwritten.
3374 * XXX: This code will tight-loop if memory for a new credential is
3375 * persistently unavailable; this is perhaps not the wisest way
3376 * to handle this condition, but current callers do not expect
3380 kauth_cred_create(kauth_cred_t cred
)
3382 kauth_cred_t found_cred
, new_cred
= NULL
;
3383 posix_cred_t pcred
= posix_cred_get(cred
);
3386 KAUTH_CRED_HASH_LOCK_ASSERT();
3388 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3389 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3392 * If the template credential is not opting out of external
3393 * group membership resolution, then we need to check that
3394 * the UID we will be using is resolvable by the external
3395 * resolver. If it's not, then we opt it out anyway, since
3396 * all future external resolution requests will be failing
3397 * anyway, and potentially taking a long time to do it. We
3398 * use gid 0 because we always know it will exist and not
3399 * trigger additional lookups. This is OK, because we end up
3400 * precatching the information here as a result.
3402 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3404 * It's a recognized value; we don't really care about
3405 * the answer, so long as it's something the external
3406 * resolver could have vended.
3408 pcred
->cr_gmuid
= pcred
->cr_uid
;
3411 * It's not something the external resolver could
3412 * have vended, so we don't want to ask it more
3413 * questions about the credential in the future. This
3414 * speeds up future lookups, as long as the caller
3415 * caches results; otherwise, it the same recurring
3416 * cost. Since most credentials are used multiple
3417 * times, we still get some performance win from this.
3419 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3420 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3424 /* Caller *must* specify at least the egid in cr_groups[0] */
3425 if (pcred
->cr_ngroups
< 1)
3429 KAUTH_CRED_HASH_LOCK();
3430 found_cred
= kauth_cred_find(cred
);
3431 if (found_cred
!= NULL
) {
3433 * Found an existing credential so we'll bump
3434 * reference count and return
3436 kauth_cred_ref(found_cred
);
3437 KAUTH_CRED_HASH_UNLOCK();
3440 KAUTH_CRED_HASH_UNLOCK();
3443 * No existing credential found. Create one and add it to
3446 new_cred
= kauth_cred_alloc();
3447 if (new_cred
!= NULL
) {
3449 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3450 new_pcred
->cr_uid
= pcred
->cr_uid
;
3451 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3452 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3453 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3454 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3455 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3456 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3457 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3459 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3460 sizeof(new_cred
->cr_audit
));
3462 new_pcred
->cr_flags
= pcred
->cr_flags
;
3464 KAUTH_CRED_HASH_LOCK();
3465 err
= kauth_cred_add(new_cred
);
3466 KAUTH_CRED_HASH_UNLOCK();
3468 /* Retry if kauth_cred_add returns non zero value */
3472 mac_cred_label_destroy(new_cred
);
3474 AUDIT_SESSION_UNREF(new_cred
);
3476 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3486 * kauth_cred_setresuid
3488 * Description: Update the given credential using the UID arguments. The given
3489 * UIDs are used to set the effective UID, real UID, saved UID,
3490 * and GMUID (used for group membership checking).
3492 * Parameters: cred The original credential
3493 * ruid The new real UID
3494 * euid The new effective UID
3495 * svuid The new saved UID
3496 * gmuid KAUTH_UID_NONE -or- the new
3497 * group membership UID
3499 * Returns: (kauth_cred_t) The updated credential
3501 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3502 * setting, so if you don't want it to change, pass it the
3503 * previous value, explicitly.
3505 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3506 * if it returns a credential other than the one it is passed,
3507 * will have dropped the reference on the passed credential. All
3508 * callers should be aware of this, and treat this function as an
3509 * unref + ref, potentially on different credentials.
3511 * Because of this, the caller is expected to take its own
3512 * reference on the credential passed as the first parameter,
3513 * and be prepared to release the reference on the credential
3514 * that is returned to them, if it is not intended to be a
3515 * persistent reference.
3518 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3520 struct ucred temp_cred
;
3521 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3522 posix_cred_t pcred
= posix_cred_get(cred
);
3524 NULLCRED_CHECK(cred
);
3527 * We don't need to do anything if the UIDs we are changing are
3528 * already the same as the UIDs passed in
3530 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3531 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3532 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3533 (pcred
->cr_gmuid
== gmuid
)) {
3534 /* no change needed */
3539 * Look up in cred hash table to see if we have a matching credential
3540 * with the new values; this is done by calling kauth_cred_update().
3542 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3543 if (euid
!= KAUTH_UID_NONE
) {
3544 temp_pcred
->cr_uid
= euid
;
3546 if (ruid
!= KAUTH_UID_NONE
) {
3547 temp_pcred
->cr_ruid
= ruid
;
3549 if (svuid
!= KAUTH_UID_NONE
) {
3550 temp_pcred
->cr_svuid
= svuid
;
3554 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3555 * opt out of participation in external group resolution, unless we
3556 * unless we explicitly opt back in later.
3558 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3559 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3562 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3567 * kauth_cred_setresgid
3569 * Description: Update the given credential using the GID arguments. The given
3570 * GIDs are used to set the effective GID, real GID, and saved
3573 * Parameters: cred The original credential
3574 * rgid The new real GID
3575 * egid The new effective GID
3576 * svgid The new saved GID
3578 * Returns: (kauth_cred_t) The updated credential
3580 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3581 * if it returns a credential other than the one it is passed,
3582 * will have dropped the reference on the passed credential. All
3583 * callers should be aware of this, and treat this function as an
3584 * unref + ref, potentially on different credentials.
3586 * Because of this, the caller is expected to take its own
3587 * reference on the credential passed as the first parameter,
3588 * and be prepared to release the reference on the credential
3589 * that is returned to them, if it is not intended to be a
3590 * persistent reference.
3593 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3595 struct ucred temp_cred
;
3596 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3597 posix_cred_t pcred
= posix_cred_get(cred
);
3599 NULLCRED_CHECK(cred
);
3600 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3603 * We don't need to do anything if the given GID are already the
3604 * same as the GIDs in the credential.
3606 if (pcred
->cr_groups
[0] == egid
&&
3607 pcred
->cr_rgid
== rgid
&&
3608 pcred
->cr_svgid
== svgid
) {
3609 /* no change needed */
3614 * Look up in cred hash table to see if we have a matching credential
3615 * with the new values; this is done by calling kauth_cred_update().
3617 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3618 if (egid
!= KAUTH_GID_NONE
) {
3619 /* displacing a supplementary group opts us out of memberd */
3620 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3621 DEBUG_CRED_CHANGE("displaced!\n");
3622 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3623 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3625 DEBUG_CRED_CHANGE("not displaced\n");
3628 if (rgid
!= KAUTH_GID_NONE
) {
3629 temp_pcred
->cr_rgid
= rgid
;
3631 if (svgid
!= KAUTH_GID_NONE
) {
3632 temp_pcred
->cr_svgid
= svgid
;
3635 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3640 * Update the given credential with the given groups. We only allocate a new
3641 * credential when the given gid actually results in changes to the existing
3643 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3644 * which will be used for group membership checking.
3647 * kauth_cred_setgroups
3649 * Description: Update the given credential using the provide supplementary
3650 * group list and group membership UID
3652 * Parameters: cred The original credential
3653 * groups Pointer to gid_t array which
3654 * contains the new group list
3655 * groupcount The cound of valid groups which
3656 * are contained in 'groups'
3657 * gmuid KAUTH_UID_NONE -or- the new
3658 * group membership UID
3660 * Returns: (kauth_cred_t) The updated credential
3662 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3663 * setting, so if you don't want it to change, pass it the
3664 * previous value, explicitly.
3666 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3667 * if it returns a credential other than the one it is passed,
3668 * will have dropped the reference on the passed credential. All
3669 * callers should be aware of this, and treat this function as an
3670 * unref + ref, potentially on different credentials.
3672 * Because of this, the caller is expected to take its own
3673 * reference on the credential passed as the first parameter,
3674 * and be prepared to release the reference on the credential
3675 * that is returned to them, if it is not intended to be a
3676 * persistent reference.
3678 * XXX: Changes are determined in ordinal order - if the caller pasess
3679 * in the same groups list that is already present in the
3680 * credential, but the members are in a different order, even if
3681 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3682 * is considered a modification to the credential, and a new
3683 * credential is created.
3685 * This should perhaps be better optimized, but it is considered
3686 * to be the caller's problem.
3689 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
3692 struct ucred temp_cred
;
3693 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3696 NULLCRED_CHECK(cred
);
3698 pcred
= posix_cred_get(cred
);
3701 * We don't need to do anything if the given list of groups does not
3704 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
3705 for (i
= 0; i
< groupcount
; i
++) {
3706 if (pcred
->cr_groups
[i
] != groups
[i
])
3709 if (i
== groupcount
) {
3710 /* no change needed */
3716 * Look up in cred hash table to see if we have a matching credential
3717 * with new values. If we are setting or clearing the gmuid, then
3718 * update the cr_flags, since clearing it is sticky. This permits an
3719 * opt-out of memberd processing using setgroups(), and an opt-in
3720 * using initgroups(). This is required for POSIX conformance.
3722 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3723 temp_pcred
->cr_ngroups
= groupcount
;
3724 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
3725 temp_pcred
->cr_gmuid
= gmuid
;
3726 if (gmuid
== KAUTH_UID_NONE
)
3727 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3729 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3731 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3735 * XXX temporary, for NFS support until we can come up with a better
3736 * XXX enumeration/comparison mechanism
3738 * Notes: The return value exists to account for the possbility of a
3739 * kauth_cred_t without a POSIX label. This will be the case in
3740 * the future (see posix_cred_get() below, for more details).
3743 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
3745 int limit
= NGROUPS
;
3748 * If they just want a copy of the groups list, they may not care
3749 * about the actual count. If they specify an input count, however,
3750 * treat it as an indicator of the buffer size available in grouplist,
3751 * and limit the returned list to that size.
3754 limit
= MIN(*countp
, cred
->cr_posix
.cr_ngroups
);
3758 memcpy(grouplist
, cred
->cr_posix
.cr_groups
, sizeof(gid_t
) * limit
);
3765 * kauth_cred_setuidgid
3767 * Description: Update the given credential using the UID and GID arguments.
3768 * The given UID is used to set the effective UID, real UID, and
3769 * saved UID. The given GID is used to set the effective GID,
3770 * real GID, and saved GID.
3772 * Parameters: cred The original credential
3773 * uid The new UID to use
3774 * gid The new GID to use
3776 * Returns: (kauth_cred_t) The updated credential
3778 * Notes: We set the gmuid to uid if the credential we are inheriting
3779 * from has not opted out of memberd participation; otherwise
3780 * we set it to KAUTH_UID_NONE
3782 * This code is only ever called from the per-thread credential
3783 * code path in the "set per thread credential" case; and in
3784 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3787 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3788 * if it returns a credential other than the one it is passed,
3789 * will have dropped the reference on the passed credential. All
3790 * callers should be aware of this, and treat this function as an
3791 * unref + ref, potentially on different credentials.
3793 * Because of this, the caller is expected to take its own
3794 * reference on the credential passed as the first parameter,
3795 * and be prepared to release the reference on the credential
3796 * that is returned to them, if it is not intended to be a
3797 * persistent reference.
3800 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3802 struct ucred temp_cred
;
3803 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3806 NULLCRED_CHECK(cred
);
3808 pcred
= posix_cred_get(cred
);
3811 * We don't need to do anything if the effective, real and saved
3812 * user IDs are already the same as the user ID passed into us.
3814 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
3815 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
3816 /* no change needed */
3821 * Look up in cred hash table to see if we have a matching credential
3822 * with the new values.
3824 bzero(&temp_cred
, sizeof(temp_cred
));
3825 temp_pcred
->cr_uid
= uid
;
3826 temp_pcred
->cr_ruid
= uid
;
3827 temp_pcred
->cr_svuid
= uid
;
3828 temp_pcred
->cr_flags
= pcred
->cr_flags
;
3829 /* inherit the opt-out of memberd */
3830 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3831 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3832 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3834 temp_pcred
->cr_gmuid
= uid
;
3835 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3837 temp_pcred
->cr_ngroups
= 1;
3838 /* displacing a supplementary group opts us out of memberd */
3839 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3840 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3841 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3843 temp_pcred
->cr_rgid
= gid
;
3844 temp_pcred
->cr_svgid
= gid
;
3846 temp_cred
.cr_label
= cred
->cr_label
;
3849 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3854 * kauth_cred_setsvuidgid
3856 * Description: Function used by execve to set the saved uid and gid values
3857 * for suid/sgid programs
3859 * Parameters: cred The credential to update
3860 * uid The saved uid to set
3861 * gid The saved gid to set
3863 * Returns: (kauth_cred_t) The updated credential
3865 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3866 * if it returns a credential other than the one it is passed,
3867 * will have dropped the reference on the passed credential. All
3868 * callers should be aware of this, and treat this function as an
3869 * unref + ref, potentially on different credentials.
3871 * Because of this, the caller is expected to take its own
3872 * reference on the credential passed as the first parameter,
3873 * and be prepared to release the reference on the credential
3874 * that is returned to them, if it is not intended to be a
3875 * persistent reference.
3878 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3880 struct ucred temp_cred
;
3881 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3884 NULLCRED_CHECK(cred
);
3886 pcred
= posix_cred_get(cred
);
3888 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
3891 * We don't need to do anything if the effective, real and saved
3892 * uids are already the same as the uid provided. This check is
3893 * likely insufficient.
3895 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
3896 /* no change needed */
3899 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3901 /* look up in cred hash table to see if we have a matching credential
3904 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3905 temp_pcred
->cr_svuid
= uid
;
3906 temp_pcred
->cr_svgid
= gid
;
3908 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3913 * kauth_cred_setauditinfo
3915 * Description: Update the given credential using the given au_session_t.
3917 * Parameters: cred The original credential
3918 * auditinfo_p Pointer to ne audit information
3920 * Returns: (kauth_cred_t) The updated credential
3922 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3923 * if it returns a credential other than the one it is passed,
3924 * will have dropped the reference on the passed credential. All
3925 * callers should be aware of this, and treat this function as an
3926 * unref + ref, potentially on different credentials.
3928 * Because of this, the caller is expected to take its own
3929 * reference on the credential passed as the first parameter,
3930 * and be prepared to release the reference on the credential
3931 * that is returned to them, if it is not intended to be a
3932 * persistent reference.
3935 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
3937 struct ucred temp_cred
;
3939 NULLCRED_CHECK(cred
);
3942 * We don't need to do anything if the audit info is already the
3943 * same as the audit info in the credential provided.
3945 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
3946 /* no change needed */
3950 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3951 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
3953 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
3958 * kauth_cred_label_update
3960 * Description: Update the MAC label associated with a credential
3962 * Parameters: cred The original credential
3963 * label The MAC label to set
3965 * Returns: (kauth_cred_t) The updated credential
3967 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3968 * if it returns a credential other than the one it is passed,
3969 * will have dropped the reference on the passed credential. All
3970 * callers should be aware of this, and treat this function as an
3971 * unref + ref, potentially on different credentials.
3973 * Because of this, the caller is expected to take its own
3974 * reference on the credential passed as the first parameter,
3975 * and be prepared to release the reference on the credential
3976 * that is returned to them, if it is not intended to be a
3977 * persistent reference.
3980 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
3982 kauth_cred_t newcred
;
3983 struct ucred temp_cred
;
3985 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3987 mac_cred_label_init(&temp_cred
);
3988 mac_cred_label_associate(cred
, &temp_cred
);
3989 mac_cred_label_update(&temp_cred
, label
);
3991 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
3992 mac_cred_label_destroy(&temp_cred
);
3997 * kauth_cred_label_update_execve
3999 * Description: Update the MAC label associated with a credential as
4002 * Parameters: cred The original credential
4004 * scriptl The script MAC label
4005 * execl The executable MAC label
4006 * disjointp Pointer to flag to set if old
4007 * and returned credentials are
4010 * Returns: (kauth_cred_t) The updated credential
4013 * *disjointp Set to 1 for disjoint creds
4015 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4016 * if it returns a credential other than the one it is passed,
4017 * will have dropped the reference on the passed credential. All
4018 * callers should be aware of this, and treat this function as an
4019 * unref + ref, potentially on different credentials.
4021 * Because of this, the caller is expected to take its own
4022 * reference on the credential passed as the first parameter,
4023 * and be prepared to release the reference on the credential
4024 * that is returned to them, if it is not intended to be a
4025 * persistent reference.
4029 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4030 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
4033 kauth_cred_t newcred
;
4034 struct ucred temp_cred
;
4036 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4038 mac_cred_label_init(&temp_cred
);
4039 mac_cred_label_associate(cred
, &temp_cred
);
4040 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
4041 vp
, scriptl
, execl
);
4043 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4044 mac_cred_label_destroy(&temp_cred
);
4049 * kauth_proc_label_update
4051 * Description: Update the label inside the credential associated with the process.
4053 * Parameters: p The process to modify
4054 * label The label to place in the process credential
4056 * Notes: The credential associated with the process may change as a result
4057 * of this call. The caller should not assume the process reference to
4058 * the old credential still exists.
4060 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4062 kauth_cred_t my_cred
, my_new_cred
;
4064 my_cred
= kauth_cred_proc_ref(p
);
4066 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4068 /* get current credential and take a reference while we muck with it */
4072 * Set the credential with new info. If there is no change,
4073 * we get back the same credential we passed in; if there is
4074 * a change, we drop the reference on the credential we
4075 * passed in. The subsequent compare is safe, because it is
4076 * a pointer compare rather than a contents compare.
4078 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4079 if (my_cred
!= my_new_cred
) {
4081 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
);
4085 * We need to protect for a race where another thread
4086 * also changed the credential after we took our
4087 * reference. If p_ucred has changed then we should
4088 * restart this again with the new cred.
4090 if (p
->p_ucred
!= my_cred
) {
4092 kauth_cred_unref(&my_new_cred
);
4093 my_cred
= kauth_cred_proc_ref(p
);
4097 p
->p_ucred
= my_new_cred
;
4098 /* update cred on proc */
4099 PROC_UPDATE_CREDS_ONPROC(p
);
4101 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4106 /* Drop old proc reference or our extra reference */
4107 kauth_cred_unref(&my_cred
);
4113 * kauth_proc_label_update_execve
4115 * Description: Update the label inside the credential associated with the
4116 * process as part of a transitioning execve. The label will
4117 * be updated by the policies as part of this processing, not
4118 * provided up front.
4120 * Parameters: p The process to modify
4121 * ctx The context of the exec
4122 * vp The vnode being exec'ed
4123 * scriptl The script MAC label
4124 * execl The executable MAC label
4126 * Returns: 0 Label update did not make credential
4128 * 1 Label update caused credential to be
4131 * Notes: The credential associated with the process WILL change as a
4132 * result of this call. The caller should not assume the process
4133 * reference to the old credential still exists.
4136 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4137 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
4139 kauth_cred_t my_cred
, my_new_cred
;
4142 my_cred
= kauth_cred_proc_ref(p
);
4144 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4146 /* get current credential and take a reference while we muck with it */
4150 * Set the credential with new info. If there is no change,
4151 * we get back the same credential we passed in; if there is
4152 * a change, we drop the reference on the credential we
4153 * passed in. The subsequent compare is safe, because it is
4154 * a pointer compare rather than a contents compare.
4156 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
4157 if (my_cred
!= my_new_cred
) {
4159 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
);
4163 * We need to protect for a race where another thread
4164 * also changed the credential after we took our
4165 * reference. If p_ucred has changed then we should
4166 * restart this again with the new cred.
4168 if (p
->p_ucred
!= my_cred
) {
4170 kauth_cred_unref(&my_new_cred
);
4171 my_cred
= kauth_cred_proc_ref(p
);
4175 p
->p_ucred
= my_new_cred
;
4176 /* update cred on proc */
4177 PROC_UPDATE_CREDS_ONPROC(p
);
4178 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4183 /* Drop old proc reference or our extra reference */
4184 kauth_cred_unref(&my_cred
);
4191 * for temporary binary compatibility
4193 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4195 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4197 return kauth_cred_label_update(cred
, label
);
4200 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4202 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4204 return kauth_proc_label_update(p
, label
);
4210 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4211 * Since we cannot build our export lists based on the kernel configuration we need
4215 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4221 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4228 * for temporary binary compatibility
4230 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4232 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4237 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4239 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4249 * Description: Add a reference to the passed credential
4251 * Parameters: cred The credential to reference
4255 * Notes: This function adds a reference to the provided credential;
4256 * the existing reference on the credential is assumed to be
4257 * held stable over this operation by taking the appropriate
4258 * lock to protect the pointer from which it is being referenced,
4259 * if necessary (e.g. the proc lock is held over the call if the
4260 * credential being referenced is from p_ucred, the vnode lock
4261 * if from the per vnode name cache cred cache, and so on).
4263 * This is safe from the kauth_cred_unref() path, since an atomic
4264 * add is used, and the unref path specifically checks to see that
4265 * the value has not been changed to add a reference between the
4266 * time the credential is unreferenced by another pointer and the
4267 * time it is unreferenced from the cred hash cache.
4270 kauth_cred_ref(kauth_cred_t cred
)
4274 NULLCRED_CHECK(cred
);
4276 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4279 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4281 #if 0 // use this to watch a specific credential
4282 if ( is_target_cred( cred
) != 0 ) {
4292 * kauth_cred_unref_hashlocked
4294 * Description: release a credential reference; when the last reference is
4295 * released, the credential will be freed.
4297 * Parameters: credp Pointer to address containing
4298 * credential to be freed
4303 * *credp Set to NOCRED
4305 * Notes: This function assumes the credential hash lock is held.
4307 * This function is internal use only, since the hash lock is
4308 * scoped to this compilation unit.
4310 * This function destroys the contents of the pointer passed by
4311 * the caller to prevent the caller accidently attempting to
4312 * release a given reference twice in error.
4314 * The last reference is considered to be released when a release
4315 * of a credential of a reference count of 2 occurs; this is an
4316 * intended effect, to take into accout the reference held by
4317 * the credential hash, which is released at the same time.
4320 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4324 KAUTH_CRED_HASH_LOCK_ASSERT();
4325 NULLCRED_CHECK(*credp
);
4327 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4331 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4333 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4336 #if 0 // use this to watch a specific credential
4337 if ( is_target_cred( *credp
) != 0 ) {
4343 * If the old_value is 2, then we have just released the last external
4344 * reference to this credential
4346 if (old_value
< 3) {
4347 /* The last absolute reference is our credential hash table */
4348 kauth_cred_remove(*credp
);
4357 * Description: Release a credential reference while holding the credential
4358 * hash lock; when the last reference is released, the credential
4361 * Parameters: credp Pointer to address containing
4362 * credential to be freed
4367 * *credp Set to NOCRED
4369 * Notes: See kauth_cred_unref_hashlocked() for more information.
4373 kauth_cred_unref(kauth_cred_t
*credp
)
4375 KAUTH_CRED_HASH_LOCK();
4376 kauth_cred_unref_hashlocked(credp
);
4377 KAUTH_CRED_HASH_UNLOCK();
4385 * Description: release a credential reference; when the last reference is
4386 * released, the credential will be freed
4388 * Parameters: cred Credential to release
4392 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4393 * clear the pointer in the caller to avoid multiple releases of
4394 * the same credential. The currently recommended interface is
4395 * kauth_cred_unref().
4398 kauth_cred_rele(kauth_cred_t cred
)
4400 kauth_cred_unref(&cred
);
4402 #endif /* !__LP64__ */
4408 * Description: Duplicate a credential via alloc and copy; the new credential
4411 * Parameters: cred The credential to duplicate
4413 * Returns: (kauth_cred_t) The duplicate credential
4415 * Notes: The typical value to calling this routine is if you are going
4416 * to modify an existing credential, and expect to need a new one
4417 * from the hash cache.
4419 * This should probably not be used in the majority of cases;
4420 * if you are using it instead of kauth_cred_create(), you are
4421 * likely making a mistake.
4423 * The newly allocated credential is copied as part of the
4424 * allocation process, with the exception of the reference
4425 * count, which is set to 1 to indicate a single reference
4426 * held by the caller.
4428 * Since newly allocated credentials have no external pointers
4429 * referencing them, prior to making them visible in an externally
4430 * visible pointer (e.g. by adding them to the credential hash
4431 * cache) is the only legal time in which an existing credential
4432 * can be safely iinitialized or modified directly.
4434 * After initialization, the caller is expected to call the
4435 * function kauth_cred_add() to add the credential to the hash
4436 * cache, after which time it's frozen and becomes publically
4439 * The release protocol depends on kauth_hash_add() being called
4440 * before kauth_cred_rele() (there is a diagnostic panic which
4441 * will trigger if this protocol is not observed).
4445 kauth_cred_dup(kauth_cred_t cred
)
4447 kauth_cred_t newcred
;
4449 struct label
*temp_label
;
4453 if (cred
== NOCRED
|| cred
== FSCRED
)
4454 panic("kauth_cred_dup: bad credential");
4456 newcred
= kauth_cred_alloc();
4457 if (newcred
!= NULL
) {
4459 temp_label
= newcred
->cr_label
;
4461 bcopy(cred
, newcred
, sizeof(*newcred
));
4463 newcred
->cr_label
= temp_label
;
4464 mac_cred_label_associate(cred
, newcred
);
4466 AUDIT_SESSION_REF(cred
);
4467 newcred
->cr_ref
= 1;
4473 * kauth_cred_copy_real
4475 * Description: Returns a credential based on the passed credential but which
4476 * reflects the real rather than effective UID and GID.
4478 * Parameters: cred The credential from which to
4479 * derive the new credential
4481 * Returns: (kauth_cred_t) The copied credential
4483 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4484 * result, the caller is responsible for dropping BOTH the
4485 * additional reference on the passed cred (if any), and the
4486 * credential returned by this function. The drop should be
4487 * via the satnadr kauth_cred_unref() KPI.
4490 kauth_cred_copy_real(kauth_cred_t cred
)
4492 kauth_cred_t newcred
= NULL
, found_cred
;
4493 struct ucred temp_cred
;
4494 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4495 posix_cred_t pcred
= posix_cred_get(cred
);
4497 /* if the credential is already 'real', just take a reference */
4498 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4499 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4500 kauth_cred_ref(cred
);
4505 * Look up in cred hash table to see if we have a matching credential
4506 * with the new values.
4508 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4509 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4510 /* displacing a supplementary group opts us out of memberd */
4511 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4512 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4513 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4516 * If the cred is not opted out, make sure we are using the r/euid
4519 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4520 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4525 KAUTH_CRED_HASH_LOCK();
4526 found_cred
= kauth_cred_find(&temp_cred
);
4527 if (found_cred
== cred
) {
4528 /* same cred so just bail */
4529 KAUTH_CRED_HASH_UNLOCK();
4532 if (found_cred
!= NULL
) {
4534 * Found a match so we bump reference count on new
4535 * one. We leave the old one alone.
4537 kauth_cred_ref(found_cred
);
4538 KAUTH_CRED_HASH_UNLOCK();
4543 * Must allocate a new credential, copy in old credential
4544 * data and update the real user and group IDs.
4546 newcred
= kauth_cred_dup(&temp_cred
);
4547 err
= kauth_cred_add(newcred
);
4548 KAUTH_CRED_HASH_UNLOCK();
4550 /* Retry if kauth_cred_add() fails */
4554 mac_cred_label_destroy(newcred
);
4556 AUDIT_SESSION_UNREF(newcred
);
4558 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4569 * Description: Common code to update a credential
4571 * Parameters: old_cred Reference counted credential
4573 * model_cred Non-reference counted model
4574 * credential to apply to the
4575 * credential to be updated
4576 * retain_auditinfo Flag as to whether or not the
4577 * audit information should be
4578 * copied from the old_cred into
4581 * Returns: (kauth_cred_t) The updated credential
4583 * IMPORTANT: This function will potentially return a credential other than
4584 * the one it is passed, and if so, it will have dropped the
4585 * reference on the passed credential. All callers should be
4586 * aware of this, and treat this function as an unref + ref,
4587 * potentially on different credentials.
4589 * Because of this, the caller is expected to take its own
4590 * reference on the credential passed as the first parameter,
4591 * and be prepared to release the reference on the credential
4592 * that is returned to them, if it is not intended to be a
4593 * persistent reference.
4596 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4597 boolean_t retain_auditinfo
)
4599 kauth_cred_t found_cred
, new_cred
= NULL
;
4602 * Make sure we carry the auditinfo forward to the new credential
4603 * unless we are actually updating the auditinfo.
4605 if (retain_auditinfo
) {
4606 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
4607 sizeof(model_cred
->cr_audit
));
4613 KAUTH_CRED_HASH_LOCK();
4614 found_cred
= kauth_cred_find(model_cred
);
4615 if (found_cred
== old_cred
) {
4616 /* same cred so just bail */
4617 KAUTH_CRED_HASH_UNLOCK();
4620 if (found_cred
!= NULL
) {
4621 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
4623 * Found a match so we bump reference count on new
4624 * one and decrement reference count on the old one.
4626 kauth_cred_ref(found_cred
);
4627 kauth_cred_unref_hashlocked(&old_cred
);
4628 KAUTH_CRED_HASH_UNLOCK();
4633 * Must allocate a new credential using the model. also
4634 * adds the new credential to the credential hash table.
4636 new_cred
= kauth_cred_dup(model_cred
);
4637 err
= kauth_cred_add(new_cred
);
4638 KAUTH_CRED_HASH_UNLOCK();
4640 /* retry if kauth_cred_add returns non zero value */
4644 mac_cred_label_destroy(new_cred
);
4646 AUDIT_SESSION_UNREF(new_cred
);
4648 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
4652 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
4653 kauth_cred_unref(&old_cred
);
4661 * Description: Add the given credential to our credential hash table and
4662 * take an additional reference to account for our use of the
4663 * credential in the hash table
4665 * Parameters: new_cred Credential to insert into cred
4668 * Returns: 0 Success
4669 * -1 Hash insertion failed: caller
4672 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4674 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4677 kauth_cred_add(kauth_cred_t new_cred
)
4681 KAUTH_CRED_HASH_LOCK_ASSERT();
4683 hash_key
= kauth_cred_get_hashkey(new_cred
);
4684 hash_key
%= kauth_cred_table_size
;
4686 /* race fix - there is a window where another matching credential
4687 * could have been inserted between the time this one was created and we
4688 * got the hash lock. If we find a match return an error and have the
4691 if (kauth_cred_find(new_cred
) != NULL
) {
4695 /* take a reference for our use in credential hash table */
4696 kauth_cred_ref(new_cred
);
4698 /* insert the credential into the hash table */
4699 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
4708 * Description: Remove the given credential from our credential hash table
4710 * Parameters: cred Credential to remove from cred
4715 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4717 * Notes: The check for the reference increment after entry is generally
4718 * agree to be safe, since we use atomic operations, and the
4719 * following code occurs with the hash lock held; in theory, this
4720 * protects us from the 2->1 reference that gets us here.
4723 kauth_cred_remove(kauth_cred_t cred
)
4726 kauth_cred_t found_cred
;
4728 hash_key
= kauth_cred_get_hashkey(cred
);
4729 hash_key
%= kauth_cred_table_size
;
4732 if (cred
->cr_ref
< 1)
4733 panic("cred reference underflow");
4734 if (cred
->cr_ref
> 1)
4735 return; /* someone else got a ref */
4737 /* Find cred in the credential hash table */
4738 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4739 if (found_cred
== cred
) {
4740 /* found a match, remove it from the hash table */
4741 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
4743 mac_cred_label_destroy(cred
);
4745 AUDIT_SESSION_UNREF(cred
);
4748 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
4749 #if KAUTH_CRED_HASH_DEBUG
4756 /* Did not find a match... this should not happen! XXX Make panic? */
4757 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
4765 * Description: Using the given credential data, look for a match in our
4766 * credential hash table
4768 * Parameters: cred Credential to lookup in cred
4771 * Returns: NULL Not found
4772 * !NULL Matching cedential already in
4775 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4778 kauth_cred_find(kauth_cred_t cred
)
4781 kauth_cred_t found_cred
;
4782 posix_cred_t pcred
= posix_cred_get(cred
);
4784 KAUTH_CRED_HASH_LOCK_ASSERT();
4786 #if KAUTH_CRED_HASH_DEBUG
4787 static int test_count
= 0;
4790 if ((test_count
% 200) == 0) {
4791 kauth_cred_hash_print();
4795 hash_key
= kauth_cred_get_hashkey(cred
);
4796 hash_key
%= kauth_cred_table_size
;
4798 /* Find cred in the credential hash table */
4799 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4801 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
4804 * don't worry about the label unless the flags in
4805 * either credential tell us to.
4807 if ((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0 ||
4808 (pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) {
4809 /* include the label pointer in the compare */
4810 match
= (bcmp(&found_pcred
->cr_uid
, &pcred
->cr_uid
,
4811 (sizeof(struct ucred
) -
4812 offsetof(struct ucred
, cr_posix
))) == 0);
4814 /* flags have to match, but skip the label in bcmp */
4815 match
= (found_pcred
->cr_flags
== pcred
->cr_flags
&&
4816 bcmp(&found_pcred
->cr_uid
, &pcred
->cr_uid
,
4817 sizeof(struct posix_cred
)) == 0 &&
4818 bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
4819 sizeof(cred
->cr_audit
)) == 0);
4827 /* No match found */
4836 * Description: Generates a hash key using data that makes up a credential;
4839 * Parameters: datap Pointer to data to hash
4840 * data_len Count of bytes to hash
4841 * start_key Start key value
4843 * Returns: (u_long) Returned hash key
4845 static inline u_long
4846 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4848 u_long hash_key
= start_key
;
4851 while (data_len
> 0) {
4852 hash_key
= (hash_key
<< 4) + *datap
++;
4853 temp
= hash_key
& 0xF0000000;
4855 hash_key
^= temp
>> 24;
4865 * kauth_cred_get_hashkey
4867 * Description: Generate a hash key using data that makes up a credential;
4868 * based on ElfHash. We hash on the entire credential data,
4869 * not including the ref count or the TAILQ, which are mutable;
4870 * everything else isn't.
4872 * Parameters: cred Credential for which hash is
4875 * Returns: (u_long) Returned hash key
4877 * Notes: When actually moving the POSIX credential into a real label,
4878 * remember to update this hash computation.
4881 kauth_cred_get_hashkey(kauth_cred_t cred
)
4883 posix_cred_t pcred
= posix_cred_get(cred
);
4884 u_long hash_key
= 0;
4886 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
4887 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
4888 sizeof(struct ucred
) - offsetof(struct ucred
, cr_posix
),
4892 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
4893 sizeof(struct posix_cred
),
4895 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
4896 sizeof(struct au_session
),
4903 #if KAUTH_CRED_HASH_DEBUG
4905 * kauth_cred_hash_print
4907 * Description: Print out cred hash cache table information for debugging
4908 * purposes, including the credential contents
4910 * Parameters: (void)
4914 * Implicit returns: Results in console output
4917 kauth_cred_hash_print(void)
4920 kauth_cred_t found_cred
;
4922 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
4923 /* count slot hits, misses, collisions, and max depth */
4924 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
4925 printf("[%02d] ", i
);
4927 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
4932 kauth_cred_print(found_cred
);
4936 printf("NOCRED \n");
4940 #endif /* KAUTH_CRED_HASH_DEBUG */
4943 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4947 * Description: Print out an individual credential's contents for debugging
4950 * Parameters: cred The credential to print out
4954 * Implicit returns: Results in console output
4957 kauth_cred_print(kauth_cred_t cred
)
4961 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
);
4962 printf("group count %d gids ", cred
->cr_ngroups
);
4963 for (i
= 0; i
< NGROUPS
; i
++) {
4966 printf("%d ", cred
->cr_groups
[i
]);
4968 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
4969 printf("auditinfo_addr %d %d %d %d %d %d\n",
4970 cred
->cr_audit
.s_aia_p
->ai_auid
,
4971 cred
->cr_audit
.as_mask
.am_success
,
4972 cred
->cr_audit
.as_mask
.am_failure
,
4973 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
4974 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
4975 cred
->cr_audit
.as_aia_p
->ai_asid
);
4978 int is_target_cred( kauth_cred_t the_cred
)
4980 if ( the_cred
->cr_uid
!= 0 )
4982 if ( the_cred
->cr_ruid
!= 0 )
4984 if ( the_cred
->cr_svuid
!= 0 )
4986 if ( the_cred
->cr_ngroups
!= 11 )
4988 if ( the_cred
->cr_groups
[0] != 11 )
4990 if ( the_cred
->cr_groups
[1] != 81 )
4992 if ( the_cred
->cr_groups
[2] != 63947 )
4994 if ( the_cred
->cr_groups
[3] != 80288 )
4996 if ( the_cred
->cr_groups
[4] != 89006 )
4998 if ( the_cred
->cr_groups
[5] != 52173 )
5000 if ( the_cred
->cr_groups
[6] != 84524 )
5002 if ( the_cred
->cr_groups
[7] != 79 )
5004 if ( the_cred
->cr_groups
[8] != 80292 )
5006 if ( the_cred
->cr_groups
[9] != 80 )
5008 if ( the_cred
->cr_groups
[10] != 90824 )
5010 if ( the_cred
->cr_rgid
!= 11 )
5012 if ( the_cred
->cr_svgid
!= 11 )
5014 if ( the_cred
->cr_gmuid
!= 3475 )
5016 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5019 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5021 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5023 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5025 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5027 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5029 if ( the_cred->cr_flags != 0 )
5032 return( -1 ); // found target cred
5035 void get_backtrace( void )
5038 void * my_stack
[ MAX_STACK_DEPTH
];
5041 if ( cred_debug_buf_p
== NULL
) {
5042 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5043 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5046 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5047 /* buffer is full */
5051 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5052 if ( my_depth
== 0 ) {
5053 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5057 /* fill new backtrace */
5058 my_slot
= cred_debug_buf_p
->next_slot
;
5059 cred_debug_buf_p
->next_slot
++;
5060 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5061 for ( i
= 0; i
< my_depth
; i
++ ) {
5062 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5069 /* subset of struct ucred for use in sysctl_dump_creds */
5070 struct debug_ucred
{
5072 u_long cr_ref
; /* reference count */
5073 uid_t cr_uid
; /* effective user id */
5074 uid_t cr_ruid
; /* real user id */
5075 uid_t cr_svuid
; /* saved user id */
5076 short cr_ngroups
; /* number of groups in advisory list */
5077 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5078 gid_t cr_rgid
; /* real group id */
5079 gid_t cr_svgid
; /* saved group id */
5080 uid_t cr_gmuid
; /* UID for group membership purposes */
5081 struct auditinfo_addr cr_audit
; /* user auditing data. */
5082 void *cr_label
; /* MACF label */
5083 int cr_flags
; /* flags on credential */
5085 typedef struct debug_ucred debug_ucred
;
5087 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5088 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5091 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5095 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5097 int i
, j
, counter
= 0;
5100 kauth_cred_t found_cred
;
5101 debug_ucred
* cred_listp
;
5102 debug_ucred
* nextp
;
5104 /* This is a readonly node. */
5105 if (req
->newptr
!= USER_ADDR_NULL
)
5108 /* calculate space needed */
5109 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5110 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5115 /* they are querying us so just return the space required. */
5116 if (req
->oldptr
== USER_ADDR_NULL
) {
5117 counter
+= 10; // add in some padding;
5118 req
->oldidx
= counter
* sizeof(debug_ucred
);
5122 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5123 if ( cred_listp
== NULL
) {
5127 /* fill in creds to send back */
5130 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5131 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5132 nextp
->credp
= found_cred
;
5133 nextp
->cr_ref
= found_cred
->cr_ref
;
5134 nextp
->cr_uid
= found_cred
->cr_uid
;
5135 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5136 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5137 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5138 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5139 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5141 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5142 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5143 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5144 nextp
->cr_audit
.ai_auid
=
5145 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5146 nextp
->cr_audit
.ai_mask
.am_success
=
5147 found_cred
->cr_audit
.as_mask
.am_success
;
5148 nextp
->cr_audit
.ai_mask
.am_failure
=
5149 found_cred
->cr_audit
.as_mask
.am_failure
;
5150 nextp
->cr_audit
.ai_termid
.at_port
=
5151 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5152 nextp
->cr_audit
.ai_termid
.at_type
=
5153 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5154 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5155 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5156 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5157 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5158 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5159 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5160 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5161 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5162 nextp
->cr_audit
.ai_asid
=
5163 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5164 nextp
->cr_audit
.ai_flags
=
5165 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5166 nextp
->cr_label
= found_cred
->cr_label
;
5167 nextp
->cr_flags
= found_cred
->cr_flags
;
5169 space
+= sizeof(debug_ucred
);
5170 if ( space
> req
->oldlen
) {
5171 FREE(cred_listp
, M_TEMP
);
5176 req
->oldlen
= space
;
5177 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5178 FREE(cred_listp
, M_TEMP
);
5183 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5184 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5187 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5191 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5196 cred_debug_buffer
* bt_bufp
;
5197 cred_backtrace
* nextp
;
5199 /* This is a readonly node. */
5200 if (req
->newptr
!= USER_ADDR_NULL
)
5203 if ( cred_debug_buf_p
== NULL
) {
5207 /* calculate space needed */
5208 space
= sizeof( cred_debug_buf_p
->next_slot
);
5209 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5211 /* they are querying us so just return the space required. */
5212 if (req
->oldptr
== USER_ADDR_NULL
) {
5213 req
->oldidx
= space
;
5217 if ( space
> req
->oldlen
) {
5221 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5222 if ( bt_bufp
== NULL
) {
5226 /* fill in backtrace info to send back */
5227 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5228 space
= sizeof(bt_bufp
->next_slot
);
5230 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5231 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5232 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5233 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5234 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5236 space
+= sizeof(*nextp
);
5239 req
->oldlen
= space
;
5240 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5241 FREE(bt_bufp
, M_TEMP
);
5245 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5249 **********************************************************************
5250 * The following routines will be moved to a policy_posix.c module at
5251 * some future point.
5252 **********************************************************************
5258 * Description: Helper function to create a kauth_cred_t credential that is
5259 * initally labelled with a specific POSIX credential label
5261 * Parameters: pcred The posix_cred_t to use as the initial
5264 * Returns: (kauth_cred_t) The credential that was found in the
5266 * NULL kauth_cred_add() failed, or there was
5267 * no egid specified, or we failed to
5268 * attach a label to the new credential
5270 * Notes: This function currently wraps kauth_cred_create(), and is the
5271 * only consume of tht ill-fated function, apart from bsd_init().
5272 * It exists solely to support the NFS server code creation of
5273 * credentials based on the over-the-wire RPC cals containing
5274 * traditional POSIX credential information being tunneled to
5275 * the server host from the client machine.
5277 * In the future, we hope this function goes away.
5279 * In the short term, it creates a temporary credential, puts
5280 * the POSIX information from NFS into it, and then calls
5281 * kauth_cred_create(), as an internal implementaiton detail.
5283 * If we have to keep it around in the medium term, it will
5284 * create a new kauth_cred_t, then label it with a POSIX label
5285 * corresponding to the contents of the kauth_cred_t. If the
5286 * policy_posix MACF module is not loaded, it will instead
5287 * substitute a posix_cred_t which GRANTS all access (effectively
5288 * a "root" credential) in order to not prevent NFS from working
5289 * in the case that we are not supporting POSIX credentials.
5292 posix_cred_create(posix_cred_t pcred
)
5294 struct ucred temp_cred
;
5296 bzero(&temp_cred
, sizeof(temp_cred
));
5297 temp_cred
.cr_posix
= *pcred
;
5299 return kauth_cred_create(&temp_cred
);
5306 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5307 * any, which is associated with it.
5309 * Parameters: cred The credential to obtain the label from
5311 * Returns: posix_cred_t The POSIX credential label
5313 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5314 * this function will return a pointer to a posix_cred_t which
5315 * GRANTS all access (effectively, a "root" credential). This is
5316 * necessary to support legacy code which insists on tightly
5317 * integrating POSIX credentails into its APIs, including, but
5318 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5319 * NFSv3, signals, dtrace, and a large number of kauth routines
5320 * used to implement POSIX permissions related system calls.
5322 * In the event that the policy_posix MACF module IS loaded, and
5323 * there is no POSIX label on the kauth_cred_t credential, this
5324 * function will return a pointer to a posix_cred_t which DENIES
5325 * all access (effectively, a "deny rights granted by POSIX"
5326 * credential). This is necessary to support the concept of a
5327 * transiently loaded POSIX policy, or kauth_cred_t credentials
5328 * which can not be used in conjunctions with POSIX permissions
5331 * This function currently returns the address of the cr_posix
5332 * field of the supplied kauth_cred_t credential, and as such
5333 * currently can not fail. In the future, this will not be the
5337 posix_cred_get(kauth_cred_t cred
)
5339 return(&cred
->cr_posix
);
5346 * Description: Label a kauth_cred_t with a POSIX credential label
5348 * Parameters: cred The credential to label
5349 * pcred The POSIX credential t label it with
5353 * Notes: This function is currently void in order to permit it to fit
5354 * in with the currrent MACF framework label methods which allow
5355 * labelling to fail silently. This is like acceptable for
5356 * mandatory access controls, but not for POSIX, since those
5357 * access controls are advisory. We will need to consider a
5358 * return value in a future version of the MACF API.
5360 * This operation currenty can not fail, as currently the POSIX
5361 * credential is a subfield of the kauth_cred_t (ucred), which
5362 * MUST be valid. In the future, this will not be the case.
5365 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5367 cred
->cr_posix
= *pcred
; /* structure assign for now */
5374 * Description: Perform a POSIX access check for a protected object
5376 * Parameters: cred The credential to check
5377 * object_uid The POSIX UID of the protected object
5378 * object_gid The POSIX GID of the protected object
5379 * object_mode The POSIX mode of the protected object
5380 * mode_req The requested POSIX access rights
5382 * Returns 0 Access is granted
5383 * EACCES Access is denied
5385 * Notes: This code optimizes the case where the world and group rights
5386 * would both grant the requested rights to avoid making a group
5387 * membership query. This is a big performance win in the case
5388 * where this is true.
5391 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5394 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5395 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5396 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5399 * Check first for owner rights
5401 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5405 * Combined group and world rights check, if we don't have owner rights
5407 * OPTIMIZED: If group and world rights would grant the same bits, and
5408 * they set of requested bits is in both, then we can simply check the
5409 * world rights, avoiding a group membership check, which is expensive.
5411 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5415 * NON-OPTIMIZED: requires group membership check.
5417 if ((mode_req
& mode_group
) != mode_req
) {
5419 * exclusion group : treat errors as "is a member"
5421 * NON-OPTIMIZED: +group would deny; must check group
5423 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5425 * DENY: +group denies
5429 if ((mode_req
& mode_world
) != mode_req
) {
5431 * DENY: both -group & world would deny
5436 * ALLOW: allowed by -group and +world
5443 * inclusion group; treat errors as "not a member"
5445 * NON-OPTIMIZED: +group allows, world denies; must
5448 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5450 * ALLOW: allowed by +group
5454 if ((mode_req
& mode_world
) != mode_req
) {
5456 * DENY: both -group & world would deny
5461 * ALLOW: allowed by -group and +world