2 * Copyright (c) 2004-2020 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>
53 #include <security/audit/audit.h>
55 #include <sys/mount.h>
56 #include <sys/stat.h> /* For manifest constants in posix_cred_access */
57 #include <sys/sysproto.h>
58 #include <mach/message.h>
59 #include <mach/host_security.h>
61 #include <machine/atomic.h>
63 #include <kern/task.h>
64 #include <kern/locks.h>
68 #define MACH_ASSERT 1 /* XXX so bogus */
69 #include <kern/assert.h>
72 #include <security/mac.h>
73 #include <security/mac_framework.h>
74 #include <security/_label.h>
77 #include <IOKit/IOBSD.h>
79 void mach_kauth_cred_uthread_update( void );
81 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
83 /* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */
97 # define K_UUID_FMT "%08x:%08x:%08x:%08x"
98 # define K_UUID_ARG(_u) &_u.g_guid_asint[0],&_u.g_guid_asint[1],&_u.g_guid_asint[2],&_u.g_guid_asint[3]
99 # define KAUTH_DEBUG(fmt, args...) do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0)
103 * Credential debugging; we can track entry into a function that might
104 * change a credential, and we can track actual credential changes that
107 * Note: Does *NOT* currently include per-thread credential changes
111 #define DEBUG_CRED_ENTER printf
112 #define DEBUG_CRED_CHANGE printf
113 extern void kauth_cred_print(kauth_cred_t cred
);
115 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
117 int is_target_cred( kauth_cred_t the_cred
);
118 void get_backtrace( void );
120 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
121 __unused
int arg2
, struct sysctl_req
*req
);
123 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
124 __unused
int arg2
, struct sysctl_req
*req
);
126 #define MAX_STACK_DEPTH 8
127 struct cred_backtrace
{
129 void * stack
[MAX_STACK_DEPTH
];
131 typedef struct cred_backtrace cred_backtrace
;
133 #define MAX_CRED_BUFFER_SLOTS 200
134 struct cred_debug_buffer
{
136 cred_backtrace stack_buffer
[MAX_CRED_BUFFER_SLOTS
];
138 typedef struct cred_debug_buffer cred_debug_buffer
;
139 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
141 #else /* !DEBUG_CRED */
143 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
144 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
146 #endif /* !DEBUG_CRED */
148 #if CONFIG_EXT_RESOLVER
150 * Interface to external identity resolver.
152 * The architecture of the interface is simple; the external resolver calls
153 * in to get work, then calls back with completed work. It also calls us
154 * to let us know that it's (re)started, so that we can resubmit work if it
158 static lck_mtx_t
*kauth_resolver_mtx
;
159 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
160 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
162 static volatile pid_t kauth_resolver_identity
;
163 static int kauth_identitysvc_has_registered
;
164 static int kauth_resolver_registered
;
165 static uint32_t kauth_resolver_sequence
;
166 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
168 struct kauth_resolver_work
{
169 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
170 struct kauth_identity_extlookup kr_work
;
175 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
176 #define KAUTH_REQUEST_SUBMITTED (1<<1)
177 #define KAUTH_REQUEST_DONE (1<<2)
181 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
182 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
183 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
185 /* Number of resolver timeouts between logged complaints */
186 #define KAUTH_COMPLAINT_INTERVAL 1000
187 int kauth_resolver_timeout_cnt
= 0;
189 #if DEVELOPMENT || DEBUG
190 /* Internal builds get different (less ambiguous) breadcrumbs. */
191 #define KAUTH_RESOLVER_FAILED_ERRCODE EOWNERDEAD
193 /* But non-Internal builds get errors that are allowed by standards. */
194 #define KAUTH_RESOLVER_FAILED_ERRCODE EIO
195 #endif /* DEVELOPMENT || DEBUG */
197 int kauth_resolver_failed_cnt
= 0;
198 #define RESOLVER_FAILED_MESSAGE(fmt, args...) \
200 if (!(kauth_resolver_failed_cnt++ % 100)) { \
201 printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##args); \
205 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
206 static int kauth_resolver_complete(user_addr_t message
);
207 static int kauth_resolver_getwork(user_addr_t message
);
208 static int kauth_resolver_getwork2(user_addr_t message
);
209 static __attribute__((noinline
)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
210 struct kauth_resolver_work
*);
212 #define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
214 struct kauth_identity
{
215 TAILQ_ENTRY(kauth_identity
) ki_link
;
219 uint32_t ki_supgrpcnt
;
220 gid_t ki_supgrps
[NGROUPS
];
223 const char *ki_name
; /* string name from string cache */
225 * Expiry times are the earliest time at which we will disregard the
226 * cached state and go to userland. Before then if the valid bit is
227 * set, we will return the cached value. If it's not set, we will
228 * not go to userland to resolve, just assume that there is no answer
231 time_t ki_groups_expiry
;
232 time_t ki_guid_expiry
;
233 time_t ki_ntsid_expiry
;
236 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
237 static lck_mtx_t
*kauth_identity_mtx
;
238 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
239 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
240 #define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
241 static int kauth_identity_cachemax
= KAUTH_IDENTITY_CACHEMAX_DEFAULT
;
242 static int kauth_identity_count
;
244 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
245 ntsid_t
*ntsidp
, time_t ntsid_expiry
, size_t supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
246 const char *name
, int nametype
);
247 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
248 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
249 static void kauth_identity_trimcache(int newsize
);
250 static void kauth_identity_lru(struct kauth_identity
*kip
);
251 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
252 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
253 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
254 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
255 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
256 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
257 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
259 struct kauth_group_membership
{
260 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
261 uid_t gm_uid
; /* the identity whose membership we're recording */
262 gid_t gm_gid
; /* group of which they are a member */
263 time_t gm_expiry
; /* TTL for the membership, or 0 for persistent entries */
265 #define KAUTH_GROUP_ISMEMBER (1<<0)
268 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
269 static lck_mtx_t
*kauth_groups_mtx
;
270 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
271 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
272 #define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
273 static int kauth_groups_cachemax
= KAUTH_GROUPS_CACHEMAX_DEFAULT
;
274 static int kauth_groups_count
;
276 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
277 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
278 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
279 static void kauth_groups_trimcache(int newsize
);
281 #endif /* CONFIG_EXT_RESOLVER */
283 #define KAUTH_CRED_TABLE_SIZE 128
285 ZONE_DECLARE(ucred_zone
, "cred", sizeof(struct ucred
), ZC_ZFREE_CLEARMEM
);
286 LIST_HEAD(kauth_cred_entry_head
, ucred
);
287 static struct kauth_cred_entry_head
288 kauth_cred_table_anchor
[KAUTH_CRED_TABLE_SIZE
];
290 static struct kauth_cred_entry_head
*kauth_cred_get_bucket(kauth_cred_t cred
);
291 static kauth_cred_t
kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
);
292 static void kauth_cred_remove_locked(kauth_cred_t cred
);
293 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
294 static kauth_cred_t
kauth_cred_find_and_ref(kauth_cred_t cred
,
295 struct kauth_cred_entry_head
*bucket
);
296 static bool kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
);
298 #if CONFIG_EXT_RESOLVER
301 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
303 * Description: Waits for the user space daemon to respond to the request
304 * we made. Function declared non inline to be visible in
305 * stackshots and spindumps as well as debugging.
307 * Parameters: workp Work queue entry.
309 * Returns: 0 on Success.
310 * EIO if Resolver is dead.
311 * EINTR thread interrupted in msleep
312 * EWOULDBLOCK thread timed out in msleep
313 * ERESTART returned by msleep.
316 static __attribute__((noinline
)) int
317 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
318 struct kauth_resolver_work
*workp
)
323 /* we could compute a better timeout here */
324 ts
.tv_sec
= kauth_resolver_timeout
;
326 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
327 /* request has been completed? */
328 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
)) {
331 /* woken because the resolver has died? */
332 if (kauth_resolver_identity
== 0) {
333 RESOLVER_FAILED_MESSAGE("kauth external resolver died while while waiting for work to complete");
334 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
347 * kauth_resolver_init
349 * Description: Initialize the daemon side of the credential identity resolver
355 * Notes: Initialize the credential identity resolver for use; the
356 * credential identity resolver is the KPI used by the user
357 * space credential identity resolver daemon to communicate
358 * with the kernel via the identitysvc() system call..
360 * This is how membership in more than 16 groups (1 effective
361 * and 15 supplementary) is supported, and also how UID's,
362 * UUID's, and so on, are translated to/from POSIX credential
365 * The credential identity resolver operates by attempting to
366 * determine identity first from the credential, then from
367 * the kernel credential identity cache, and finally by
368 * enqueueing a request to a user space daemon.
370 * This function is called from kauth_init() in the file
371 * kern_authorization.c.
374 kauth_resolver_init(void)
376 TAILQ_INIT(&kauth_resolver_unsubmitted
);
377 TAILQ_INIT(&kauth_resolver_submitted
);
378 TAILQ_INIT(&kauth_resolver_done
);
379 kauth_resolver_sequence
= 31337;
380 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
384 * kauth_resolver_identity_reset
386 * Description: Reset the identity of the external resolver in certain
387 * controlled circumstances.
394 kauth_resolver_identity_reset(void)
396 KAUTH_RESOLVER_LOCK();
397 if (kauth_resolver_identity
!= 0) {
398 printf("kauth external resolver %d failed to de-register.\n",
399 kauth_resolver_identity
);
400 kauth_resolver_identity
= 0;
401 kauth_resolver_registered
= 0;
403 KAUTH_RESOLVER_UNLOCK();
407 * kauth_resolver_submit
409 * Description: Submit an external credential identity resolution request to
410 * the user space daemon.
412 * Parameters: lkp A pointer to an external
414 * extend_data extended data for kr_extend
417 * EWOULDBLOCK No resolver registered
418 * EINTR Operation interrupted (e.g. by
420 * ENOMEM Could not allocate work item
421 * copyinstr:EFAULT Bad message from user space
422 * workp->kr_result:??? An error from the user space
423 * daemon (includes ENOENT!)
428 * Notes: Allocate a work queue entry, submit the work and wait for
429 * the operation to either complete or time out. Outstanding
430 * operations may also be cancelled.
432 * Submission is by means of placing the item on a work queue
433 * which is serviced by an external resolver thread calling
434 * into the kernel. The caller then sleeps until timeout,
435 * cancellation, or an external resolver thread calls in with
436 * a result message to kauth_resolver_complete(). All of these
437 * events wake the caller back up.
439 * This code is called from either kauth_cred_ismember_gid()
440 * for a group membership request, or it is called from
441 * kauth_cred_cache_lookup() when we get a cache miss.
444 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
446 struct kauth_resolver_work
*workp
, *killp
;
448 int error
, shouldfree
;
450 /* no point actually blocking if the resolver isn't up yet */
451 if (kauth_resolver_identity
== 0) {
453 * We've already waited an initial <kauth_resolver_timeout>
454 * seconds with no result.
456 * Sleep on a stack address so no one wakes us before timeout;
457 * we sleep a half a second in case we are a high priority
458 * process, so that memberd doesn't starve while we are in a
459 * tight loop between user and kernel, eating all the CPU.
461 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/ 2);
462 if (kauth_resolver_identity
== 0) {
464 * if things haven't changed while we were asleep,
465 * tell the caller we couldn't get an authoritative
472 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
477 workp
->kr_work
= *lkp
;
478 workp
->kr_extend
= extend_data
;
480 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
481 workp
->kr_result
= 0;
484 * We insert the request onto the unsubmitted queue, the call in from
485 * the resolver will it to the submitted thread when appropriate.
487 KAUTH_RESOLVER_LOCK();
488 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
489 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
492 * XXX We *MUST NOT* attempt to coalesce identical work items due to
493 * XXX the inability to ensure order of update of the request item
494 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
495 * XXX for each item repeat the update when they wake up.
497 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
500 * Wake up an external resolver thread to deal with the new work; one
501 * may not be available, and if not, then the request will be grabbed
502 * when a resolver thread comes back into the kernel to request new
505 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
506 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
508 /* if the request was processed, copy the result */
510 *lkp
= workp
->kr_work
;
513 if (error
== EWOULDBLOCK
) {
514 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
515 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
516 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
519 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
521 * If the request timed out and was never collected, the resolver
522 * is dead and probably not coming back anytime soon. In this
523 * case we revert to no-resolver behaviour, and punt all the other
524 * sleeping requests to clear the backlog.
526 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
529 * Make the current resolver non-authoritative, and mark it as
530 * no longer registered to prevent kauth_cred_ismember_gid()
531 * enqueueing more work until a new one is registered. This
532 * mitigates the damage a crashing resolver may inflict.
534 kauth_resolver_identity
= 0;
535 kauth_resolver_registered
= 0;
537 /* kill all the other requestes that are waiting as well */
538 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
540 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
542 /* Cause all waiting-for-work threads to return EIO */
543 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
548 * drop our reference on the work item, and note whether we should
551 if (--workp
->kr_refs
<= 0) {
552 /* work out which list we have to remove it from */
553 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
554 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
555 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
556 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
557 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
558 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
560 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
564 /* someone else still has a reference on this request */
568 /* collect request result */
570 error
= workp
->kr_result
;
572 KAUTH_RESOLVER_UNLOCK();
575 * If we dropped the last reference, free the request.
578 FREE(workp
, M_KAUTH
);
581 KAUTH_DEBUG("RESOLVER - returning %d", error
);
589 * Description: System call interface for the external identity resolver.
591 * Parameters: uap->message Message from daemon to kernel
593 * Returns: 0 Successfully became resolver
594 * EPERM Not the resolver process
595 * kauth_authorize_generic:EPERM Not root user
596 * kauth_resolver_complete:EIO
597 * kauth_resolver_complete:EFAULT
598 * kauth_resolver_getwork:EINTR
599 * kauth_resolver_getwork:EFAULT
601 * Notes: This system call blocks until there is work enqueued, at
602 * which time the kernel wakes it up, and a message from the
603 * kernel is copied out to the identity resolution daemon, which
604 * proceed to attempt to resolve it. When the resolution has
605 * completed (successfully or not), the daemon called back into
606 * this system call to give the result to the kernel, and wait
607 * for the next request.
610 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
612 int opcode
= uap
->opcode
;
613 user_addr_t message
= uap
->message
;
614 struct kauth_resolver_work
*workp
;
615 struct kauth_cache_sizes sz_arg
= {};
619 if (!IOTaskHasEntitlement(current_task(), IDENTITYSVC_ENTITLEMENT
)) {
620 KAUTH_DEBUG("RESOLVER - pid %d not entitled to call identitysvc", current_proc()->p_pid
);
625 * New server registering itself.
627 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
628 new_id
= current_proc()->p_pid
;
629 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
630 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
633 KAUTH_RESOLVER_LOCK();
634 if (kauth_resolver_identity
!= new_id
) {
635 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
637 * We have a new server, so assume that all the old requests have been lost.
639 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
640 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
641 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
642 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
643 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
646 * Allow user space resolver to override the
647 * external resolution timeout
649 if (message
> 30 && message
< 10000) {
650 kauth_resolver_timeout
= (int)message
;
651 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
653 kauth_resolver_identity
= new_id
;
654 kauth_resolver_registered
= 1;
655 kauth_identitysvc_has_registered
= 1;
656 wakeup(&kauth_resolver_unsubmitted
);
658 KAUTH_RESOLVER_UNLOCK();
663 * Beyond this point, we must be the resolver process. We verify this
664 * by confirming the resolver credential and pid.
666 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
667 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
671 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
672 KAUTH_IDENTITY_LOCK();
673 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
674 KAUTH_IDENTITY_UNLOCK();
677 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
678 KAUTH_GROUPS_UNLOCK();
680 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof(sz_arg
))) != 0) {
685 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
686 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof(sz_arg
))) != 0) {
690 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
691 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
695 KAUTH_IDENTITY_LOCK();
696 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
697 kauth_identity_trimcache(kauth_identity_cachemax
);
698 KAUTH_IDENTITY_UNLOCK();
701 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
702 kauth_groups_trimcache(kauth_groups_cachemax
);
703 KAUTH_GROUPS_UNLOCK();
706 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
707 KAUTH_IDENTITY_LOCK();
708 kauth_identity_trimcache(0);
709 KAUTH_IDENTITY_UNLOCK();
712 kauth_groups_trimcache(0);
713 KAUTH_GROUPS_UNLOCK();
714 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
716 * Terminate outstanding requests; without an authoritative
717 * resolver, we are now back on our own authority.
719 struct kauth_resolver_work
*killp
;
721 KAUTH_RESOLVER_LOCK();
724 * Clear the identity, but also mark it as unregistered so
725 * there is no explicit future expectation of us getting a
726 * new resolver any time soon.
728 kauth_resolver_identity
= 0;
729 kauth_resolver_registered
= 0;
731 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
733 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
735 /* Cause all waiting-for-work threads to return EIO */
736 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
737 KAUTH_RESOLVER_UNLOCK();
741 * Got a result returning?
743 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
744 if ((error
= kauth_resolver_complete(message
)) != 0) {
750 * Caller wants to take more work?
752 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
753 if ((error
= kauth_resolver_getwork(message
)) != 0) {
763 * kauth_resolver_getwork_continue
765 * Description: Continuation for kauth_resolver_getwork
767 * Parameters: result Error code or 0 for the sleep
768 * that got us to this function
771 * EINTR Interrupted (e.g. by signal)
772 * kauth_resolver_getwork2:EFAULT
774 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
778 kauth_resolver_getwork_continue(int result
)
785 KAUTH_RESOLVER_UNLOCK();
790 * If we lost a race with another thread/memberd restarting, then we
791 * need to go back to sleep to look for more work. If it was memberd
792 * restarting, then the msleep0() will error out here, as our thread
793 * will already be "dead".
795 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
798 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
800 * If this is a wakeup from another thread in the resolver
801 * deregistering it, error out the request-for-work thread
803 if (!kauth_resolver_identity
) {
804 RESOLVER_FAILED_MESSAGE("external resolver died");
805 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
807 KAUTH_RESOLVER_UNLOCK();
811 thread
= current_thread();
812 ut
= get_bsdthread_info(thread
);
813 message
= ut
->uu_save
.uus_kauth
.message
;
814 return kauth_resolver_getwork2(message
);
819 * kauth_resolver_getwork2
821 * Decription: Common utility function to copy out a identity resolver work
822 * item from the kernel to user space as part of the user space
823 * identity resolver requesting work.
825 * Parameters: message message to user space
828 * EFAULT Bad user space message address
830 * Notes: This common function exists to permit the use of continuations
831 * in the identity resolution process. This frees up the stack
832 * while we are waiting for the user space resolver to complete
833 * a request. This is specifically used so that our per thread
834 * cost can be small, and we will therefore be willing to run a
835 * larger number of threads in the user space identity resolver.
838 kauth_resolver_getwork2(user_addr_t message
)
840 struct kauth_resolver_work
*workp
;
844 * Note: We depend on the caller protecting us from a NULL work item
845 * queue, since we must have the kauth resolver lock on entry to this
848 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
851 * Copy out the external lookup structure for the request, not
852 * including the el_extend field, which contains the address of the
853 * external buffer provided by the external resolver into which we
854 * copy the extension request information.
857 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
858 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
862 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
863 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
864 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
865 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
870 * Handle extended requests here; if we have a request of a type where
871 * the kernel wants a translation of extended information, then we need
872 * to copy it out into the extended buffer, assuming the buffer is
873 * valid; we only attempt to get the buffer address if we have request
874 * data to copy into it.
878 * translate a user@domain string into a uid/gid/whatever
880 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
883 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
885 size_t actual
; /* not used */
887 * Use copyoutstr() to reduce the copy size; we let
888 * this catch a NULL uaddr because we shouldn't be
889 * asking in that case anyway.
891 error
= copyoutstr(CAST_DOWN(void *, workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
894 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
898 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
899 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
900 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
901 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
904 KAUTH_RESOLVER_UNLOCK();
910 * kauth_resolver_getwork
912 * Description: Get a work item from the enqueued requests from the kernel and
913 * give it to the user space daemon.
915 * Parameters: message message to user space
918 * EINTR Interrupted (e.g. by signal)
919 * kauth_resolver_getwork2:EFAULT
921 * Notes: This function blocks in a continuation if there are no work
922 * items available for processing at the time the user space
923 * identity resolution daemon makes a request for work. This
924 * permits a large number of threads to be used by the daemon,
925 * without using a lot of wired kernel memory when there are no
926 * actual request outstanding.
929 kauth_resolver_getwork(user_addr_t message
)
931 struct kauth_resolver_work
*workp
;
934 KAUTH_RESOLVER_LOCK();
936 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
937 thread_t thread
= current_thread();
938 struct uthread
*ut
= get_bsdthread_info(thread
);
940 ut
->uu_save
.uus_kauth
.message
= message
;
941 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
942 KAUTH_RESOLVER_UNLOCK();
944 * If this is a wakeup from another thread in the resolver
945 * deregistering it, error out the request-for-work thread
947 if (!kauth_resolver_identity
) {
948 printf("external resolver died");
949 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
953 return kauth_resolver_getwork2(message
);
958 * kauth_resolver_complete
960 * Description: Return a result from userspace.
962 * Parameters: message message from user space
965 * EIO The resolver is dead
966 * copyin:EFAULT Bad message from user space
969 kauth_resolver_complete(user_addr_t message
)
971 struct kauth_identity_extlookup extl
;
972 struct kauth_resolver_work
*workp
;
973 struct kauth_resolver_work
*killp
;
974 int error
, result
, want_extend_data
;
977 * Copy in the mesage, including the extension field, since we are
978 * copying into a local variable.
980 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
981 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
985 KAUTH_RESOLVER_LOCK();
989 switch (extl
.el_result
) {
990 case KAUTH_EXTLOOKUP_INPROG
:
994 /* XXX this should go away once memberd is updated */
996 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
1002 case KAUTH_EXTLOOKUP_SUCCESS
:
1005 case KAUTH_EXTLOOKUP_FATAL
:
1006 /* fatal error means the resolver is dead */
1007 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
1008 RESOLVER_FAILED_MESSAGE("resolver %d died, waiting for a new one", kauth_resolver_identity
);
1010 * Terminate outstanding requests; without an authoritative
1011 * resolver, we are now back on our own authority. Tag the
1012 * resolver unregistered to prevent kauth_cred_ismember_gid()
1013 * enqueueing more work until a new one is registered. This
1014 * mitigates the damage a crashing resolver may inflict.
1016 kauth_resolver_identity
= 0;
1017 kauth_resolver_registered
= 0;
1019 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
1021 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
1023 /* Cause all waiting-for-work threads to return EIO */
1024 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
1025 /* and return EIO to the caller */
1026 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1029 case KAUTH_EXTLOOKUP_BADRQ
:
1030 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
1034 case KAUTH_EXTLOOKUP_FAILURE
:
1035 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
1036 RESOLVER_FAILED_MESSAGE("resolver reported transient failure for request %d", extl
.el_seqno
);
1037 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1041 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
1042 RESOLVER_FAILED_MESSAGE("resolver returned unexpected status %d", extl
.el_result
);
1043 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1048 * In the case of a fatal error, we assume that the resolver will
1049 * restart quickly and re-collect all of the outstanding requests.
1050 * Thus, we don't complete the request which returned the fatal
1053 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1054 /* scan our list for this request */
1055 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1057 if (workp
->kr_seqno
== extl
.el_seqno
) {
1059 * Do we want extend_data?
1061 want_extend_data
= (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_WANT_PWNAM
| KAUTH_EXTLOOKUP_WANT_GRNAM
));
1064 * Get the request of the submitted queue so
1065 * that it is not cleaned up out from under
1068 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1069 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1070 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1071 workp
->kr_result
= result
;
1073 /* Copy the result message to the work item. */
1074 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1077 * Check if we have a result in the extension
1078 * field; if we do, then we need to separately
1079 * copy the data from the message el_extend
1080 * into the request buffer that's in the work
1081 * item. We have to do it here because we do
1082 * not want to wake up the waiter until the
1083 * data is in their buffer, and because the
1084 * actual request response may be destroyed
1085 * by the time the requester wakes up, and they
1086 * do not have access to the user space buffer
1089 * It is safe to drop and reacquire the lock
1090 * here because we've already removed the item
1091 * from the submission queue, but have not yet
1092 * moved it to the completion queue. Note that
1093 * near simultaneous requests may result in
1094 * duplication of requests for items in this
1095 * window. This should not be a performance
1096 * issue and is easily detectable by comparing
1097 * time to live on last response vs. time of
1098 * next request in the resolver logs.
1100 * A malicious/faulty resolver could overwrite
1101 * part of a user's address space if they return
1102 * flags that mismatch the original request's flags.
1104 if (want_extend_data
&& (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
))) {
1105 size_t actual
; /* notused */
1107 KAUTH_RESOLVER_UNLOCK();
1108 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1109 KAUTH_DEBUG("RESOLVER - resolver got name :%*s: len = %d\n", (int)actual
,
1110 actual
? "null" : (char *)extl
.el_extend
, actual
);
1111 KAUTH_RESOLVER_LOCK();
1112 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1114 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1115 extl
.el_flags
, want_extend_data
);
1119 * Move the completed work item to the
1120 * completion queue and wake up requester(s)
1122 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1129 * Note that it's OK for us not to find anything; if the request has
1130 * timed out the work record will be gone.
1132 KAUTH_RESOLVER_UNLOCK();
1136 #endif /* CONFIG_EXT_RESOLVER */
1143 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1144 #define KI_VALID_GID (1<<1)
1145 #define KI_VALID_GUID (1<<2)
1146 #define KI_VALID_NTSID (1<<3)
1147 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1148 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1149 #define KI_VALID_GROUPS (1<<6)
1151 #if CONFIG_EXT_RESOLVER
1153 * kauth_identity_init
1155 * Description: Initialize the kernel side of the credential identity resolver
1157 * Parameters: (void)
1161 * Notes: Initialize the credential identity resolver for use; the
1162 * credential identity resolver is the KPI used to communicate
1163 * with a user space credential identity resolver daemon.
1165 * This function is called from kauth_init() in the file
1166 * kern_authorization.c.
1169 kauth_identity_init(void)
1171 TAILQ_INIT(&kauth_identities
);
1172 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
1177 * kauth_identity_alloc
1179 * Description: Allocate and fill out a kauth_identity structure for
1180 * translation between {UID|GID}/GUID/NTSID
1184 * Returns: NULL Insufficient memory to satisfy
1185 * the request or bad parameters
1186 * !NULL A pointer to the allocated
1187 * structure, filled in
1189 * Notes: It is illegal to translate between UID and GID; any given UUID
1190 * or NTSID can only refer to an NTSID or UUID (respectively),
1191 * and *either* a UID *or* a GID, but not both.
1193 static struct kauth_identity
*
1194 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1195 ntsid_t
*ntsidp
, time_t ntsid_expiry
, size_t supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1196 const char *name
, int nametype
)
1198 struct kauth_identity
*kip
;
1200 /* get and fill in a new identity */
1201 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1203 if (gid
!= KAUTH_GID_NONE
) {
1205 kip
->ki_valid
= KI_VALID_GID
;
1207 if (uid
!= KAUTH_UID_NONE
) {
1208 if (kip
->ki_valid
& KI_VALID_GID
) {
1209 panic("can't allocate kauth identity with both uid and gid");
1212 kip
->ki_valid
= KI_VALID_UID
;
1216 * A malicious/faulty resolver could return bad values
1218 assert(supgrpcnt
<= NGROUPS
);
1219 assert(supgrps
!= NULL
);
1221 if ((supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1224 if (kip
->ki_valid
& KI_VALID_GID
) {
1225 panic("can't allocate kauth identity with both gid and supplementary groups");
1227 kip
->ki_supgrpcnt
= (uint32_t)supgrpcnt
;
1228 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1229 kip
->ki_valid
|= KI_VALID_GROUPS
;
1231 kip
->ki_groups_expiry
= groups_expiry
;
1232 if (guidp
!= NULL
) {
1233 kip
->ki_guid
= *guidp
;
1234 kip
->ki_valid
|= KI_VALID_GUID
;
1236 kip
->ki_guid_expiry
= guid_expiry
;
1237 if (ntsidp
!= NULL
) {
1238 kip
->ki_ntsid
= *ntsidp
;
1239 kip
->ki_valid
|= KI_VALID_NTSID
;
1241 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1243 kip
->ki_name
= name
;
1244 kip
->ki_valid
|= nametype
;
1252 * kauth_identity_register_and_free
1254 * Description: Register an association between identity tokens. The passed
1255 * 'kip' is consumed by this function.
1257 * Parameters: kip Pointer to kauth_identity
1258 * structure to register
1262 * Notes: The memory pointer to by 'kip' is assumed to have been
1263 * previously allocated via kauth_identity_alloc().
1266 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1268 struct kauth_identity
*ip
;
1271 * We search the cache for the UID listed in the incoming association.
1272 * If we already have an entry, the new information is merged.
1275 KAUTH_IDENTITY_LOCK();
1276 if (kip
->ki_valid
& KI_VALID_UID
) {
1277 if (kip
->ki_valid
& KI_VALID_GID
) {
1278 panic("kauth_identity: can't insert record with both UID and GID as key");
1280 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1281 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
)) {
1284 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1285 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1286 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
)) {
1290 panic("kauth_identity: can't insert record without UID or GID as key");
1294 /* we already have an entry, merge/overwrite */
1295 if (kip
->ki_valid
& KI_VALID_GUID
) {
1296 ip
->ki_guid
= kip
->ki_guid
;
1297 ip
->ki_valid
|= KI_VALID_GUID
;
1299 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1300 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1301 ip
->ki_ntsid
= kip
->ki_ntsid
;
1302 ip
->ki_valid
|= KI_VALID_NTSID
;
1304 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1305 /* a valid ki_name field overwrites the previous name field */
1306 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1307 /* if there's an old one, discard it */
1308 const char *oname
= NULL
;
1309 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1310 oname
= ip
->ki_name
;
1312 ip
->ki_name
= kip
->ki_name
;
1313 kip
->ki_name
= oname
;
1315 /* and discard the incoming entry */
1319 * if we don't have any information on this identity, add it;
1320 * if it pushes us over our limit, discard the oldest one.
1322 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1323 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1324 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1325 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1326 kauth_identity_count
--;
1329 KAUTH_IDENTITY_UNLOCK();
1330 /* have to drop lock before freeing expired entry (it may be in use) */
1332 /* if the ki_name field is used, clear it first */
1333 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1334 vfs_removename(ip
->ki_name
);
1336 /* free the expired entry */
1343 * kauth_identity_updatecache
1345 * Description: Given a lookup result, add any associations that we don't
1346 * currently have; replace ones which have changed.
1348 * Parameters: elp External lookup result from
1349 * user space daemon to kernel
1350 * rkip pointer to returned kauth
1352 * extend_data Extended data (can vary)
1357 * *rkip Modified (if non-NULL)
1359 * Notes: For extended information requests, this code relies on the fact
1360 * that elp->el_flags is never used as an rvalue, and is only
1361 * ever bit-tested for valid lookup information we are willing
1364 * XXX: We may have to do the same in the case that extended data was
1365 * passed out to user space to ensure that the request string
1366 * gets cached; we may also be able to use the rkip as an
1367 * input to avoid this. The jury is still out.
1369 * XXX: This codes performance could be improved for multiple valid
1370 * results by combining the loop iteration in a single loop.
1373 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1376 struct kauth_identity
*kip
;
1377 const char *speculative_name
= NULL
;
1382 * If there is extended data, and that data represents a name rather
1383 * than something else, speculatively create an entry for it in the
1384 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1385 * over the allocation later.
1387 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1388 const char *tmp
= CAST_DOWN(const char *, extend_data
);
1389 speculative_name
= vfs_addname(tmp
, (uint32_t)strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1392 /* user identity? */
1393 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1394 KAUTH_IDENTITY_LOCK();
1395 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1396 /* matching record */
1397 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1398 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1399 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1400 if (elp
->el_sup_grp_cnt
> NGROUPS
) {
1401 KAUTH_DEBUG("CACHE - invalid sup_grp_cnt provided (%d), truncating to %d",
1402 elp
->el_sup_grp_cnt
, NGROUPS
);
1403 elp
->el_sup_grp_cnt
= NGROUPS
;
1405 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1406 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1407 kip
->ki_valid
|= KI_VALID_GROUPS
;
1408 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1410 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1411 kip
->ki_guid
= elp
->el_uguid
;
1412 kip
->ki_valid
|= KI_VALID_GUID
;
1414 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1415 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1416 kip
->ki_ntsid
= elp
->el_usid
;
1417 kip
->ki_valid
|= KI_VALID_NTSID
;
1419 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1420 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1421 const char *oname
= kip
->ki_name
;
1422 kip
->ki_name
= speculative_name
;
1423 speculative_name
= NULL
;
1424 kip
->ki_valid
|= KI_VALID_PWNAM
;
1427 * free oname (if any) outside
1430 speculative_name
= oname
;
1433 kauth_identity_lru(kip
);
1437 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1441 KAUTH_IDENTITY_UNLOCK();
1442 /* not found in cache, add new record */
1444 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1445 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1446 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1447 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1448 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1449 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1450 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1451 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1452 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1458 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1459 speculative_name
= NULL
;
1461 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1462 kauth_identity_register_and_free(kip
);
1467 /* group identity? (ignore, if we already processed it as a user) */
1468 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1469 KAUTH_IDENTITY_LOCK();
1470 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1471 /* matching record */
1472 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1473 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1474 kip
->ki_guid
= elp
->el_gguid
;
1475 kip
->ki_valid
|= KI_VALID_GUID
;
1477 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1478 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1479 kip
->ki_ntsid
= elp
->el_gsid
;
1480 kip
->ki_valid
|= KI_VALID_NTSID
;
1482 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1483 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1484 const char *oname
= kip
->ki_name
;
1485 kip
->ki_name
= speculative_name
;
1486 speculative_name
= NULL
;
1487 kip
->ki_valid
|= KI_VALID_GRNAM
;
1490 * free oname (if any) outside
1493 speculative_name
= oname
;
1496 kauth_identity_lru(kip
);
1500 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1504 KAUTH_IDENTITY_UNLOCK();
1505 /* not found in cache, add new record */
1507 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1508 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1509 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1510 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1511 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1512 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1513 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1514 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1515 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1521 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1522 speculative_name
= NULL
;
1524 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1525 kauth_identity_register_and_free(kip
);
1530 /* If we have a name reference to drop, drop it here */
1531 if (speculative_name
!= NULL
) {
1532 vfs_removename(speculative_name
);
1538 * Trim older entries from the identity cache.
1540 * Must be called with the identity cache lock held.
1543 kauth_identity_trimcache(int newsize
)
1545 struct kauth_identity
*kip
;
1547 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1549 while (kauth_identity_count
> newsize
) {
1550 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1551 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1552 kauth_identity_count
--;
1558 * kauth_identity_lru
1560 * Description: Promote the entry to the head of the LRU, assumes the cache
1563 * Parameters: kip kauth identity to move to the
1564 * head of the LRU list, if it's
1569 * Notes: This is called even if the entry has expired; typically an
1570 * expired entry that's been looked up is about to be revalidated,
1571 * and having it closer to the head of the LRU means finding it
1572 * quickly again when the revalidation comes through.
1575 kauth_identity_lru(struct kauth_identity
*kip
)
1577 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1578 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1579 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1585 * kauth_identity_guid_expired
1587 * Description: Handle lazy expiration of GUID translations.
1589 * Parameters: kip kauth identity to check for
1592 * Returns: 1 Expired
1596 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1601 * Expiration time of 0 means this entry is persistent.
1603 if (kip
->ki_guid_expiry
== 0) {
1608 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1610 return (kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1615 * kauth_identity_ntsid_expired
1617 * Description: Handle lazy expiration of NTSID translations.
1619 * Parameters: kip kauth identity to check for
1622 * Returns: 1 Expired
1626 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1631 * Expiration time of 0 means this entry is persistent.
1633 if (kip
->ki_ntsid_expiry
== 0) {
1638 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1640 return (kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1644 * kauth_identity_groups_expired
1646 * Description: Handle lazy expiration of supplemental group translations.
1648 * Parameters: kip kauth identity to check for
1651 * Returns: 1 Expired
1655 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1660 * Expiration time of 0 means this entry is persistent.
1662 if (kip
->ki_groups_expiry
== 0) {
1667 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1669 return (kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0;
1673 * kauth_identity_find_uid
1675 * Description: Search for an entry by UID
1677 * Parameters: uid UID to find
1678 * kir Pointer to return area
1679 * getname Name buffer, if ki_name wanted
1685 * *klr Modified, if found
1688 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1690 struct kauth_identity
*kip
;
1692 KAUTH_IDENTITY_LOCK();
1693 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1694 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1695 kauth_identity_lru(kip
);
1696 /* Copy via structure assignment */
1698 /* If a name is wanted and one exists, copy it out */
1699 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1700 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1705 KAUTH_IDENTITY_UNLOCK();
1706 return (kip
== NULL
) ? ENOENT
: 0;
1711 * kauth_identity_find_gid
1713 * Description: Search for an entry by GID
1715 * Parameters: gid GID to find
1716 * kir Pointer to return area
1717 * getname Name buffer, if ki_name wanted
1723 * *klr Modified, if found
1726 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1728 struct kauth_identity
*kip
;
1730 KAUTH_IDENTITY_LOCK();
1731 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1732 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1733 kauth_identity_lru(kip
);
1734 /* Copy via structure assignment */
1736 /* If a name is wanted and one exists, copy it out */
1737 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1738 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1743 KAUTH_IDENTITY_UNLOCK();
1744 return (kip
== NULL
) ? ENOENT
: 0;
1749 * kauth_identity_find_guid
1751 * Description: Search for an entry by GUID
1753 * Parameters: guidp Pointer to GUID to find
1754 * kir Pointer to return area
1755 * getname Name buffer, if ki_name wanted
1761 * *klr Modified, if found
1763 * Note: The association may be expired, in which case the caller
1764 * may elect to call out to userland to revalidate.
1767 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1769 struct kauth_identity
*kip
;
1771 KAUTH_IDENTITY_LOCK();
1772 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1773 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1774 kauth_identity_lru(kip
);
1775 /* Copy via structure assignment */
1777 /* If a name is wanted and one exists, copy it out */
1778 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1779 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1784 KAUTH_IDENTITY_UNLOCK();
1785 return (kip
== NULL
) ? ENOENT
: 0;
1789 * kauth_identity_find_nam
1791 * Description: Search for an entry by name
1793 * Parameters: name Pointer to name to find
1794 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1795 * kir Pointer to return area
1801 * *klr Modified, if found
1804 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1806 struct kauth_identity
*kip
;
1808 KAUTH_IDENTITY_LOCK();
1809 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1810 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1811 kauth_identity_lru(kip
);
1812 /* Copy via structure assignment */
1817 KAUTH_IDENTITY_UNLOCK();
1818 return (kip
== NULL
) ? ENOENT
: 0;
1823 * kauth_identity_find_ntsid
1825 * Description: Search for an entry by NTSID
1827 * Parameters: ntsid Pointer to NTSID to find
1828 * kir Pointer to return area
1829 * getname Name buffer, if ki_name wanted
1835 * *klr Modified, if found
1837 * Note: The association may be expired, in which case the caller
1838 * may elect to call out to userland to revalidate.
1841 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1843 struct kauth_identity
*kip
;
1845 KAUTH_IDENTITY_LOCK();
1846 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1847 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1848 kauth_identity_lru(kip
);
1849 /* Copy via structure assignment */
1851 /* If a name is wanted and one exists, copy it out */
1852 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1853 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1858 KAUTH_IDENTITY_UNLOCK();
1859 return (kip
== NULL
) ? ENOENT
: 0;
1861 #endif /* CONFIG_EXT_RESOLVER */
1867 guid_t kauth_null_guid
;
1873 * Description: Determine the equality of two GUIDs
1875 * Parameters: guid1 Pointer to first GUID
1876 * guid2 Pointer to second GUID
1878 * Returns: 0 If GUIDs are unequal
1879 * !0 If GUIDs are equal
1882 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1884 return bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0;
1889 * kauth_wellknown_guid
1891 * Description: Determine if a GUID is a well-known GUID
1893 * Parameters: guid Pointer to GUID to check
1895 * Returns: KAUTH_WKG_NOT Not a well known GUID
1896 * KAUTH_WKG_EVERYBODY "Everybody"
1897 * KAUTH_WKG_NOBODY "Nobody"
1898 * KAUTH_WKG_OWNER "Other"
1899 * KAUTH_WKG_GROUP "Group"
1902 kauth_wellknown_guid(guid_t
*guid
)
1904 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1907 * All WKGs begin with the same 12 bytes.
1909 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1911 * The final 4 bytes are our code (in network byte order).
1913 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1916 return KAUTH_WKG_EVERYBODY
;
1918 return KAUTH_WKG_NOBODY
;
1920 return KAUTH_WKG_OWNER
;
1922 return KAUTH_WKG_GROUP
;
1925 return KAUTH_WKG_NOT
;
1932 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1934 * Parameters: sid1 Pointer to first NTSID
1935 * sid2 Pointer to second NTSID
1937 * Returns: 0 If GUIDs are unequal
1938 * !0 If GUIDs are equal
1941 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1943 /* check sizes for equality, also sanity-check size while we're at it */
1944 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1945 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1946 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0) {
1956 * We support four tokens representing identity:
1957 * - Credential reference
1960 * - NT security identifier
1962 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1969 * kauth_cred_change_egid
1971 * Description: Set EGID by changing the first element of cr_groups for the
1972 * passed credential; if the new EGID exists in the list of
1973 * groups already, then rotate the old EGID into its position,
1974 * otherwise replace it
1976 * Parameters: cred Pointer to the credential to modify
1977 * new_egid The new EGID to set
1979 * Returns: 0 The egid did not displace a member of
1980 * the supplementary group list
1981 * 1 The egid being set displaced a member
1982 * of the supplementary groups list
1984 * Note: Utility function; internal use only because of locking.
1986 * This function operates on the credential passed; the caller
1987 * must operate either on a newly allocated credential (one for
1988 * which there is no hash cache reference and no externally
1989 * visible pointer reference), or a template credential.
1992 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1998 #endif /* radar_4600026 */
1999 gid_t old_egid
= kauth_cred_getgid(cred
);
2000 posix_cred_t pcred
= posix_cred_get(cred
);
2002 /* Ignoring the first entry, scan for a match for the new egid */
2003 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
2005 * If we find a match, swap them so we don't lose overall
2008 if (pcred
->cr_groups
[i
] == new_egid
) {
2009 pcred
->cr_groups
[i
] = old_egid
;
2010 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
2017 #error Fix radar 4600026 first!!!
2020 * This is correct for memberd behaviour, but incorrect for POSIX; to address
2021 * this, we would need to automatically opt-out any SUID/SGID binary, and force
2022 * it to use initgroups to opt back in. We take the approach of considering it
2023 * opt'ed out in any group of 16 displacement instead, since it's a much more
2024 * conservative approach (i.e. less likely to cause things to break).
2028 * If we displaced a member of the supplementary groups list of the
2029 * credential, and we have not opted out of memberd, then if memberd
2030 * says that the credential is a member of the group, then it has not
2031 * actually been displaced.
2033 * NB: This is typically a cold code path.
2035 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
2036 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
2039 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
2041 #endif /* radar_4600026 */
2043 /* set the new EGID into the old spot */
2044 pcred
->cr_groups
[0] = new_egid
;
2053 * Description: Fetch UID from credential
2055 * Parameters: cred Credential to examine
2057 * Returns: (uid_t) UID associated with credential
2060 kauth_cred_getuid(kauth_cred_t cred
)
2062 NULLCRED_CHECK(cred
);
2063 return posix_cred_get(cred
)->cr_uid
;
2068 * kauth_cred_getruid
2070 * Description: Fetch RUID from credential
2072 * Parameters: cred Credential to examine
2074 * Returns: (uid_t) RUID associated with credential
2077 kauth_cred_getruid(kauth_cred_t cred
)
2079 NULLCRED_CHECK(cred
);
2080 return posix_cred_get(cred
)->cr_ruid
;
2085 * kauth_cred_getsvuid
2087 * Description: Fetch SVUID from credential
2089 * Parameters: cred Credential to examine
2091 * Returns: (uid_t) SVUID associated with credential
2094 kauth_cred_getsvuid(kauth_cred_t cred
)
2096 NULLCRED_CHECK(cred
);
2097 return posix_cred_get(cred
)->cr_svuid
;
2104 * Description: Fetch GID from credential
2106 * Parameters: cred Credential to examine
2108 * Returns: (gid_t) GID associated with credential
2111 kauth_cred_getgid(kauth_cred_t cred
)
2113 NULLCRED_CHECK(cred
);
2114 return posix_cred_get(cred
)->cr_gid
;
2119 * kauth_cred_getrgid
2121 * Description: Fetch RGID from credential
2123 * Parameters: cred Credential to examine
2125 * Returns: (gid_t) RGID associated with credential
2128 kauth_cred_getrgid(kauth_cred_t cred
)
2130 NULLCRED_CHECK(cred
);
2131 return posix_cred_get(cred
)->cr_rgid
;
2136 * kauth_cred_getsvgid
2138 * Description: Fetch SVGID from credential
2140 * Parameters: cred Credential to examine
2142 * Returns: (gid_t) SVGID associated with credential
2145 kauth_cred_getsvgid(kauth_cred_t cred
)
2147 NULLCRED_CHECK(cred
);
2148 return posix_cred_get(cred
)->cr_svgid
;
2152 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2154 #if CONFIG_EXT_RESOLVER == 0
2156 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
2159 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2161 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2162 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2163 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2164 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2169 case KI_VALID_UID
: {
2170 id_t uid
= htonl(*(id_t
*)src
);
2172 if (to
== KI_VALID_GUID
) {
2174 memcpy(uu
, _user_compat_prefix
, sizeof(_user_compat_prefix
));
2175 memcpy(&uu
[COMPAT_PREFIX_LEN
], &uid
, sizeof(uid
));
2180 case KI_VALID_GID
: {
2181 id_t gid
= htonl(*(id_t
*)src
);
2183 if (to
== KI_VALID_GUID
) {
2185 memcpy(uu
, _group_compat_prefix
, sizeof(_group_compat_prefix
));
2186 memcpy(&uu
[COMPAT_PREFIX_LEN
], &gid
, sizeof(gid
));
2191 case KI_VALID_GUID
: {
2192 const uint8_t *uu
= src
;
2194 if (to
== KI_VALID_UID
) {
2195 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2197 memcpy(&uid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(uid
));
2198 *(id_t
*)dst
= ntohl(uid
);
2201 } else if (to
== KI_VALID_GID
) {
2202 if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2204 memcpy(&gid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(gid
));
2205 *(id_t
*)dst
= ntohl(gid
);
2212 /* NOT IMPLEMENTED */
2219 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2221 * Structure to hold supplemental groups. Used for impedance matching with
2222 * kauth_cred_cache_lookup below.
2230 * kauth_cred_uid2groups
2232 * Description: Fetch supplemental GROUPS from UID
2234 * Parameters: uid UID to examine
2235 * groups pointer to an array of gid_ts
2236 * gcount pointer to the number of groups wanted/returned
2238 * Returns: 0 Success
2239 * kauth_cred_cache_lookup:EINVAL
2242 * *groups Modified, if successful
2243 * *gcount Modified, if successful
2247 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, size_t *gcount
)
2251 struct supgroups supgroups
;
2252 supgroups
.count
= gcount
;
2253 supgroups
.groups
= groups
;
2255 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2262 * kauth_cred_guid2pwnam
2264 * Description: Fetch PWNAM from GUID
2266 * Parameters: guidp Pointer to GUID to examine
2267 * pwnam Pointer to user@domain buffer
2269 * Returns: 0 Success
2270 * kauth_cred_cache_lookup:EINVAL
2273 * *pwnam Modified, if successful
2275 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2278 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2280 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
);
2285 * kauth_cred_guid2grnam
2287 * Description: Fetch GRNAM from GUID
2289 * Parameters: guidp Pointer to GUID to examine
2290 * grnam Pointer to group@domain buffer
2292 * Returns: 0 Success
2293 * kauth_cred_cache_lookup:EINVAL
2296 * *grnam Modified, if successful
2298 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2301 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2303 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
);
2308 * kauth_cred_pwnam2guid
2310 * Description: Fetch PWNAM from GUID
2312 * Parameters: pwnam String containing user@domain
2313 * guidp Pointer to buffer for GUID
2315 * Returns: 0 Success
2316 * kauth_cred_cache_lookup:EINVAL
2319 * *guidp Modified, if successful
2321 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2322 * bytes in size, including the NUL termination of the string.
2325 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2327 return kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
);
2332 * kauth_cred_grnam2guid
2334 * Description: Fetch GRNAM from GUID
2336 * Parameters: grnam String containing group@domain
2337 * guidp Pointer to buffer for GUID
2339 * Returns: 0 Success
2340 * kauth_cred_cache_lookup:EINVAL
2343 * *guidp Modified, if successful
2345 * Notes: grnam should not point to a request larger than MAXPATHLEN
2346 * bytes in size, including the NUL termination of the string.
2349 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2351 return kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
);
2356 * kauth_cred_guid2uid
2358 * Description: Fetch UID from GUID
2360 * Parameters: guidp Pointer to GUID to examine
2361 * uidp Pointer to buffer for UID
2363 * Returns: 0 Success
2364 * kauth_cred_cache_lookup:EINVAL
2367 * *uidp Modified, if successful
2370 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2372 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
);
2377 * kauth_cred_guid2gid
2379 * Description: Fetch GID from GUID
2381 * Parameters: guidp Pointer to GUID to examine
2382 * gidp Pointer to buffer for GID
2384 * Returns: 0 Success
2385 * kauth_cred_cache_lookup:EINVAL
2388 * *gidp Modified, if successful
2391 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2393 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
);
2397 * kauth_cred_nfs4domain2dsnode
2399 * Description: Fetch dsnode from nfs4domain
2401 * Parameters: nfs4domain Pointer to a string nfs4 domain
2402 * dsnode Pointer to buffer for dsnode
2404 * Returns: 0 Success
2405 * ENOENT For now just a stub that always fails
2408 * *dsnode Modified, if successuful
2411 kauth_cred_nfs4domain2dsnode(__unused
char *nfs4domain
, __unused
char *dsnode
)
2417 * kauth_cred_dsnode2nfs4domain
2419 * Description: Fetch nfs4domain from dsnode
2421 * Parameters: nfs4domain Pointer to string dsnode
2422 * dsnode Pointer to buffer for nfs4domain
2424 * Returns: 0 Success
2425 * ENOENT For now just a stub that always fails
2428 * *nfs4domain Modified, if successuful
2431 kauth_cred_dsnode2nfs4domain(__unused
char *dsnode
, __unused
char *nfs4domain
)
2437 * kauth_cred_ntsid2uid
2439 * Description: Fetch UID from NTSID
2441 * Parameters: sidp Pointer to NTSID to examine
2442 * uidp Pointer to buffer for UID
2444 * Returns: 0 Success
2445 * kauth_cred_cache_lookup:EINVAL
2448 * *uidp Modified, if successful
2451 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2453 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
);
2458 * kauth_cred_ntsid2gid
2460 * Description: Fetch GID from NTSID
2462 * Parameters: sidp Pointer to NTSID to examine
2463 * gidp Pointer to buffer for GID
2465 * Returns: 0 Success
2466 * kauth_cred_cache_lookup:EINVAL
2469 * *gidp Modified, if successful
2472 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2474 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
);
2479 * kauth_cred_ntsid2guid
2481 * Description: Fetch GUID from NTSID
2483 * Parameters: sidp Pointer to NTSID to examine
2484 * guidp Pointer to buffer for GUID
2486 * Returns: 0 Success
2487 * kauth_cred_cache_lookup:EINVAL
2490 * *guidp Modified, if successful
2493 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2495 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
);
2500 * kauth_cred_uid2guid
2502 * Description: Fetch GUID from UID
2504 * Parameters: uid UID to examine
2505 * guidp Pointer to buffer for GUID
2507 * Returns: 0 Success
2508 * kauth_cred_cache_lookup:EINVAL
2511 * *guidp Modified, if successful
2514 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2516 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
);
2521 * kauth_cred_getguid
2523 * Description: Fetch GUID from credential
2525 * Parameters: cred Credential to examine
2526 * guidp Pointer to buffer for GUID
2528 * Returns: 0 Success
2529 * kauth_cred_cache_lookup:EINVAL
2532 * *guidp Modified, if successful
2535 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2537 NULLCRED_CHECK(cred
);
2538 return kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
);
2543 * kauth_cred_getguid
2545 * Description: Fetch GUID from GID
2547 * Parameters: gid GID to examine
2548 * guidp Pointer to buffer for GUID
2550 * Returns: 0 Success
2551 * kauth_cred_cache_lookup:EINVAL
2554 * *guidp Modified, if successful
2557 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2559 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
);
2564 * kauth_cred_uid2ntsid
2566 * Description: Fetch NTSID from UID
2568 * Parameters: uid UID to examine
2569 * sidp Pointer to buffer for NTSID
2571 * Returns: 0 Success
2572 * kauth_cred_cache_lookup:EINVAL
2575 * *sidp Modified, if successful
2578 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2580 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
);
2585 * kauth_cred_getntsid
2587 * Description: Fetch NTSID from credential
2589 * Parameters: cred Credential to examine
2590 * sidp Pointer to buffer for NTSID
2592 * Returns: 0 Success
2593 * kauth_cred_cache_lookup:EINVAL
2596 * *sidp Modified, if successful
2599 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2601 NULLCRED_CHECK(cred
);
2602 return kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
);
2607 * kauth_cred_gid2ntsid
2609 * Description: Fetch NTSID from GID
2611 * Parameters: gid GID to examine
2612 * sidp Pointer to buffer for NTSID
2614 * Returns: 0 Success
2615 * kauth_cred_cache_lookup:EINVAL
2618 * *sidp Modified, if successful
2621 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2623 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
);
2628 * kauth_cred_guid2ntsid
2630 * Description: Fetch NTSID from GUID
2632 * Parameters: guidp Pointer to GUID to examine
2633 * sidp Pointer to buffer for NTSID
2635 * Returns: 0 Success
2636 * kauth_cred_cache_lookup:EINVAL
2639 * *sidp Modified, if successful
2642 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2644 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
);
2649 * kauth_cred_cache_lookup
2651 * Description: Lookup a translation in the cache; if one is not found, and
2652 * the attempt was not fatal, submit the request to the resolver
2653 * instead, and wait for it to complete or be aborted.
2655 * Parameters: from Identity information we have
2656 * to Identity information we want
2657 * src Pointer to buffer containing
2658 * the source identity
2659 * dst Pointer to buffer to receive
2660 * the target identity
2662 * Returns: 0 Success
2663 * EINVAL Unknown source identity type
2665 #if CONFIG_EXT_RESOLVER
2667 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2669 struct kauth_identity ki
;
2670 struct kauth_identity_extlookup el
;
2672 uint64_t extend_data
= 0ULL;
2673 int (* expired
)(struct kauth_identity
*kip
);
2674 char *namebuf
= NULL
;
2676 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2679 * Look for an existing cache entry for this association.
2680 * If the entry has not expired, return the cached information.
2681 * We do not cache user@domain translations here; they use too
2682 * much memory to hold onto forever, and can not be updated
2685 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2695 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2698 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2701 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2703 case KI_VALID_NTSID
:
2704 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2706 case KI_VALID_PWNAM
:
2707 case KI_VALID_GRNAM
:
2708 /* Names are unique in their 'from' space */
2709 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2714 /* If we didn't get what we're asking for. Call the resolver */
2715 if (!error
&& !(to
& ki
.ki_valid
)) {
2718 /* lookup failure or error */
2720 /* any other error is fatal */
2721 if (error
!= ENOENT
) {
2722 /* XXX bogus check - this is not possible */
2723 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2727 /* found a valid cached entry, check expiry */
2730 expired
= kauth_identity_guid_expired
;
2732 case KI_VALID_NTSID
:
2733 expired
= kauth_identity_ntsid_expired
;
2735 case KI_VALID_GROUPS
:
2736 expired
= kauth_identity_groups_expired
;
2741 expired
= kauth_identity_guid_expired
;
2743 case KI_VALID_NTSID
:
2744 expired
= kauth_identity_ntsid_expired
;
2752 * If no expiry function, or not expired, we have found
2756 if (!expired(&ki
)) {
2757 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2758 expired
= NULL
; /* must clear it is used as a flag */
2761 * We leave ki_valid set here; it contains a
2762 * translation but the TTL has expired. If we can't
2763 * get a result from the resolver, we will use it as
2764 * a better-than nothing alternative.
2767 KAUTH_DEBUG("CACHE - expired entry found");
2770 KAUTH_DEBUG("CACHE - no expiry function");
2774 /* do we have a translation? */
2775 if (ki
.ki_valid
& to
) {
2776 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2777 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2781 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2782 * If we went looking for a translation from GUID or NTSID and
2783 * found a translation that wasn't for our desired type, then
2784 * don't bother calling the resolver. We know that this
2785 * GUID/NTSID can't translate to our desired type.
2789 case KI_VALID_NTSID
:
2792 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2793 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2798 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2799 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2811 * We failed to find a cache entry; call the resolver.
2813 * Note: We ask for as much non-extended data as we can get,
2814 * and only provide (or ask for) extended information if
2815 * we have a 'from' (or 'to') which requires it. This
2816 * way we don't pay for the extra transfer overhead for
2817 * data we don't need.
2819 bzero(&el
, sizeof(el
));
2820 el
.el_info_pid
= current_proc()->p_pid
;
2823 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2824 el
.el_uid
= *(uid_t
*)src
;
2827 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2828 el
.el_gid
= *(gid_t
*)src
;
2831 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2832 el
.el_uguid
= *(guid_t
*)src
;
2833 el
.el_gguid
= *(guid_t
*)src
;
2835 case KI_VALID_NTSID
:
2836 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2837 el
.el_usid
= *(ntsid_t
*)src
;
2838 el
.el_gsid
= *(ntsid_t
*)src
;
2840 case KI_VALID_PWNAM
:
2841 /* extra overhead */
2842 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2843 extend_data
= CAST_USER_ADDR_T(src
);
2845 case KI_VALID_GRNAM
:
2846 /* extra overhead */
2847 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2848 extend_data
= CAST_USER_ADDR_T(src
);
2854 * Here we ask for everything all at once, to avoid having to work
2855 * out what we really want now, or might want soon.
2857 * Asking for SID translations when we don't know we need them right
2858 * now is going to cause excess work to be done if we're connected
2859 * to a network that thinks it can translate them. This list needs
2860 * to get smaller/smarter.
2862 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2863 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2864 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2865 if (to
== KI_VALID_PWNAM
) {
2866 /* extra overhead */
2867 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2868 extend_data
= CAST_USER_ADDR_T(dst
);
2870 if (to
== KI_VALID_GRNAM
) {
2871 /* extra overhead */
2872 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2873 extend_data
= CAST_USER_ADDR_T(dst
);
2875 if (to
== KI_VALID_GROUPS
) {
2876 /* Expensive and only useful for an NFS client not using kerberos */
2877 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2878 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2880 * Copy the current supplemental groups for the resolver.
2881 * The resolver should check these groups first and if
2882 * the user (uid) is still a member it should endeavor to
2883 * keep them in the list. Otherwise NFS clients could get
2884 * changing access to server file system objects on each
2887 if (ki
.ki_supgrpcnt
> NGROUPS
) {
2888 panic("kauth data structure corrupted. kauth identity 0x%p with %u groups, greater than max of %d",
2889 &ki
, ki
.ki_supgrpcnt
, NGROUPS
);
2892 el
.el_sup_grp_cnt
= (uint32_t)ki
.ki_supgrpcnt
;
2894 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof(el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2895 /* Let the resolver know these were the previous valid groups */
2896 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2897 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2899 KAUTH_DEBUG("GROUPS: no valid groups to send");
2904 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2906 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2908 error
= kauth_resolver_submit(&el
, extend_data
);
2910 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2912 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2914 /* was the external lookup successful? */
2917 * Save the results from the lookup - we may have other
2918 * information, even if we didn't get a guid or the
2921 * If we came from a name, we know the extend_data is valid.
2923 if (from
== KI_VALID_PWNAM
) {
2924 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2925 } else if (from
== KI_VALID_GRNAM
) {
2926 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2929 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2932 * Check to see if we have a valid cache entry
2933 * originating from the result.
2935 if (!(ki
.ki_valid
& to
)) {
2944 * Copy from the appropriate struct kauth_identity cache entry
2945 * structure into the destination buffer area.
2949 *(uid_t
*)dst
= ki
.ki_uid
;
2952 *(gid_t
*)dst
= ki
.ki_gid
;
2955 *(guid_t
*)dst
= ki
.ki_guid
;
2957 case KI_VALID_NTSID
:
2958 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2960 case KI_VALID_GROUPS
: {
2961 struct supgroups
*gp
= (struct supgroups
*)dst
;
2962 size_t limit
= ki
.ki_supgrpcnt
;
2965 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2969 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2972 case KI_VALID_PWNAM
:
2973 case KI_VALID_GRNAM
:
2974 /* handled in kauth_resolver_complete() */
2979 KAUTH_DEBUG("CACHE - returned successfully");
2985 * Group membership cache.
2987 * XXX the linked-list implementation here needs to be optimized.
2993 * Description: Initialize the groups cache
2995 * Parameters: (void)
2999 * Notes: Initialize the groups cache for use; the group cache is used
3000 * to avoid unnecessary calls out to user space.
3002 * This function is called from kauth_init() in the file
3003 * kern_authorization.c.
3006 kauth_groups_init(void)
3008 TAILQ_INIT(&kauth_groups
);
3009 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
3014 * kauth_groups_expired
3016 * Description: Handle lazy expiration of group membership cache entries
3018 * Parameters: gm group membership entry to
3019 * check for expiration
3021 * Returns: 1 Expired
3025 kauth_groups_expired(struct kauth_group_membership
*gm
)
3030 * Expiration time of 0 means this entry is persistent.
3032 if (gm
->gm_expiry
== 0) {
3038 return (gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0;
3045 * Description: Promote the entry to the head of the LRU, assumes the cache
3048 * Parameters: kip group membership entry to move
3049 * to the head of the LRU list,
3050 * if it's not already there
3054 * Notes: This is called even if the entry has expired; typically an
3055 * expired entry that's been looked up is about to be revalidated,
3056 * and having it closer to the head of the LRU means finding it
3057 * quickly again when the revalidation comes through.
3060 kauth_groups_lru(struct kauth_group_membership
*gm
)
3062 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
3063 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3064 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3070 * kauth_groups_updatecache
3072 * Description: Given a lookup result, add any group cache associations that
3073 * we don't currently have.
3075 * Parameters: elp External lookup result from
3076 * user space daemon to kernel
3077 * rkip pointer to returned kauth
3083 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
3085 struct kauth_group_membership
*gm
;
3088 /* need a valid response if we are to cache anything */
3090 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
3091 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) {
3098 * Search for an existing record for this association before inserting
3099 * a new one; if we find one, update it instead of creating a new one
3101 KAUTH_GROUPS_LOCK();
3102 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3103 if ((el
->el_uid
== gm
->gm_uid
) &&
3104 (el
->el_gid
== gm
->gm_gid
)) {
3105 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3106 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3108 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3110 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3111 kauth_groups_lru(gm
);
3115 KAUTH_GROUPS_UNLOCK();
3117 /* if we found an entry to update, stop here */
3122 /* allocate a new record */
3123 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
3125 gm
->gm_uid
= el
->el_uid
;
3126 gm
->gm_gid
= el
->el_gid
;
3127 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3128 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3130 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3132 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3136 * Insert the new entry. Note that it's possible to race ourselves
3137 * here and end up with duplicate entries in the list. Wasteful, but
3138 * harmless since the first into the list will never be looked up,
3139 * and thus will eventually just fall off the end.
3141 KAUTH_GROUPS_LOCK();
3142 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3143 if (++kauth_groups_count
> kauth_groups_cachemax
) {
3144 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3145 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3146 kauth_groups_count
--;
3150 KAUTH_GROUPS_UNLOCK();
3152 /* free expired cache entry */
3159 * Trim older entries from the group membership cache.
3161 * Must be called with the group cache lock held.
3164 kauth_groups_trimcache(int new_size
)
3166 struct kauth_group_membership
*gm
;
3168 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
3170 while (kauth_groups_count
> new_size
) {
3171 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3172 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3173 kauth_groups_count
--;
3177 #endif /* CONFIG_EXT_RESOLVER */
3180 * Group membership KPI
3184 * kauth_cred_ismember_gid
3186 * Description: Given a credential and a GID, determine if the GID is a member
3187 * of one of the supplementary groups associated with the given
3190 * Parameters: cred Credential to check in
3191 * gid GID to check for membership
3192 * resultp Pointer to int to contain the
3193 * result of the call
3195 * Returns: 0 Success
3196 * ENOENT Could not perform lookup
3197 * kauth_resolver_submit:EWOULDBLOCK
3198 * kauth_resolver_submit:EINTR
3199 * kauth_resolver_submit:ENOMEM
3200 * kauth_resolver_submit:ENOENT User space daemon did not vend
3202 * kauth_resolver_submit:??? Unlikely error from user space
3205 * *resultp (modified) 1 Is member
3208 * Notes: This function guarantees not to modify resultp when returning
3211 * This function effectively checks the EGID as well, since the
3212 * EGID is cr_groups[0] as an implementation detail.
3215 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3217 posix_cred_t pcred
= posix_cred_get(cred
);
3221 * Check the per-credential list of override groups.
3223 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3224 * the cache should be used for that case.
3226 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3227 if (gid
== pcred
->cr_groups
[i
]) {
3234 * If we don't have a UID for group membership checks, the in-cred list
3235 * was authoritative and we can stop here.
3237 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3242 #if CONFIG_EXT_RESOLVER
3243 struct kauth_group_membership
*gm
;
3244 struct kauth_identity_extlookup el
;
3248 * If the resolver hasn't checked in yet, we are early in the boot
3249 * phase and the local group list is complete and authoritative.
3251 if (!kauth_resolver_registered
) {
3257 /* XXX check supplementary groups */
3258 /* XXX check whiteout groups */
3259 /* XXX nesting of supplementary/whiteout groups? */
3262 * Check the group cache.
3264 KAUTH_GROUPS_LOCK();
3265 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3266 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3267 kauth_groups_lru(gm
);
3272 /* did we find a membership entry? */
3274 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3276 KAUTH_GROUPS_UNLOCK();
3278 /* if we did, we can return now */
3280 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3284 /* nothing in the cache, need to go to userland */
3285 bzero(&el
, sizeof(el
));
3286 el
.el_info_pid
= current_proc()->p_pid
;
3287 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3288 el
.el_uid
= pcred
->cr_gmuid
;
3290 el
.el_member_valid
= 0; /* XXX set by resolver? */
3292 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3294 error
= kauth_resolver_submit(&el
, 0ULL);
3296 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3301 /* save the results from the lookup */
3302 kauth_groups_updatecache(&el
);
3304 /* if we successfully ascertained membership, report */
3305 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3306 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3318 * kauth_cred_ismember_guid
3320 * Description: Determine whether the supplied credential is a member of the
3321 * group nominated by GUID.
3323 * Parameters: cred Credential to check in
3324 * guidp Pointer to GUID whose group
3325 * we are testing for membership
3326 * resultp Pointer to int to contain the
3327 * result of the call
3329 * Returns: 0 Success
3330 * kauth_cred_guid2gid:EINVAL
3331 * kauth_cred_ismember_gid:ENOENT
3332 * kauth_resolver_submit:ENOENT User space daemon did not vend
3334 * kauth_cred_ismember_gid:EWOULDBLOCK
3335 * kauth_cred_ismember_gid:EINTR
3336 * kauth_cred_ismember_gid:ENOMEM
3337 * kauth_cred_ismember_gid:??? Unlikely error from user space
3340 * *resultp (modified) 1 Is member
3344 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3348 switch (kauth_wellknown_guid(guidp
)) {
3349 case KAUTH_WKG_NOBODY
:
3352 case KAUTH_WKG_EVERYBODY
:
3358 #if CONFIG_EXT_RESOLVER
3359 struct kauth_identity ki
;
3362 * Grovel the identity cache looking for this GUID.
3363 * If we find it, and it is for a user record, return
3364 * false because it's not a group.
3366 * This is necessary because we don't have -ve caching
3367 * of group memberships, and we really want to avoid
3368 * calling out to the resolver if at all possible.
3370 * Because we're called by the ACL evaluator, and the
3371 * ACL evaluator is likely to encounter ACEs for users,
3372 * this is expected to be a common case.
3375 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3376 !kauth_identity_guid_expired(&ki
)) {
3377 if (ki
.ki_valid
& KI_VALID_GID
) {
3378 /* It's a group after all... */
3382 if (ki
.ki_valid
& KI_VALID_UID
) {
3387 #endif /* CONFIG_EXT_RESOLVER */
3389 * Attempt to translate the GUID to a GID. Even if
3390 * this fails, we will have primed the cache if it is
3391 * a user record and we'll see it above the next time
3394 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3396 * If we have no guid -> gid translation, it's not a group and
3397 * thus the cred can't be a member.
3399 if (error
== ENOENT
) {
3404 #if CONFIG_EXT_RESOLVER
3406 #endif /* CONFIG_EXT_RESOLVER */
3407 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3416 * kauth_cred_gid_subset
3418 * Description: Given two credentials, determine if all GIDs associated with
3419 * the first are also associated with the second
3421 * Parameters: cred1 Credential to check for
3422 * cred2 Credential to check in
3423 * resultp Pointer to int to contain the
3424 * result of the call
3426 * Returns: 0 Success
3427 * non-zero See kauth_cred_ismember_gid for
3431 * *resultp (modified) 1 Is subset
3434 * Notes: This function guarantees not to modify resultp when returning
3438 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3440 int i
, err
, res
= 1;
3442 posix_cred_t pcred1
= posix_cred_get(cred1
);
3443 posix_cred_t pcred2
= posix_cred_get(cred2
);
3445 /* First, check the local list of groups */
3446 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3447 gid
= pcred1
->cr_groups
[i
];
3448 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3452 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3458 /* Check real gid */
3459 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3463 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3464 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3469 /* Finally, check saved gid */
3470 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0) {
3474 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3475 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3486 * kauth_cred_issuser
3488 * Description: Fast replacement for issuser()
3490 * Parameters: cred Credential to check for super
3493 * Returns: 0 Not super user
3496 * Notes: This function uses a magic number which is not a manifest
3497 * constant; this is bad practice.
3500 kauth_cred_issuser(kauth_cred_t cred
)
3502 return kauth_cred_getuid(cred
) == 0;
3510 /* lock protecting credential hash table */
3511 static lck_mtx_t kauth_cred_hash_mtx
;
3512 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(&kauth_cred_hash_mtx);
3513 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(&kauth_cred_hash_mtx);
3514 #define KAUTH_CRED_HASH_LOCK_ASSERT() LCK_MTX_ASSERT(&kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3520 * Description: Initialize the credential hash cache
3522 * Parameters: (void)
3526 * Notes: Intialize the credential hash cache for use; the credential
3527 * hash cache is used convert duplicate credentials into a
3528 * single reference counted credential in order to save wired
3529 * kernel memory. In practice, this generally means a desktop
3530 * system runs with a few tens of credentials, instead of one
3531 * per process, one per thread, one per vnode cache entry, and
3532 * so on. This generally results in savings of 200K or more
3533 * (potentially much more on server systems).
3535 * The hash cache internally has a reference on the credential
3536 * for itself as a means of avoiding a reclaim race for a
3537 * credential in the process of having it's last non-hash
3538 * reference released. This would otherwise result in the
3539 * possibility of a freed credential that was still in uses due
3540 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3542 * On final release, the hash reference is droped, and the
3543 * credential is freed back to the system.
3545 * This function is called from kauth_init() in the file
3546 * kern_authorization.c.
3549 kauth_cred_init(void)
3551 lck_mtx_init(&kauth_cred_hash_mtx
, kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
3553 for (int i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
3554 LIST_INIT(&kauth_cred_table_anchor
[i
]);
3562 * Description: Get the current thread's effective UID.
3564 * Parameters: (void)
3566 * Returns: (uid_t) The effective UID of the
3572 return kauth_cred_getuid(kauth_cred_get());
3579 * Description: Get the current thread's real UID.
3581 * Parameters: (void)
3583 * Returns: (uid_t) The real UID of the current
3589 return kauth_cred_getruid(kauth_cred_get());
3596 * Description: Get the current thread's effective GID.
3598 * Parameters: (void)
3600 * Returns: (gid_t) The effective GID of the
3606 return kauth_cred_getgid(kauth_cred_get());
3613 * Description: Get the current thread's real GID.
3615 * Parameters: (void)
3617 * Returns: (gid_t) The real GID of the current
3623 return kauth_cred_getrgid(kauth_cred_get());
3630 * Description: Returns a pointer to the current thread's credential
3632 * Parameters: (void)
3634 * Returns: (kauth_cred_t) Pointer to the current thread's
3637 * Notes: This function does not take a reference; because of this, the
3638 * caller MUST NOT do anything that would let the thread's
3639 * credential change while using the returned value, without
3640 * first explicitly taking their own reference.
3642 * If a caller intends to take a reference on the resulting
3643 * credential pointer from calling this function, it is strongly
3644 * recommended that the caller use kauth_cred_get_with_ref()
3645 * instead, to protect against any future changes to the cred
3646 * locking protocols; such changes could otherwise potentially
3647 * introduce race windows in the callers code.
3650 kauth_cred_get(void)
3653 struct uthread
*uthread
;
3655 uthread
= get_bsdthread_info(current_thread());
3657 if (uthread
== NULL
) {
3658 panic("thread wants credential but has no BSD thread info");
3661 * We can lazy-bind credentials to threads, as long as their processes
3664 * XXX If we later inline this function, the code in this block
3665 * XXX should probably be called out in a function.
3667 if (uthread
->uu_ucred
== NOCRED
) {
3668 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
) {
3669 panic("thread wants credential but has no BSD process");
3671 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3673 return uthread
->uu_ucred
;
3677 mach_kauth_cred_uthread_update(void)
3682 uthread
= get_bsdthread_info(current_thread());
3683 proc
= current_proc();
3685 kauth_cred_uthread_update(uthread
, proc
);
3689 * kauth_cred_uthread_update
3691 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3692 * late-bind the uthread cred to the proc cred.
3694 * Parameters: uthread_t The uthread to update
3695 * proc_t The process to update to
3699 * Notes: This code is common code called from system call or trap entry
3700 * in the case that the process thread may have been changed
3701 * since the last time the thread entered the kernel. It is
3702 * generally only called with the current uthread and process as
3706 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3708 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3709 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3710 kauth_cred_t old
= uthread
->uu_ucred
;
3711 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3712 if (IS_VALID_CRED(old
)) {
3713 kauth_cred_unref(&old
);
3720 * kauth_cred_get_with_ref
3722 * Description: Takes a reference on the current thread's credential, and then
3723 * returns a pointer to it to the caller.
3725 * Parameters: (void)
3727 * Returns: (kauth_cred_t) Pointer to the current thread's
3728 * newly referenced credential
3730 * Notes: This function takes a reference on the credential before
3731 * returning it to the caller.
3733 * It is the responsibility of the calling code to release this
3734 * reference when the credential is no longer in use.
3736 * Since the returned reference may be a persistent reference
3737 * (e.g. one cached in another data structure with a lifetime
3738 * longer than the calling function), this release may be delayed
3739 * until such time as the persistent reference is to be destroyed.
3740 * An example of this would be the per vnode credential cache used
3741 * to accelerate lookup operations.
3744 kauth_cred_get_with_ref(void)
3746 struct uthread
*uthread
= current_uthread();
3749 * We can lazy-bind credentials to threads, as long as their processes
3752 * XXX If we later inline this function, the code in this block
3753 * XXX should probably be called out in a function.
3755 if (uthread
->uu_ucred
== NOCRED
) {
3756 /* take reference for new cred in thread */
3757 uthread
->uu_ucred
= kauth_cred_proc_ref(current_proc());
3759 /* take a reference for our caller */
3760 kauth_cred_ref(uthread
->uu_ucred
);
3761 return uthread
->uu_ucred
;
3766 * kauth_cred_proc_ref
3768 * Description: Takes a reference on the current process's credential, and
3769 * then returns a pointer to it to the caller.
3771 * Parameters: procp Process whose credential we
3772 * intend to take a reference on
3774 * Returns: (kauth_cred_t) Pointer to the process's
3775 * newly referenced credential
3777 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3778 * after the refeence is taken to protect the p_ucred field of
3779 * the process referred to by procp.
3781 * Notes: This function takes a reference on the credential before
3782 * returning it to the caller.
3784 * It is the responsibility of the calling code to release this
3785 * reference when the credential is no longer in use.
3787 * Since the returned reference may be a persistent reference
3788 * (e.g. one cached in another data structure with a lifetime
3789 * longer than the calling function), this release may be delayed
3790 * until such time as the persistent reference is to be destroyed.
3791 * An example of this would be the per vnode credential cache used
3792 * to accelerate lookup operations.
3795 kauth_cred_proc_ref(proc_t procp
)
3799 proc_ucred_lock(procp
);
3800 cred
= proc_ucred(procp
);
3801 kauth_cred_ref(cred
);
3802 proc_ucred_unlock(procp
);
3809 * Description: Allocate a new credential
3811 * Parameters: (void)
3813 * Returns: !NULL Newly allocated credential
3814 * NULL Insufficient memory
3816 * Notes: The newly allocated credential is zero'ed as part of the
3817 * allocation process, with the exception of the reference
3818 * count, which is set to 0 to indicate the caller still has
3819 * to call kauth_cred_add().
3821 * Since newly allocated credentials have no external pointers
3822 * referencing them, prior to making them visible in an externally
3823 * visible pointer (e.g. by adding them to the credential hash
3824 * cache) is the only legal time in which an existing credential
3825 * can be safely iinitialized or modified directly.
3827 * After initialization, the caller is expected to call the
3828 * function kauth_cred_add() to add the credential to the hash
3829 * cache, after which time it's frozen and becomes publically
3832 * The release protocol depends on kauth_hash_add() being called
3833 * before kauth_cred_rele() (there is a diagnostic panic which
3834 * will trigger if this protocol is not observed).
3836 * XXX: This function really ought to be static, rather than being
3837 * exported as KPI, since a failure of kauth_cred_add() can only
3838 * be handled by an explicit free of the credential; such frees
3839 * depend on knowlegdge of the allocation method used, which is
3840 * permitted to change between kernel revisions.
3842 * XXX: In the insufficient resource case, this code panic's rather
3843 * than returning a NULL pointer; the code that calls this
3844 * function needs to be audited before this can be changed.
3847 kauth_cred_alloc(void)
3849 kauth_cred_t newcred
;
3851 newcred
= zalloc_flags(ucred_zone
, Z_WAITOK
| Z_ZERO
);
3852 posix_cred_get(newcred
)->cr_gmuid
= KAUTH_UID_NONE
;
3853 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3854 /* must do this, or cred has same group membership as uid 0 */
3856 mac_cred_label_init(newcred
);
3865 * Description: Destroy a credential
3867 * Parameters: cred Credential to destroy.
3870 kauth_cred_free(kauth_cred_t cred
)
3872 assert(os_atomic_load(&cred
->cr_ref
, relaxed
) == 0);
3874 mac_cred_label_destroy(cred
);
3876 AUDIT_SESSION_UNREF(cred
);
3877 zfree(ucred_zone
, cred
);
3883 * Description: Look to see if we already have a known credential in the hash
3884 * cache; if one is found, bump the reference count and return
3885 * it. If there are no credentials that match the given
3886 * credential, then allocate a new credential.
3888 * Parameters: cred Template for credential to
3891 * Returns: (kauth_cred_t) The credential that was found
3892 * in the hash or created
3893 * NULL kauth_cred_add() failed, or
3894 * there was not an egid specified
3896 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3897 * maintain this field, we can't expect callers to know how it
3898 * needs to be set. Callers should be prepared for this field
3899 * to be overwritten.
3902 kauth_cred_create(kauth_cred_t cred
)
3904 kauth_cred_t found_cred
, new_cred
= NULL
;
3905 posix_cred_t pcred
= posix_cred_get(cred
);
3908 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3909 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3912 * If the template credential is not opting out of external
3913 * group membership resolution, then we need to check that
3914 * the UID we will be using is resolvable by the external
3915 * resolver. If it's not, then we opt it out anyway, since
3916 * all future external resolution requests will be failing
3917 * anyway, and potentially taking a long time to do it. We
3918 * use gid 0 because we always know it will exist and not
3919 * trigger additional lookups. This is OK, because we end up
3920 * precatching the information here as a result.
3922 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3924 * It's a recognized value; we don't really care about
3925 * the answer, so long as it's something the external
3926 * resolver could have vended.
3928 pcred
->cr_gmuid
= pcred
->cr_uid
;
3931 * It's not something the external resolver could
3932 * have vended, so we don't want to ask it more
3933 * questions about the credential in the future. This
3934 * speeds up future lookups, as long as the caller
3935 * caches results; otherwise, it the same recurring
3936 * cost. Since most credentials are used multiple
3937 * times, we still get some performance win from this.
3939 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3940 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3944 /* Caller *must* specify at least the egid in cr_groups[0] */
3945 if (pcred
->cr_ngroups
< 1) {
3949 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
3951 KAUTH_CRED_HASH_LOCK();
3952 found_cred
= kauth_cred_find_and_ref(cred
, bucket
);
3953 KAUTH_CRED_HASH_UNLOCK();
3954 if (found_cred
!= NULL
) {
3959 * No existing credential found. Create one and add it to
3962 new_cred
= kauth_cred_alloc();
3963 if (new_cred
!= NULL
) {
3964 *posix_cred_get(new_cred
) = *pcred
;
3966 new_cred
->cr_audit
= cred
->cr_audit
;
3968 new_cred
= kauth_cred_add(new_cred
, bucket
);
3976 * kauth_cred_setresuid
3978 * Description: Update the given credential using the UID arguments. The given
3979 * UIDs are used to set the effective UID, real UID, saved UID,
3980 * and GMUID (used for group membership checking).
3982 * Parameters: cred The original credential
3983 * ruid The new real UID
3984 * euid The new effective UID
3985 * svuid The new saved UID
3986 * gmuid KAUTH_UID_NONE -or- the new
3987 * group membership UID
3989 * Returns: (kauth_cred_t) The updated credential
3991 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3992 * setting, so if you don't want it to change, pass it the
3993 * previous value, explicitly.
3995 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3996 * if it returns a credential other than the one it is passed,
3997 * will have dropped the reference on the passed credential. All
3998 * callers should be aware of this, and treat this function as an
3999 * unref + ref, potentially on different credentials.
4001 * Because of this, the caller is expected to take its own
4002 * reference on the credential passed as the first parameter,
4003 * and be prepared to release the reference on the credential
4004 * that is returned to them, if it is not intended to be a
4005 * persistent reference.
4008 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
4010 struct ucred temp_cred
;
4011 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4012 posix_cred_t pcred
= posix_cred_get(cred
);
4014 NULLCRED_CHECK(cred
);
4017 * We don't need to do anything if the UIDs we are changing are
4018 * already the same as the UIDs passed in
4020 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
4021 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
4022 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
4023 (pcred
->cr_gmuid
== gmuid
)) {
4024 /* no change needed */
4029 * Look up in cred hash table to see if we have a matching credential
4030 * with the new values; this is done by calling kauth_cred_update().
4033 if (euid
!= KAUTH_UID_NONE
) {
4034 temp_pcred
->cr_uid
= euid
;
4036 if (ruid
!= KAUTH_UID_NONE
) {
4037 temp_pcred
->cr_ruid
= ruid
;
4039 if (svuid
!= KAUTH_UID_NONE
) {
4040 temp_pcred
->cr_svuid
= svuid
;
4044 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
4045 * opt out of participation in external group resolution, unless we
4046 * unless we explicitly opt back in later.
4048 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
4049 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4052 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4057 * kauth_cred_setresgid
4059 * Description: Update the given credential using the GID arguments. The given
4060 * GIDs are used to set the effective GID, real GID, and saved
4063 * Parameters: cred The original credential
4064 * rgid The new real GID
4065 * egid The new effective GID
4066 * svgid The new saved GID
4068 * Returns: (kauth_cred_t) The updated credential
4070 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4071 * if it returns a credential other than the one it is passed,
4072 * will have dropped the reference on the passed credential. All
4073 * callers should be aware of this, and treat this function as an
4074 * unref + ref, potentially on different credentials.
4076 * Because of this, the caller is expected to take its own
4077 * reference on the credential passed as the first parameter,
4078 * and be prepared to release the reference on the credential
4079 * that is returned to them, if it is not intended to be a
4080 * persistent reference.
4083 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
4085 struct ucred temp_cred
;
4086 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4087 posix_cred_t pcred
= posix_cred_get(cred
);
4089 NULLCRED_CHECK(cred
);
4090 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
4093 * We don't need to do anything if the given GID are already the
4094 * same as the GIDs in the credential.
4096 if (pcred
->cr_groups
[0] == egid
&&
4097 pcred
->cr_rgid
== rgid
&&
4098 pcred
->cr_svgid
== svgid
) {
4099 /* no change needed */
4104 * Look up in cred hash table to see if we have a matching credential
4105 * with the new values; this is done by calling kauth_cred_update().
4108 if (egid
!= KAUTH_GID_NONE
) {
4109 /* displacing a supplementary group opts us out of memberd */
4110 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
4111 DEBUG_CRED_CHANGE("displaced!\n");
4112 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4113 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4115 DEBUG_CRED_CHANGE("not displaced\n");
4118 if (rgid
!= KAUTH_GID_NONE
) {
4119 temp_pcred
->cr_rgid
= rgid
;
4121 if (svgid
!= KAUTH_GID_NONE
) {
4122 temp_pcred
->cr_svgid
= svgid
;
4125 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4130 * Update the given credential with the given groups. We only allocate a new
4131 * credential when the given gid actually results in changes to the existing
4133 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4134 * which will be used for group membership checking.
4137 * kauth_cred_setgroups
4139 * Description: Update the given credential using the provide supplementary
4140 * group list and group membership UID
4142 * Parameters: cred The original credential
4143 * groups Pointer to gid_t array which
4144 * contains the new group list
4145 * groupcount The count of valid groups which
4146 * are contained in 'groups'
4147 * gmuid KAUTH_UID_NONE -or- the new
4148 * group membership UID
4150 * Returns: (kauth_cred_t) The updated credential
4152 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4153 * setting, so if you don't want it to change, pass it the
4154 * previous value, explicitly.
4156 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4157 * if it returns a credential other than the one it is passed,
4158 * will have dropped the reference on the passed credential. All
4159 * callers should be aware of this, and treat this function as an
4160 * unref + ref, potentially on different credentials.
4162 * Because of this, the caller is expected to take its own
4163 * reference on the credential passed as the first parameter,
4164 * and be prepared to release the reference on the credential
4165 * that is returned to them, if it is not intended to be a
4166 * persistent reference.
4168 * XXX: Changes are determined in ordinal order - if the caller passes
4169 * in the same groups list that is already present in the
4170 * credential, but the members are in a different order, even if
4171 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4172 * is considered a modification to the credential, and a new
4173 * credential is created.
4175 * This should perhaps be better optimized, but it is considered
4176 * to be the caller's problem.
4179 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, size_t groupcount
, uid_t gmuid
)
4182 struct ucred temp_cred
;
4183 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4186 NULLCRED_CHECK(cred
);
4187 assert(groupcount
<= NGROUPS
);
4188 groupcount
= MIN(groupcount
, NGROUPS
);
4190 pcred
= posix_cred_get(cred
);
4193 * We don't need to do anything if the given list of groups does not
4196 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4197 for (i
= 0; i
< groupcount
; i
++) {
4198 if (pcred
->cr_groups
[i
] != groups
[i
]) {
4202 if (i
== groupcount
) {
4203 /* no change needed */
4209 * Look up in cred hash table to see if we have a matching credential
4210 * with new values. If we are setting or clearing the gmuid, then
4211 * update the cr_flags, since clearing it is sticky. This permits an
4212 * opt-out of memberd processing using setgroups(), and an opt-in
4213 * using initgroups(). This is required for POSIX conformance.
4216 temp_pcred
->cr_ngroups
= (short)groupcount
;
4217 bcopy(groups
, temp_pcred
->cr_groups
, groupcount
* sizeof(temp_pcred
->cr_groups
[0]));
4218 temp_pcred
->cr_gmuid
= gmuid
;
4219 if (gmuid
== KAUTH_UID_NONE
) {
4220 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4222 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4225 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4229 * Notes: The return value exists to account for the possibility of a
4230 * kauth_cred_t without a POSIX label. This will be the case in
4231 * the future (see posix_cred_get() below, for more details).
4233 #if CONFIG_EXT_RESOLVER
4234 int kauth_external_supplementary_groups_supported
= 1;
4236 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4240 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, size_t *countp
)
4242 size_t limit
= NGROUPS
;
4245 pcred
= posix_cred_get(cred
);
4247 #if CONFIG_EXT_RESOLVER
4249 * If we've not opted out of using the resolver, then convert the cred to a list
4250 * of supplemental groups. We do this only if there has been a resolver to talk to,
4251 * since we may be too early in boot, or in an environment that isn't using DS.
4253 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4254 uid_t uid
= kauth_cred_getuid(cred
);
4257 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4262 /* On error just fall through */
4263 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4265 #endif /* CONFIG_EXT_RESOLVER */
4268 * If they just want a copy of the groups list, they may not care
4269 * about the actual count. If they specify an input count, however,
4270 * treat it as an indicator of the buffer size available in grouplist,
4271 * and limit the returned list to that size.
4274 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4278 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4285 * kauth_cred_setuidgid
4287 * Description: Update the given credential using the UID and GID arguments.
4288 * The given UID is used to set the effective UID, real UID, and
4289 * saved UID. The given GID is used to set the effective GID,
4290 * real GID, and saved GID.
4292 * Parameters: cred The original credential
4293 * uid The new UID to use
4294 * gid The new GID to use
4296 * Returns: (kauth_cred_t) The updated credential
4298 * Notes: We set the gmuid to uid if the credential we are inheriting
4299 * from has not opted out of memberd participation; otherwise
4300 * we set it to KAUTH_UID_NONE
4302 * This code is only ever called from the per-thread credential
4303 * code path in the "set per thread credential" case; and in
4304 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4307 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4308 * if it returns a credential other than the one it is passed,
4309 * will have dropped the reference on the passed credential. All
4310 * callers should be aware of this, and treat this function as an
4311 * unref + ref, potentially on different credentials.
4313 * Because of this, the caller is expected to take its own
4314 * reference on the credential passed as the first parameter,
4315 * and be prepared to release the reference on the credential
4316 * that is returned to them, if it is not intended to be a
4317 * persistent reference.
4320 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4322 struct ucred temp_cred
;
4323 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4326 NULLCRED_CHECK(cred
);
4328 pcred
= posix_cred_get(cred
);
4331 * We don't need to do anything if the effective, real and saved
4332 * user IDs are already the same as the user ID passed into us.
4334 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4335 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4336 /* no change needed */
4341 * Look up in cred hash table to see if we have a matching credential
4342 * with the new values.
4344 bzero(&temp_cred
, sizeof(temp_cred
));
4345 temp_pcred
->cr_uid
= uid
;
4346 temp_pcred
->cr_ruid
= uid
;
4347 temp_pcred
->cr_svuid
= uid
;
4348 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4349 /* inherit the opt-out of memberd */
4350 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4351 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4352 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4354 temp_pcred
->cr_gmuid
= uid
;
4355 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4357 temp_pcred
->cr_ngroups
= 1;
4358 /* displacing a supplementary group opts us out of memberd */
4359 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4360 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4361 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4363 temp_pcred
->cr_rgid
= gid
;
4364 temp_pcred
->cr_svgid
= gid
;
4366 temp_cred
.cr_label
= cred
->cr_label
;
4369 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4374 * kauth_cred_setsvuidgid
4376 * Description: Function used by execve to set the saved uid and gid values
4377 * for suid/sgid programs
4379 * Parameters: cred The credential to update
4380 * uid The saved uid to set
4381 * gid The saved gid to set
4383 * Returns: (kauth_cred_t) The updated credential
4385 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4386 * if it returns a credential other than the one it is passed,
4387 * will have dropped the reference on the passed credential. All
4388 * callers should be aware of this, and treat this function as an
4389 * unref + ref, potentially on different credentials.
4391 * Because of this, the caller is expected to take its own
4392 * reference on the credential passed as the first parameter,
4393 * and be prepared to release the reference on the credential
4394 * that is returned to them, if it is not intended to be a
4395 * persistent reference.
4398 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4400 struct ucred temp_cred
;
4401 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4404 NULLCRED_CHECK(cred
);
4406 pcred
= posix_cred_get(cred
);
4408 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4411 * We don't need to do anything if the effective, real and saved
4412 * uids are already the same as the uid provided. This check is
4413 * likely insufficient.
4415 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4416 /* no change needed */
4419 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4421 /* look up in cred hash table to see if we have a matching credential
4425 temp_pcred
->cr_svuid
= uid
;
4426 temp_pcred
->cr_svgid
= gid
;
4428 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4433 * kauth_cred_setauditinfo
4435 * Description: Update the given credential using the given au_session_t.
4437 * Parameters: cred The original credential
4438 * auditinfo_p Pointer to ne audit information
4440 * Returns: (kauth_cred_t) The updated credential
4442 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4443 * if it returns a credential other than the one it is passed,
4444 * will have dropped the reference on the passed credential. All
4445 * callers should be aware of this, and treat this function as an
4446 * unref + ref, potentially on different credentials.
4448 * Because of this, the caller is expected to take its own
4449 * reference on the credential passed as the first parameter,
4450 * and be prepared to release the reference on the credential
4451 * that is returned to them, if it is not intended to be a
4452 * persistent reference.
4455 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4457 struct ucred temp_cred
;
4459 NULLCRED_CHECK(cred
);
4462 * We don't need to do anything if the audit info is already the
4463 * same as the audit info in the credential provided.
4465 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4466 /* no change needed */
4471 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4473 return kauth_cred_update(cred
, &temp_cred
, FALSE
);
4478 * kauth_cred_label_update
4480 * Description: Update the MAC label associated with a credential
4482 * Parameters: cred The original credential
4483 * label The MAC label to set
4485 * Returns: (kauth_cred_t) The updated credential
4487 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4488 * if it returns a credential other than the one it is passed,
4489 * will have dropped the reference on the passed credential. All
4490 * callers should be aware of this, and treat this function as an
4491 * unref + ref, potentially on different credentials.
4493 * Because of this, the caller is expected to take its own
4494 * reference on the credential passed as the first parameter,
4495 * and be prepared to release the reference on the credential
4496 * that is returned to them, if it is not intended to be a
4497 * persistent reference.
4500 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4502 kauth_cred_t newcred
;
4503 struct ucred temp_cred
;
4507 mac_cred_label_init(&temp_cred
);
4508 mac_cred_label_associate(cred
, &temp_cred
);
4509 mac_cred_label_update(&temp_cred
, label
);
4511 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4512 mac_cred_label_destroy(&temp_cred
);
4517 * kauth_cred_label_update_execve
4519 * Description: Update the MAC label associated with a credential as
4522 * Parameters: cred The original credential
4524 * scriptl The script MAC label
4525 * execl The executable MAC label
4526 * disjointp Pointer to flag to set if old
4527 * and returned credentials are
4530 * Returns: (kauth_cred_t) The updated credential
4533 * *disjointp Set to 1 for disjoint creds
4535 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4536 * if it returns a credential other than the one it is passed,
4537 * will have dropped the reference on the passed credential. All
4538 * callers should be aware of this, and treat this function as an
4539 * unref + ref, potentially on different credentials.
4541 * Because of this, the caller is expected to take its own
4542 * reference on the credential passed as the first parameter,
4543 * and be prepared to release the reference on the credential
4544 * that is returned to them, if it is not intended to be a
4545 * persistent reference.
4550 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4551 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4552 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4554 kauth_cred_t newcred
;
4555 struct ucred temp_cred
;
4559 mac_cred_label_init(&temp_cred
);
4560 mac_cred_label_associate(cred
, &temp_cred
);
4561 mac_cred_label_update_execve(ctx
, &temp_cred
,
4562 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4563 macextensions
, disjointp
, labelupdateerror
);
4565 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4566 mac_cred_label_destroy(&temp_cred
);
4571 * kauth_proc_label_update
4573 * Description: Update the label inside the credential associated with the process.
4575 * Parameters: p The process to modify
4576 * label The label to place in the process credential
4578 * Notes: The credential associated with the process may change as a result
4579 * of this call. The caller should not assume the process reference to
4580 * the old credential still exists.
4583 kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4585 kauth_cred_t my_cred
, my_new_cred
;
4587 my_cred
= kauth_cred_proc_ref(p
);
4589 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4591 /* get current credential and take a reference while we muck with it */
4594 * Set the credential with new info. If there is no change,
4595 * we get back the same credential we passed in; if there is
4596 * a change, we drop the reference on the credential we
4597 * passed in. The subsequent compare is safe, because it is
4598 * a pointer compare rather than a contents compare.
4600 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4601 if (my_cred
!= my_new_cred
) {
4602 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
);
4606 * We need to protect for a race where another thread
4607 * also changed the credential after we took our
4608 * reference. If p_ucred has changed then we should
4609 * restart this again with the new cred.
4611 if (p
->p_ucred
!= my_cred
) {
4612 proc_ucred_unlock(p
);
4613 kauth_cred_unref(&my_new_cred
);
4614 my_cred
= kauth_cred_proc_ref(p
);
4618 p
->p_ucred
= my_new_cred
;
4619 /* update cred on proc */
4620 PROC_UPDATE_CREDS_ONPROC(p
);
4622 proc_ucred_unlock(p
);
4626 /* Drop old proc reference or our extra reference */
4627 kauth_cred_unref(&my_cred
);
4633 * kauth_proc_label_update_execve
4635 * Description: Update the label inside the credential associated with the
4636 * process as part of a transitioning execve. The label will
4637 * be updated by the policies as part of this processing, not
4638 * provided up front.
4640 * Parameters: p The process to modify
4641 * ctx The context of the exec
4642 * vp The vnode being exec'ed
4643 * scriptl The script MAC label
4644 * execl The executable MAC label
4645 * lupdateerror The error place holder for MAC label authority
4646 * to update about possible termination
4648 * Returns: 0 Label update did not make credential
4650 * 1 Label update caused credential to be
4653 * Notes: The credential associated with the process WILL change as a
4654 * result of this call. The caller should not assume the process
4655 * reference to the old credential still exists.
4659 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4660 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4661 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4663 kauth_cred_t my_cred
, my_new_cred
;
4664 my_cred
= kauth_cred_proc_ref(p
);
4666 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4668 /* get current credential and take a reference while we muck with it */
4671 * Set the credential with new info. If there is no change,
4672 * we get back the same credential we passed in; if there is
4673 * a change, we drop the reference on the credential we
4674 * passed in. The subsequent compare is safe, because it is
4675 * a pointer compare rather than a contents compare.
4677 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4678 if (my_cred
!= my_new_cred
) {
4679 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
);
4683 * We need to protect for a race where another thread
4684 * also changed the credential after we took our
4685 * reference. If p_ucred has changed then we should
4686 * restart this again with the new cred.
4688 if (p
->p_ucred
!= my_cred
) {
4689 proc_ucred_unlock(p
);
4690 kauth_cred_unref(&my_new_cred
);
4691 my_cred
= kauth_cred_proc_ref(p
);
4695 p
->p_ucred
= my_new_cred
;
4696 /* update cred on proc */
4697 PROC_UPDATE_CREDS_ONPROC(p
);
4698 proc_ucred_unlock(p
);
4702 /* Drop old proc reference or our extra reference */
4703 kauth_cred_unref(&my_cred
);
4708 * for temporary binary compatibility
4710 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4712 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4714 return kauth_cred_label_update(cred
, label
);
4717 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4719 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4721 return kauth_proc_label_update(p
, label
);
4727 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4728 * Since we cannot build our export lists based on the kernel configuration we need
4732 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4738 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4745 * for temporary binary compatibility
4747 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4749 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4754 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4756 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4763 // TODO: move to os_refcnt once the ABI issue is resolved
4765 #define KAUTH_CRED_REF_MAX 0x0ffffffful
4767 __attribute__((noinline
, cold
, noreturn
))
4769 kauth_cred_panic_resurrection(kauth_cred_t cred
)
4771 panic("kauth_cred_unref: cred %p resurrected", cred
);
4772 __builtin_unreachable();
4775 __attribute__((noinline
, cold
, noreturn
))
4777 kauth_cred_panic_over_released(kauth_cred_t cred
)
4779 panic("kauth_cred_unref: cred %p over-released", cred
);
4780 __builtin_unreachable();
4783 __attribute__((noinline
, cold
, noreturn
))
4785 kauth_cred_panic_over_retain(kauth_cred_t cred
)
4787 panic("kauth_cred_ref: cred %p over-retained", cred
);
4788 __builtin_unreachable();
4794 * Description: Tries to take a reference, used from kauth_cred_find_and_ref
4795 * to debounce the race with kauth_cred_unref.
4797 * Parameters: cred The credential to reference
4799 * Returns: (bool) Whether the reference was taken
4802 kauth_cred_tryref(kauth_cred_t cred
)
4804 u_long old_ref
, new_ref
;
4805 os_atomic_rmw_loop(&cred
->cr_ref
, old_ref
, new_ref
, relaxed
, {
4807 os_atomic_rmw_loop_give_up(return false);
4809 new_ref
= old_ref
+ 1;
4811 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4812 kauth_cred_panic_over_retain(cred
);
4815 #if 0 // use this to watch a specific credential
4816 if (is_target_cred( *credp
) != 0) {
4827 * Description: Add a reference to the passed credential
4829 * Parameters: cred The credential to reference
4834 kauth_cred_ref(kauth_cred_t cred
)
4836 u_long old_ref
= os_atomic_inc_orig(&cred
->cr_ref
, relaxed
);
4838 if (__improbable(old_ref
< 1)) {
4839 kauth_cred_panic_resurrection(cred
);
4841 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4842 kauth_cred_panic_over_retain(cred
);
4845 #if 0 // use this to watch a specific credential
4846 if (is_target_cred( cred
) != 0) {
4853 * kauth_cred_unref_fast
4855 * Description: Release a credential reference.
4857 * Parameters: credp Pointer to address containing
4858 * credential to be freed
4860 * Returns: true This was the last reference.
4861 * false The object has more refs.
4865 kauth_cred_unref_fast(kauth_cred_t cred
)
4867 u_long old_ref
= os_atomic_dec_orig(&cred
->cr_ref
, relaxed
);
4869 #if 0 // use this to watch a specific credential
4870 if (is_target_cred( *credp
) != 0) {
4875 if (__improbable(old_ref
<= 0)) {
4876 kauth_cred_panic_over_released(cred
);
4878 return old_ref
== 1;
4884 * Description: Release a credential reference.
4885 * Frees the credential if it is the last ref.
4887 * Parameters: credp Pointer to address containing
4888 * credential to be freed
4893 * *credp Set to NOCRED
4897 kauth_cred_unref(kauth_cred_t
*credp
)
4899 if (kauth_cred_unref_fast(*credp
)) {
4900 KAUTH_CRED_HASH_LOCK();
4901 kauth_cred_remove_locked(*credp
);
4902 KAUTH_CRED_HASH_UNLOCK();
4903 kauth_cred_free(*credp
);
4914 * Description: release a credential reference; when the last reference is
4915 * released, the credential will be freed
4917 * Parameters: cred Credential to release
4921 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4922 * clear the pointer in the caller to avoid multiple releases of
4923 * the same credential. The currently recommended interface is
4924 * kauth_cred_unref().
4927 kauth_cred_rele(kauth_cred_t cred
)
4929 kauth_cred_unref(&cred
);
4931 #endif /* !__LP64__ */
4937 * Description: Duplicate a credential via alloc and copy; the new credential
4940 * Parameters: cred The credential to duplicate
4942 * Returns: (kauth_cred_t) The duplicate credential
4944 * Notes: The typical value to calling this routine is if you are going
4945 * to modify an existing credential, and expect to need a new one
4946 * from the hash cache.
4948 * This should probably not be used in the majority of cases;
4949 * if you are using it instead of kauth_cred_create(), you are
4950 * likely making a mistake.
4952 * The newly allocated credential is copied as part of the
4953 * allocation process, with the exception of the reference
4954 * count, which is set to 0 to indicate the caller still has
4955 * to call kauth_cred_add().
4957 * Since newly allocated credentials have no external pointers
4958 * referencing them, prior to making them visible in an externally
4959 * visible pointer (e.g. by adding them to the credential hash
4960 * cache) is the only legal time in which an existing credential
4961 * can be safely initialized or modified directly.
4963 * After initialization, the caller is expected to call the
4964 * function kauth_cred_add() to add the credential to the hash
4965 * cache, after which time it's frozen and becomes publicly
4968 * The release protocol depends on kauth_hash_add() being called
4969 * before kauth_cred_rele() (there is a diagnostic panic which
4970 * will trigger if this protocol is not observed).
4974 kauth_cred_dup(kauth_cred_t cred
)
4976 kauth_cred_t newcred
;
4978 assert(cred
!= NOCRED
&& cred
!= FSCRED
);
4979 newcred
= kauth_cred_alloc();
4980 if (newcred
!= NULL
) {
4981 newcred
->cr_posix
= cred
->cr_posix
;
4983 newcred
->cr_audit
= cred
->cr_audit
;
4986 mac_cred_label_associate(cred
, newcred
);
4988 AUDIT_SESSION_REF(cred
);
4994 * kauth_cred_copy_real
4996 * Description: Returns a credential based on the passed credential but which
4997 * reflects the real rather than effective UID and GID.
4999 * Parameters: cred The credential from which to
5000 * derive the new credential
5002 * Returns: (kauth_cred_t) The copied credential
5004 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
5005 * result, the caller is responsible for dropping BOTH the
5006 * additional reference on the passed cred (if any), and the
5007 * credential returned by this function. The drop should be
5008 * via the kauth_cred_unref() KPI.
5011 kauth_cred_copy_real(kauth_cred_t cred
)
5013 kauth_cred_t newcred
= NULL
, found_cred
;
5014 struct ucred temp_cred
;
5015 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
5016 posix_cred_t pcred
= posix_cred_get(cred
);
5018 /* if the credential is already 'real', just take a reference */
5019 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
5020 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
5021 kauth_cred_ref(cred
);
5026 * Look up in cred hash table to see if we have a matching credential
5027 * with the new values.
5030 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
5031 /* displacing a supplementary group opts us out of memberd */
5032 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
5033 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
5034 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
5037 * If the cred is not opted out, make sure we are using the r/euid
5040 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
) {
5041 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
5044 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
5046 KAUTH_CRED_HASH_LOCK();
5047 found_cred
= kauth_cred_find_and_ref(&temp_cred
, bucket
);
5048 KAUTH_CRED_HASH_UNLOCK();
5055 * Must allocate a new credential, copy in old credential
5056 * data and update the real user and group IDs.
5058 newcred
= kauth_cred_dup(&temp_cred
);
5059 return kauth_cred_add(newcred
, bucket
);
5066 * Description: Common code to update a credential
5068 * Parameters: old_cred Reference counted credential
5070 * model_cred Non-reference counted model
5071 * credential to apply to the
5072 * credential to be updated
5073 * retain_auditinfo Flag as to whether or not the
5074 * audit information should be
5075 * copied from the old_cred into
5078 * Returns: (kauth_cred_t) The updated credential
5080 * IMPORTANT: This function will potentially return a credential other than
5081 * the one it is passed, and if so, it will have dropped the
5082 * reference on the passed credential. All callers should be
5083 * aware of this, and treat this function as an unref + ref,
5084 * potentially on different credentials.
5086 * Because of this, the caller is expected to take its own
5087 * reference on the credential passed as the first parameter,
5088 * and be prepared to release the reference on the credential
5089 * that is returned to them, if it is not intended to be a
5090 * persistent reference.
5093 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
5094 boolean_t retain_auditinfo
)
5099 * Make sure we carry the auditinfo forward to the new credential
5100 * unless we are actually updating the auditinfo.
5102 if (retain_auditinfo
) {
5103 model_cred
->cr_audit
= old_cred
->cr_audit
;
5106 if (kauth_cred_is_equal(old_cred
, model_cred
)) {
5110 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(model_cred
);
5112 KAUTH_CRED_HASH_LOCK();
5113 cred
= kauth_cred_find_and_ref(model_cred
, bucket
);
5116 * We found a hit, so we can get rid of the old_cred.
5117 * If we didn't, then we need to keep the old_cred around,
5118 * because `model_cred` has copies of things such as the cr_label
5119 * or audit session that it has not refcounts for.
5121 bool needs_free
= kauth_cred_unref_fast(old_cred
);
5123 kauth_cred_remove_locked(old_cred
);
5125 KAUTH_CRED_HASH_UNLOCK();
5127 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n",
5130 kauth_cred_free(old_cred
);
5135 KAUTH_CRED_HASH_UNLOCK();
5138 * Must allocate a new credential using the model. also
5139 * adds the new credential to the credential hash table.
5141 cred
= kauth_cred_dup(model_cred
);
5142 cred
= kauth_cred_add(cred
, bucket
);
5143 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n",
5148 * This can't be done before the kauth_cred_dup() as the model_cred
5149 * has pointers that old_cred owns references for.
5151 kauth_cred_unref(&old_cred
);
5159 * Description: Add the given credential to our credential hash table and
5160 * take an initial reference to account for the object being
5163 * Parameters: new_cred Credential to insert into cred
5164 * hash cache, or to destroy when
5165 * a collision is detected.
5167 * Returns: (kauth_thread_t) The inserted cred, or the
5168 * collision that was found.
5170 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5173 kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
)
5175 kauth_cred_t found_cred
;
5178 KAUTH_CRED_HASH_LOCK();
5179 found_cred
= kauth_cred_find_and_ref(new_cred
, bucket
);
5181 KAUTH_CRED_HASH_UNLOCK();
5182 kauth_cred_free(new_cred
);
5186 old_ref
= os_atomic_xchg(&new_cred
->cr_ref
, 1, relaxed
);
5188 panic("kauth_cred_add: invalid cred %p", new_cred
);
5191 /* insert the credential into the hash table */
5192 LIST_INSERT_HEAD(bucket
, new_cred
, cr_link
);
5194 KAUTH_CRED_HASH_UNLOCK();
5199 * kauth_cred_remove_locked
5201 * Description: Remove the given credential from our credential hash table.
5203 * Parameters: cred Credential to remove.
5205 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5208 kauth_cred_remove_locked(kauth_cred_t cred
)
5210 KAUTH_CRED_HASH_LOCK_ASSERT();
5212 if (cred
->cr_link
.le_prev
== NULL
) {
5213 panic("kauth_cred_unref: cred %p never added", cred
);
5216 LIST_REMOVE(cred
, cr_link
);
5220 * kauth_cred_is_equal
5222 * Description: Returns whether two credentions are identical.
5224 * Parameters: cred1 Credential to compare
5225 * cred2 Credential to compare
5227 * Returns: true Credentials are equal
5228 * false Credentials are different
5231 kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
)
5233 posix_cred_t pcred1
= posix_cred_get(cred1
);
5234 posix_cred_t pcred2
= posix_cred_get(cred2
);
5237 * don't worry about the label unless the flags in
5238 * either credential tell us to.
5240 if (memcmp(pcred1
, pcred2
, sizeof(*pcred1
))) {
5243 if (memcmp(&cred1
->cr_audit
, &cred2
->cr_audit
, sizeof(cred1
->cr_audit
))) {
5247 /* Note: we know the flags are equal, so we only need to test one */
5248 if (pcred1
->cr_flags
& CRF_MAC_ENFORCE
) {
5249 if (!mac_cred_label_compare(cred1
->cr_label
, cred2
->cr_label
)) {
5258 * kauth_cred_find_and_ref
5260 * Description: Using the given credential data, look for a match in our
5261 * credential hash table
5263 * Parameters: cred Credential to lookup in cred
5266 * Returns: NULL Not found
5267 * !NULL Matching credential already in
5268 * cred hash cache, with a +1 ref
5270 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5273 kauth_cred_find_and_ref(kauth_cred_t cred
, struct kauth_cred_entry_head
*bucket
)
5275 kauth_cred_t found_cred
;
5277 KAUTH_CRED_HASH_LOCK_ASSERT();
5279 /* Find cred in the credential hash table */
5280 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5281 if (kauth_cred_is_equal(found_cred
, cred
)) {
5283 * newer entries are inserted at the head,
5284 * no hit further in the chain can possibly
5285 * be successfully retained.
5287 if (!kauth_cred_tryref(found_cred
)) {
5300 * Description: This interface is sadly KPI but people can't possibly use it,
5301 * as they need to hold a lock that isn't exposed.
5303 * Parameters: cred Credential to lookup in cred
5306 * Returns: NULL Not found
5307 * !NULL Matching credential already in
5310 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5313 kauth_cred_find(kauth_cred_t cred
)
5315 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
5316 kauth_cred_t found_cred
;
5318 KAUTH_CRED_HASH_LOCK_ASSERT();
5320 /* Find cred in the credential hash table */
5321 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5322 if (kauth_cred_is_equal(found_cred
, cred
)) {
5334 * Description: Generates a hash key using data that makes up a credential;
5337 * Parameters: datap Pointer to data to hash
5338 * data_len Count of bytes to hash
5339 * start_key Start key value
5341 * Returns: (u_long) Returned hash key
5343 static inline u_long
5344 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5346 u_long hash_key
= start_key
;
5349 while (data_len
> 0) {
5350 hash_key
= (hash_key
<< 4) + *datap
++;
5351 temp
= hash_key
& 0xF0000000;
5353 hash_key
^= temp
>> 24;
5363 * kauth_cred_get_bucket
5365 * Description: Generate a hash key using data that makes up a credential;
5366 * based on ElfHash. We hash on the entire credential data,
5367 * not including the ref count or the TAILQ, which are mutable;
5368 * everything else isn't.
5370 * Returns the bucket correspondong to this hash key.
5372 * Parameters: cred Credential for which hash is
5375 * Returns: (kauth_cred_entry_head *) Returned bucket.
5377 * Notes: When actually moving the POSIX credential into a real label,
5378 * remember to update this hash computation.
5380 static struct kauth_cred_entry_head
*
5381 kauth_cred_get_bucket(kauth_cred_t cred
)
5384 posix_cred_t pcred
= posix_cred_get(cred
);
5386 u_long hash_key
= 0;
5388 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5389 sizeof(struct posix_cred
),
5391 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5392 sizeof(struct au_session
),
5395 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5396 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5397 sizeof(struct label
),
5402 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5403 return &kauth_cred_table_anchor
[hash_key
];
5411 * Description: Print out an individual credential's contents for debugging
5414 * Parameters: cred The credential to print out
5418 * Implicit returns: Results in console output
5421 kauth_cred_print(kauth_cred_t cred
)
5425 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
);
5426 printf("group count %d gids ", cred
->cr_ngroups
);
5427 for (i
= 0; i
< NGROUPS
; i
++) {
5431 printf("%d ", cred
->cr_groups
[i
]);
5433 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5434 printf("auditinfo_addr %d %d %d %d %d %d\n",
5435 cred
->cr_audit
.s_aia_p
->ai_auid
,
5436 cred
->cr_audit
.as_mask
.am_success
,
5437 cred
->cr_audit
.as_mask
.am_failure
,
5438 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5439 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5440 cred
->cr_audit
.as_aia_p
->ai_asid
);
5444 is_target_cred( kauth_cred_t the_cred
)
5446 if (the_cred
->cr_uid
!= 0) {
5449 if (the_cred
->cr_ruid
!= 0) {
5452 if (the_cred
->cr_svuid
!= 0) {
5455 if (the_cred
->cr_ngroups
!= 11) {
5458 if (the_cred
->cr_groups
[0] != 11) {
5461 if (the_cred
->cr_groups
[1] != 81) {
5464 if (the_cred
->cr_groups
[2] != 63947) {
5467 if (the_cred
->cr_groups
[3] != 80288) {
5470 if (the_cred
->cr_groups
[4] != 89006) {
5473 if (the_cred
->cr_groups
[5] != 52173) {
5476 if (the_cred
->cr_groups
[6] != 84524) {
5479 if (the_cred
->cr_groups
[7] != 79) {
5482 if (the_cred
->cr_groups
[8] != 80292) {
5485 if (the_cred
->cr_groups
[9] != 80) {
5488 if (the_cred
->cr_groups
[10] != 90824) {
5491 if (the_cred
->cr_rgid
!= 11) {
5494 if (the_cred
->cr_svgid
!= 11) {
5497 if (the_cred
->cr_gmuid
!= 3475) {
5500 if (the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475) {
5504 * if ( the_cred->cr_audit.as_mask.am_success != 0 )
5506 * if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5508 * if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5510 * if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5512 * if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5514 * if ( the_cred->cr_flags != 0 )
5517 return -1; // found target cred
5521 get_backtrace( void )
5524 void * my_stack
[MAX_STACK_DEPTH
];
5527 if (cred_debug_buf_p
== NULL
) {
5528 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5529 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5532 if (cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1)) {
5533 /* buffer is full */
5537 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5538 if (my_depth
== 0) {
5539 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5543 /* fill new backtrace */
5544 my_slot
= cred_debug_buf_p
->next_slot
;
5545 cred_debug_buf_p
->next_slot
++;
5546 cred_debug_buf_p
->stack_buffer
[my_slot
].depth
= my_depth
;
5547 for (i
= 0; i
< my_depth
; i
++) {
5548 cred_debug_buf_p
->stack_buffer
[my_slot
].stack
[i
] = my_stack
[i
];
5555 /* subset of struct ucred for use in sysctl_dump_creds */
5556 struct debug_ucred
{
5558 u_long cr_ref
; /* reference count */
5559 uid_t cr_uid
; /* effective user id */
5560 uid_t cr_ruid
; /* real user id */
5561 uid_t cr_svuid
; /* saved user id */
5562 u_short cr_ngroups
; /* number of groups in advisory list */
5563 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5564 gid_t cr_rgid
; /* real group id */
5565 gid_t cr_svgid
; /* saved group id */
5566 uid_t cr_gmuid
; /* UID for group membership purposes */
5567 struct auditinfo_addr cr_audit
; /* user auditing data. */
5568 void *cr_label
; /* MACF label */
5569 int cr_flags
; /* flags on credential */
5571 typedef struct debug_ucred debug_ucred
;
5573 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5574 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5577 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5581 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5583 int i
, j
, counter
= 0;
5586 kauth_cred_t found_cred
;
5587 debug_ucred
* cred_listp
;
5588 debug_ucred
* nextp
;
5590 /* This is a readonly node. */
5591 if (req
->newptr
!= USER_ADDR_NULL
) {
5595 /* calculate space needed */
5596 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5597 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5602 /* they are querying us so just return the space required. */
5603 if (req
->oldptr
== USER_ADDR_NULL
) {
5604 counter
+= 10; // add in some padding;
5605 req
->oldidx
= counter
* sizeof(debug_ucred
);
5609 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5610 if (cred_listp
== NULL
) {
5614 /* fill in creds to send back */
5617 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5618 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5619 nextp
->credp
= found_cred
;
5620 nextp
->cr_ref
= found_cred
->cr_ref
;
5621 nextp
->cr_uid
= found_cred
->cr_uid
;
5622 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5623 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5624 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5625 for (j
= 0; j
< nextp
->cr_ngroups
; j
++) {
5626 nextp
->cr_groups
[j
] = found_cred
->cr_groups
[j
];
5628 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5629 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5630 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5631 nextp
->cr_audit
.ai_auid
=
5632 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5633 nextp
->cr_audit
.ai_mask
.am_success
=
5634 found_cred
->cr_audit
.as_mask
.am_success
;
5635 nextp
->cr_audit
.ai_mask
.am_failure
=
5636 found_cred
->cr_audit
.as_mask
.am_failure
;
5637 nextp
->cr_audit
.ai_termid
.at_port
=
5638 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5639 nextp
->cr_audit
.ai_termid
.at_type
=
5640 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5641 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5642 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5643 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5644 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5645 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5646 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5647 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5648 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5649 nextp
->cr_audit
.ai_asid
=
5650 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5651 nextp
->cr_audit
.ai_flags
=
5652 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5653 nextp
->cr_label
= found_cred
->cr_label
;
5654 nextp
->cr_flags
= found_cred
->cr_flags
;
5656 space
+= sizeof(debug_ucred
);
5657 if (space
> req
->oldlen
) {
5658 FREE(cred_listp
, M_TEMP
);
5663 req
->oldlen
= space
;
5664 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5665 FREE(cred_listp
, M_TEMP
);
5670 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5671 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5674 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5678 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5683 cred_debug_buffer
* bt_bufp
;
5684 cred_backtrace
* nextp
;
5686 /* This is a readonly node. */
5687 if (req
->newptr
!= USER_ADDR_NULL
) {
5691 if (cred_debug_buf_p
== NULL
) {
5695 /* calculate space needed */
5696 space
= sizeof(cred_debug_buf_p
->next_slot
);
5697 space
+= (sizeof(cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5699 /* they are querying us so just return the space required. */
5700 if (req
->oldptr
== USER_ADDR_NULL
) {
5701 req
->oldidx
= space
;
5705 if (space
> req
->oldlen
) {
5709 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5710 if (bt_bufp
== NULL
) {
5714 /* fill in backtrace info to send back */
5715 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5716 space
= sizeof(bt_bufp
->next_slot
);
5718 nextp
= &bt_bufp
->stack_buffer
[0];
5719 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5720 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[i
].depth
;
5721 for (j
= 0; j
< nextp
->depth
; j
++) {
5722 nextp
->stack
[j
] = cred_debug_buf_p
->stack_buffer
[i
].stack
[j
];
5724 space
+= sizeof(*nextp
);
5727 req
->oldlen
= space
;
5728 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5729 FREE(bt_bufp
, M_TEMP
);
5733 #endif /* DEBUG_CRED */
5737 **********************************************************************
5738 * The following routines will be moved to a policy_posix.c module at
5739 * some future point.
5740 **********************************************************************
5746 * Description: Helper function to create a kauth_cred_t credential that is
5747 * initally labelled with a specific POSIX credential label
5749 * Parameters: pcred The posix_cred_t to use as the initial
5752 * Returns: (kauth_cred_t) The credential that was found in the
5754 * NULL kauth_cred_add() failed, or there was
5755 * no egid specified, or we failed to
5756 * attach a label to the new credential
5758 * Notes: This function currently wraps kauth_cred_create(), and is the
5759 * only consumer of that ill-fated function, apart from bsd_init().
5760 * It exists solely to support the NFS server code creation of
5761 * credentials based on the over-the-wire RPC calls containing
5762 * traditional POSIX credential information being tunneled to
5763 * the server host from the client machine.
5765 * In the future, we hope this function goes away.
5767 * In the short term, it creates a temporary credential, puts
5768 * the POSIX information from NFS into it, and then calls
5769 * kauth_cred_create(), as an internal implementation detail.
5771 * If we have to keep it around in the medium term, it will
5772 * create a new kauth_cred_t, then label it with a POSIX label
5773 * corresponding to the contents of the kauth_cred_t. If the
5774 * policy_posix MACF module is not loaded, it will instead
5775 * substitute a posix_cred_t which GRANTS all access (effectively
5776 * a "root" credential) in order to not prevent NFS from working
5777 * in the case that we are not supporting POSIX credentials.
5780 posix_cred_create(posix_cred_t pcred
)
5782 struct ucred temp_cred
;
5784 bzero(&temp_cred
, sizeof(temp_cred
));
5785 temp_cred
.cr_posix
= *pcred
;
5787 return kauth_cred_create(&temp_cred
);
5794 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5795 * any, which is associated with it.
5797 * Parameters: cred The credential to obtain the label from
5799 * Returns: posix_cred_t The POSIX credential label
5801 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5802 * this function will return a pointer to a posix_cred_t which
5803 * GRANTS all access (effectively, a "root" credential). This is
5804 * necessary to support legacy code which insists on tightly
5805 * integrating POSIX credentials into its APIs, including, but
5806 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5807 * NFSv3, signals, dtrace, and a large number of kauth routines
5808 * used to implement POSIX permissions related system calls.
5810 * In the event that the policy_posix MACF module IS loaded, and
5811 * there is no POSIX label on the kauth_cred_t credential, this
5812 * function will return a pointer to a posix_cred_t which DENIES
5813 * all access (effectively, a "deny rights granted by POSIX"
5814 * credential). This is necessary to support the concept of a
5815 * transiently loaded POSIX policy, or kauth_cred_t credentials
5816 * which can not be used in conjunctions with POSIX permissions
5819 * This function currently returns the address of the cr_posix
5820 * field of the supplied kauth_cred_t credential, and as such
5821 * currently can not fail. In the future, this will not be the
5825 posix_cred_get(kauth_cred_t cred
)
5827 return &cred
->cr_posix
;
5834 * Description: Label a kauth_cred_t with a POSIX credential label
5836 * Parameters: cred The credential to label
5837 * pcred The POSIX credential t label it with
5841 * Notes: This function is currently void in order to permit it to fit
5842 * in with the current MACF framework label methods which allow
5843 * labeling to fail silently. This is like acceptable for
5844 * mandatory access controls, but not for POSIX, since those
5845 * access controls are advisory. We will need to consider a
5846 * return value in a future version of the MACF API.
5848 * This operation currently cannot fail, as currently the POSIX
5849 * credential is a subfield of the kauth_cred_t (ucred), which
5850 * MUST be valid. In the future, this will not be the case.
5853 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5855 cred
->cr_posix
= *pcred
; /* structure assign for now */
5862 * Description: Perform a POSIX access check for a protected object
5864 * Parameters: cred The credential to check
5865 * object_uid The POSIX UID of the protected object
5866 * object_gid The POSIX GID of the protected object
5867 * object_mode The POSIX mode of the protected object
5868 * mode_req The requested POSIX access rights
5870 * Returns 0 Access is granted
5871 * EACCES Access is denied
5873 * Notes: This code optimizes the case where the world and group rights
5874 * would both grant the requested rights to avoid making a group
5875 * membership query. This is a big performance win in the case
5876 * where this is true.
5879 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5882 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5883 mode_t mode_group
= (mode_t
)((object_mode
& S_IRWXG
) << 3);
5884 mode_t mode_world
= (mode_t
)((object_mode
& S_IRWXO
) << 6);
5887 * Check first for owner rights
5889 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
) {
5894 * Combined group and world rights check, if we don't have owner rights
5896 * OPTIMIZED: If group and world rights would grant the same bits, and
5897 * they set of requested bits is in both, then we can simply check the
5898 * world rights, avoiding a group membership check, which is expensive.
5900 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5904 * NON-OPTIMIZED: requires group membership check.
5906 if ((mode_req
& mode_group
) != mode_req
) {
5908 * exclusion group : treat errors as "is a member"
5910 * NON-OPTIMIZED: +group would deny; must check group
5912 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5914 * DENY: +group denies
5918 if ((mode_req
& mode_world
) != mode_req
) {
5920 * DENY: both -group & world would deny
5925 * ALLOW: allowed by -group and +world
5932 * inclusion group; treat errors as "not a member"
5934 * NON-OPTIMIZED: +group allows, world denies; must
5937 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5939 * ALLOW: allowed by +group
5943 if ((mode_req
& mode_world
) != mode_req
) {
5945 * DENY: both -group & world would deny
5950 * ALLOW: allowed by -group and +world