2 * Copyright (c) 2004-2011 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
;
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
, int 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 LIST_HEAD(kauth_cred_entry_head
, ucred
);
286 static struct kauth_cred_entry_head
287 kauth_cred_table_anchor
[KAUTH_CRED_TABLE_SIZE
];
289 static struct kauth_cred_entry_head
*kauth_cred_get_bucket(kauth_cred_t cred
);
290 static kauth_cred_t
kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
);
291 static void kauth_cred_remove_locked(kauth_cred_t cred
);
292 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
293 static kauth_cred_t
kauth_cred_find_and_ref(kauth_cred_t cred
,
294 struct kauth_cred_entry_head
*bucket
);
295 static bool kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
);
297 #if CONFIG_EXT_RESOLVER
300 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
302 * Description: Waits for the user space daemon to respond to the request
303 * we made. Function declared non inline to be visible in
304 * stackshots and spindumps as well as debugging.
306 * Parameters: workp Work queue entry.
308 * Returns: 0 on Success.
309 * EIO if Resolver is dead.
310 * EINTR thread interrupted in msleep
311 * EWOULDBLOCK thread timed out in msleep
312 * ERESTART returned by msleep.
315 static __attribute__((noinline
)) int
316 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
317 struct kauth_resolver_work
*workp
)
322 /* we could compute a better timeout here */
323 ts
.tv_sec
= kauth_resolver_timeout
;
325 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
326 /* request has been completed? */
327 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
)) {
330 /* woken because the resolver has died? */
331 if (kauth_resolver_identity
== 0) {
332 RESOLVER_FAILED_MESSAGE("kauth external resolver died while while waiting for work to complete");
333 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
346 * kauth_resolver_init
348 * Description: Initialize the daemon side of the credential identity resolver
354 * Notes: Initialize the credential identity resolver for use; the
355 * credential identity resolver is the KPI used by the user
356 * space credential identity resolver daemon to communicate
357 * with the kernel via the identitysvc() system call..
359 * This is how membership in more than 16 groups (1 effective
360 * and 15 supplementary) is supported, and also how UID's,
361 * UUID's, and so on, are translated to/from POSIX credential
364 * The credential identity resolver operates by attempting to
365 * determine identity first from the credential, then from
366 * the kernel credential identity cache, and finally by
367 * enqueueing a request to a user space daemon.
369 * This function is called from kauth_init() in the file
370 * kern_authorization.c.
373 kauth_resolver_init(void)
375 TAILQ_INIT(&kauth_resolver_unsubmitted
);
376 TAILQ_INIT(&kauth_resolver_submitted
);
377 TAILQ_INIT(&kauth_resolver_done
);
378 kauth_resolver_sequence
= 31337;
379 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
384 * kauth_resolver_submit
386 * Description: Submit an external credential identity resolution request to
387 * the user space daemon.
389 * Parameters: lkp A pointer to an external
391 * extend_data extended data for kr_extend
394 * EWOULDBLOCK No resolver registered
395 * EINTR Operation interrupted (e.g. by
397 * ENOMEM Could not allocate work item
398 * copyinstr:EFAULT Bad message from user space
399 * workp->kr_result:??? An error from the user space
400 * daemon (includes ENOENT!)
405 * Notes: Allocate a work queue entry, submit the work and wait for
406 * the operation to either complete or time out. Outstanding
407 * operations may also be cancelled.
409 * Submission is by means of placing the item on a work queue
410 * which is serviced by an external resolver thread calling
411 * into the kernel. The caller then sleeps until timeout,
412 * cancellation, or an external resolver thread calls in with
413 * a result message to kauth_resolver_complete(). All of these
414 * events wake the caller back up.
416 * This code is called from either kauth_cred_ismember_gid()
417 * for a group membership request, or it is called from
418 * kauth_cred_cache_lookup() when we get a cache miss.
421 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
423 struct kauth_resolver_work
*workp
, *killp
;
425 int error
, shouldfree
;
427 /* no point actually blocking if the resolver isn't up yet */
428 if (kauth_resolver_identity
== 0) {
430 * We've already waited an initial <kauth_resolver_timeout>
431 * seconds with no result.
433 * Sleep on a stack address so no one wakes us before timeout;
434 * we sleep a half a second in case we are a high priority
435 * process, so that memberd doesn't starve while we are in a
436 * tight loop between user and kernel, eating all the CPU.
438 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/ 2);
439 if (kauth_resolver_identity
== 0) {
441 * if things haven't changed while we were asleep,
442 * tell the caller we couldn't get an authoritative
449 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
454 workp
->kr_work
= *lkp
;
455 workp
->kr_extend
= extend_data
;
457 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
458 workp
->kr_result
= 0;
461 * We insert the request onto the unsubmitted queue, the call in from
462 * the resolver will it to the submitted thread when appropriate.
464 KAUTH_RESOLVER_LOCK();
465 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
466 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
469 * XXX We *MUST NOT* attempt to coalesce identical work items due to
470 * XXX the inability to ensure order of update of the request item
471 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
472 * XXX for each item repeat the update when they wake up.
474 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
477 * Wake up an external resolver thread to deal with the new work; one
478 * may not be available, and if not, then the request will be grabbed
479 * when a resolver thread comes back into the kernel to request new
482 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
483 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
485 /* if the request was processed, copy the result */
487 *lkp
= workp
->kr_work
;
490 if (error
== EWOULDBLOCK
) {
491 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
492 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
493 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
496 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
498 * If the request timed out and was never collected, the resolver
499 * is dead and probably not coming back anytime soon. In this
500 * case we revert to no-resolver behaviour, and punt all the other
501 * sleeping requests to clear the backlog.
503 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
506 * Make the current resolver non-authoritative, and mark it as
507 * no longer registered to prevent kauth_cred_ismember_gid()
508 * enqueueing more work until a new one is registered. This
509 * mitigates the damage a crashing resolver may inflict.
511 kauth_resolver_identity
= 0;
512 kauth_resolver_registered
= 0;
514 /* kill all the other requestes that are waiting as well */
515 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
517 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
519 /* Cause all waiting-for-work threads to return EIO */
520 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
525 * drop our reference on the work item, and note whether we should
528 if (--workp
->kr_refs
<= 0) {
529 /* work out which list we have to remove it from */
530 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
531 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
532 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
533 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
534 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
535 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
537 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
541 /* someone else still has a reference on this request */
545 /* collect request result */
547 error
= workp
->kr_result
;
549 KAUTH_RESOLVER_UNLOCK();
552 * If we dropped the last reference, free the request.
555 FREE(workp
, M_KAUTH
);
558 KAUTH_DEBUG("RESOLVER - returning %d", error
);
566 * Description: System call interface for the external identity resolver.
568 * Parameters: uap->message Message from daemon to kernel
570 * Returns: 0 Successfully became resolver
571 * EPERM Not the resolver process
572 * kauth_authorize_generic:EPERM Not root user
573 * kauth_resolver_complete:EIO
574 * kauth_resolver_complete:EFAULT
575 * kauth_resolver_getwork:EINTR
576 * kauth_resolver_getwork:EFAULT
578 * Notes: This system call blocks until there is work enqueued, at
579 * which time the kernel wakes it up, and a message from the
580 * kernel is copied out to the identity resolution daemon, which
581 * proceed to attempt to resolve it. When the resolution has
582 * completed (successfully or not), the daemon called back into
583 * this system call to give the result to the kernel, and wait
584 * for the next request.
587 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
589 int opcode
= uap
->opcode
;
590 user_addr_t message
= uap
->message
;
591 struct kauth_resolver_work
*workp
;
592 struct kauth_cache_sizes sz_arg
= {};
596 if (!IOTaskHasEntitlement(current_task(), IDENTITYSVC_ENTITLEMENT
)) {
597 KAUTH_DEBUG("RESOLVER - pid %d not entitled to call identitysvc", current_proc()->p_pid
);
602 * New server registering itself.
604 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
605 new_id
= current_proc()->p_pid
;
606 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
607 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
610 KAUTH_RESOLVER_LOCK();
611 if (kauth_resolver_identity
!= new_id
) {
612 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
614 * We have a new server, so assume that all the old requests have been lost.
616 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
617 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
618 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
619 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
620 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
623 * Allow user space resolver to override the
624 * external resolution timeout
626 if (message
> 30 && message
< 10000) {
627 kauth_resolver_timeout
= message
;
628 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
630 kauth_resolver_identity
= new_id
;
631 kauth_resolver_registered
= 1;
632 kauth_identitysvc_has_registered
= 1;
633 wakeup(&kauth_resolver_unsubmitted
);
635 KAUTH_RESOLVER_UNLOCK();
640 * Beyond this point, we must be the resolver process. We verify this
641 * by confirming the resolver credential and pid.
643 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
644 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
648 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
649 KAUTH_IDENTITY_LOCK();
650 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
651 KAUTH_IDENTITY_UNLOCK();
654 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
655 KAUTH_GROUPS_UNLOCK();
657 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof(sz_arg
))) != 0) {
662 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
663 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof(sz_arg
))) != 0) {
667 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
668 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
672 KAUTH_IDENTITY_LOCK();
673 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
674 kauth_identity_trimcache(kauth_identity_cachemax
);
675 KAUTH_IDENTITY_UNLOCK();
678 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
679 kauth_groups_trimcache(kauth_groups_cachemax
);
680 KAUTH_GROUPS_UNLOCK();
683 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
684 KAUTH_IDENTITY_LOCK();
685 kauth_identity_trimcache(0);
686 KAUTH_IDENTITY_UNLOCK();
689 kauth_groups_trimcache(0);
690 KAUTH_GROUPS_UNLOCK();
691 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
693 * Terminate outstanding requests; without an authoritative
694 * resolver, we are now back on our own authority.
696 struct kauth_resolver_work
*killp
;
698 KAUTH_RESOLVER_LOCK();
701 * Clear the identity, but also mark it as unregistered so
702 * there is no explicit future expectation of us getting a
703 * new resolver any time soon.
705 kauth_resolver_identity
= 0;
706 kauth_resolver_registered
= 0;
708 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
710 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
712 /* Cause all waiting-for-work threads to return EIO */
713 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
714 KAUTH_RESOLVER_UNLOCK();
718 * Got a result returning?
720 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
721 if ((error
= kauth_resolver_complete(message
)) != 0) {
727 * Caller wants to take more work?
729 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
730 if ((error
= kauth_resolver_getwork(message
)) != 0) {
740 * kauth_resolver_getwork_continue
742 * Description: Continuation for kauth_resolver_getwork
744 * Parameters: result Error code or 0 for the sleep
745 * that got us to this function
748 * EINTR Interrupted (e.g. by signal)
749 * kauth_resolver_getwork2:EFAULT
751 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
755 kauth_resolver_getwork_continue(int result
)
762 KAUTH_RESOLVER_UNLOCK();
767 * If we lost a race with another thread/memberd restarting, then we
768 * need to go back to sleep to look for more work. If it was memberd
769 * restarting, then the msleep0() will error out here, as our thread
770 * will already be "dead".
772 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
775 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
777 * If this is a wakeup from another thread in the resolver
778 * deregistering it, error out the request-for-work thread
780 if (!kauth_resolver_identity
) {
781 RESOLVER_FAILED_MESSAGE("external resolver died");
782 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
784 KAUTH_RESOLVER_UNLOCK();
788 thread
= current_thread();
789 ut
= get_bsdthread_info(thread
);
790 message
= ut
->uu_save
.uus_kauth
.message
;
791 return kauth_resolver_getwork2(message
);
796 * kauth_resolver_getwork2
798 * Decription: Common utility function to copy out a identity resolver work
799 * item from the kernel to user space as part of the user space
800 * identity resolver requesting work.
802 * Parameters: message message to user space
805 * EFAULT Bad user space message address
807 * Notes: This common function exists to permit the use of continuations
808 * in the identity resolution process. This frees up the stack
809 * while we are waiting for the user space resolver to complete
810 * a request. This is specifically used so that our per thread
811 * cost can be small, and we will therefore be willing to run a
812 * larger number of threads in the user space identity resolver.
815 kauth_resolver_getwork2(user_addr_t message
)
817 struct kauth_resolver_work
*workp
;
821 * Note: We depend on the caller protecting us from a NULL work item
822 * queue, since we must have the kauth resolver lock on entry to this
825 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
828 * Copy out the external lookup structure for the request, not
829 * including the el_extend field, which contains the address of the
830 * external buffer provided by the external resolver into which we
831 * copy the extension request information.
834 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
835 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
839 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
840 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
841 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
842 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
847 * Handle extended requests here; if we have a request of a type where
848 * the kernel wants a translation of extended information, then we need
849 * to copy it out into the extended buffer, assuming the buffer is
850 * valid; we only attempt to get the buffer address if we have request
851 * data to copy into it.
855 * translate a user@domain string into a uid/gid/whatever
857 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
860 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
862 size_t actual
; /* not used */
864 * Use copyoutstr() to reduce the copy size; we let
865 * this catch a NULL uaddr because we shouldn't be
866 * asking in that case anyway.
868 error
= copyoutstr(CAST_DOWN(void *, workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
871 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
875 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
876 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
877 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
878 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
881 KAUTH_RESOLVER_UNLOCK();
887 * kauth_resolver_getwork
889 * Description: Get a work item from the enqueued requests from the kernel and
890 * give it to the user space daemon.
892 * Parameters: message message to user space
895 * EINTR Interrupted (e.g. by signal)
896 * kauth_resolver_getwork2:EFAULT
898 * Notes: This function blocks in a continuation if there are no work
899 * items available for processing at the time the user space
900 * identity resolution daemon makes a request for work. This
901 * permits a large number of threads to be used by the daemon,
902 * without using a lot of wired kernel memory when there are no
903 * actual request outstanding.
906 kauth_resolver_getwork(user_addr_t message
)
908 struct kauth_resolver_work
*workp
;
911 KAUTH_RESOLVER_LOCK();
913 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
914 thread_t thread
= current_thread();
915 struct uthread
*ut
= get_bsdthread_info(thread
);
917 ut
->uu_save
.uus_kauth
.message
= message
;
918 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
919 KAUTH_RESOLVER_UNLOCK();
921 * If this is a wakeup from another thread in the resolver
922 * deregistering it, error out the request-for-work thread
924 if (!kauth_resolver_identity
) {
925 printf("external resolver died");
926 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
930 return kauth_resolver_getwork2(message
);
935 * kauth_resolver_complete
937 * Description: Return a result from userspace.
939 * Parameters: message message from user space
942 * EIO The resolver is dead
943 * copyin:EFAULT Bad message from user space
946 kauth_resolver_complete(user_addr_t message
)
948 struct kauth_identity_extlookup extl
;
949 struct kauth_resolver_work
*workp
;
950 struct kauth_resolver_work
*killp
;
951 int error
, result
, want_extend_data
;
954 * Copy in the mesage, including the extension field, since we are
955 * copying into a local variable.
957 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
958 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
962 KAUTH_RESOLVER_LOCK();
966 switch (extl
.el_result
) {
967 case KAUTH_EXTLOOKUP_INPROG
:
971 /* XXX this should go away once memberd is updated */
973 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
979 case KAUTH_EXTLOOKUP_SUCCESS
:
982 case KAUTH_EXTLOOKUP_FATAL
:
983 /* fatal error means the resolver is dead */
984 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
985 RESOLVER_FAILED_MESSAGE("resolver %d died, waiting for a new one", kauth_resolver_identity
);
987 * Terminate outstanding requests; without an authoritative
988 * resolver, we are now back on our own authority. Tag the
989 * resolver unregistered to prevent kauth_cred_ismember_gid()
990 * enqueueing more work until a new one is registered. This
991 * mitigates the damage a crashing resolver may inflict.
993 kauth_resolver_identity
= 0;
994 kauth_resolver_registered
= 0;
996 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
998 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
1000 /* Cause all waiting-for-work threads to return EIO */
1001 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
1002 /* and return EIO to the caller */
1003 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1006 case KAUTH_EXTLOOKUP_BADRQ
:
1007 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
1011 case KAUTH_EXTLOOKUP_FAILURE
:
1012 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
1013 RESOLVER_FAILED_MESSAGE("resolver reported transient failure for request %d", extl
.el_seqno
);
1014 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1018 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
1019 RESOLVER_FAILED_MESSAGE("resolver returned unexpected status %d", extl
.el_result
);
1020 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1025 * In the case of a fatal error, we assume that the resolver will
1026 * restart quickly and re-collect all of the outstanding requests.
1027 * Thus, we don't complete the request which returned the fatal
1030 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1031 /* scan our list for this request */
1032 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1034 if (workp
->kr_seqno
== extl
.el_seqno
) {
1036 * Do we want extend_data?
1038 want_extend_data
= (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_WANT_PWNAM
| KAUTH_EXTLOOKUP_WANT_GRNAM
));
1041 * Get the request of the submitted queue so
1042 * that it is not cleaned up out from under
1045 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1046 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1047 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1048 workp
->kr_result
= result
;
1050 /* Copy the result message to the work item. */
1051 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1054 * Check if we have a result in the extension
1055 * field; if we do, then we need to separately
1056 * copy the data from the message el_extend
1057 * into the request buffer that's in the work
1058 * item. We have to do it here because we do
1059 * not want to wake up the waiter until the
1060 * data is in their buffer, and because the
1061 * actual request response may be destroyed
1062 * by the time the requester wakes up, and they
1063 * do not have access to the user space buffer
1066 * It is safe to drop and reacquire the lock
1067 * here because we've already removed the item
1068 * from the submission queue, but have not yet
1069 * moved it to the completion queue. Note that
1070 * near simultaneous requests may result in
1071 * duplication of requests for items in this
1072 * window. This should not be a performance
1073 * issue and is easily detectable by comparing
1074 * time to live on last response vs. time of
1075 * next request in the resolver logs.
1077 * A malicious/faulty resolver could overwrite
1078 * part of a user's address space if they return
1079 * flags that mismatch the original request's flags.
1081 if (want_extend_data
&& (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
))) {
1082 size_t actual
; /* notused */
1084 KAUTH_RESOLVER_UNLOCK();
1085 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1086 KAUTH_DEBUG("RESOLVER - resolver got name :%*s: len = %d\n", (int)actual
,
1087 actual
? "null" : (char *)extl
.el_extend
, actual
);
1088 KAUTH_RESOLVER_LOCK();
1089 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1091 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1092 extl
.el_flags
, want_extend_data
);
1096 * Move the completed work item to the
1097 * completion queue and wake up requester(s)
1099 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1106 * Note that it's OK for us not to find anything; if the request has
1107 * timed out the work record will be gone.
1109 KAUTH_RESOLVER_UNLOCK();
1113 #endif /* CONFIG_EXT_RESOLVER */
1120 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1121 #define KI_VALID_GID (1<<1)
1122 #define KI_VALID_GUID (1<<2)
1123 #define KI_VALID_NTSID (1<<3)
1124 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1125 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1126 #define KI_VALID_GROUPS (1<<6)
1128 #if CONFIG_EXT_RESOLVER
1130 * kauth_identity_init
1132 * Description: Initialize the kernel side of the credential identity resolver
1134 * Parameters: (void)
1138 * Notes: Initialize the credential identity resolver for use; the
1139 * credential identity resolver is the KPI used to communicate
1140 * with a user space credential identity resolver daemon.
1142 * This function is called from kauth_init() in the file
1143 * kern_authorization.c.
1146 kauth_identity_init(void)
1148 TAILQ_INIT(&kauth_identities
);
1149 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
1154 * kauth_identity_alloc
1156 * Description: Allocate and fill out a kauth_identity structure for
1157 * translation between {UID|GID}/GUID/NTSID
1161 * Returns: NULL Insufficient memory to satisfy
1162 * the request or bad parameters
1163 * !NULL A pointer to the allocated
1164 * structure, filled in
1166 * Notes: It is illegal to translate between UID and GID; any given UUID
1167 * or NTSID can only refer to an NTSID or UUID (respectively),
1168 * and *either* a UID *or* a GID, but not both.
1170 static struct kauth_identity
*
1171 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1172 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1173 const char *name
, int nametype
)
1175 struct kauth_identity
*kip
;
1177 /* get and fill in a new identity */
1178 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1180 if (gid
!= KAUTH_GID_NONE
) {
1182 kip
->ki_valid
= KI_VALID_GID
;
1184 if (uid
!= KAUTH_UID_NONE
) {
1185 if (kip
->ki_valid
& KI_VALID_GID
) {
1186 panic("can't allocate kauth identity with both uid and gid");
1189 kip
->ki_valid
= KI_VALID_UID
;
1193 * A malicious/faulty resolver could return bad values
1195 assert(supgrpcnt
>= 0);
1196 assert(supgrpcnt
<= NGROUPS
);
1197 assert(supgrps
!= NULL
);
1199 if ((supgrpcnt
< 0) || (supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1202 if (kip
->ki_valid
& KI_VALID_GID
) {
1203 panic("can't allocate kauth identity with both gid and supplementary groups");
1205 kip
->ki_supgrpcnt
= supgrpcnt
;
1206 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1207 kip
->ki_valid
|= KI_VALID_GROUPS
;
1209 kip
->ki_groups_expiry
= groups_expiry
;
1210 if (guidp
!= NULL
) {
1211 kip
->ki_guid
= *guidp
;
1212 kip
->ki_valid
|= KI_VALID_GUID
;
1214 kip
->ki_guid_expiry
= guid_expiry
;
1215 if (ntsidp
!= NULL
) {
1216 kip
->ki_ntsid
= *ntsidp
;
1217 kip
->ki_valid
|= KI_VALID_NTSID
;
1219 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1221 kip
->ki_name
= name
;
1222 kip
->ki_valid
|= nametype
;
1230 * kauth_identity_register_and_free
1232 * Description: Register an association between identity tokens. The passed
1233 * 'kip' is consumed by this function.
1235 * Parameters: kip Pointer to kauth_identity
1236 * structure to register
1240 * Notes: The memory pointer to by 'kip' is assumed to have been
1241 * previously allocated via kauth_identity_alloc().
1244 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1246 struct kauth_identity
*ip
;
1249 * We search the cache for the UID listed in the incoming association.
1250 * If we already have an entry, the new information is merged.
1253 KAUTH_IDENTITY_LOCK();
1254 if (kip
->ki_valid
& KI_VALID_UID
) {
1255 if (kip
->ki_valid
& KI_VALID_GID
) {
1256 panic("kauth_identity: can't insert record with both UID and GID as key");
1258 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1259 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
)) {
1262 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1263 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1264 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
)) {
1268 panic("kauth_identity: can't insert record without UID or GID as key");
1272 /* we already have an entry, merge/overwrite */
1273 if (kip
->ki_valid
& KI_VALID_GUID
) {
1274 ip
->ki_guid
= kip
->ki_guid
;
1275 ip
->ki_valid
|= KI_VALID_GUID
;
1277 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1278 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1279 ip
->ki_ntsid
= kip
->ki_ntsid
;
1280 ip
->ki_valid
|= KI_VALID_NTSID
;
1282 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1283 /* a valid ki_name field overwrites the previous name field */
1284 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1285 /* if there's an old one, discard it */
1286 const char *oname
= NULL
;
1287 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1288 oname
= ip
->ki_name
;
1290 ip
->ki_name
= kip
->ki_name
;
1291 kip
->ki_name
= oname
;
1293 /* and discard the incoming entry */
1297 * if we don't have any information on this identity, add it;
1298 * if it pushes us over our limit, discard the oldest one.
1300 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1301 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1302 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1303 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1304 kauth_identity_count
--;
1307 KAUTH_IDENTITY_UNLOCK();
1308 /* have to drop lock before freeing expired entry (it may be in use) */
1310 /* if the ki_name field is used, clear it first */
1311 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1312 vfs_removename(ip
->ki_name
);
1314 /* free the expired entry */
1321 * kauth_identity_updatecache
1323 * Description: Given a lookup result, add any associations that we don't
1324 * currently have; replace ones which have changed.
1326 * Parameters: elp External lookup result from
1327 * user space daemon to kernel
1328 * rkip pointer to returned kauth
1330 * extend_data Extended data (can vary)
1335 * *rkip Modified (if non-NULL)
1337 * Notes: For extended information requests, this code relies on the fact
1338 * that elp->el_flags is never used as an rvalue, and is only
1339 * ever bit-tested for valid lookup information we are willing
1342 * XXX: We may have to do the same in the case that extended data was
1343 * passed out to user space to ensure that the request string
1344 * gets cached; we may also be able to use the rkip as an
1345 * input to avoid this. The jury is still out.
1347 * XXX: This codes performance could be improved for multiple valid
1348 * results by combining the loop iteration in a single loop.
1351 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1354 struct kauth_identity
*kip
;
1355 const char *speculative_name
= NULL
;
1360 * If there is extended data, and that data represents a name rather
1361 * than something else, speculatively create an entry for it in the
1362 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1363 * over the allocation later.
1365 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1366 const char *tmp
= CAST_DOWN(const char *, extend_data
);
1367 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1370 /* user identity? */
1371 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1372 KAUTH_IDENTITY_LOCK();
1373 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1374 /* matching record */
1375 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1376 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1377 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1378 if (elp
->el_sup_grp_cnt
> NGROUPS
) {
1379 KAUTH_DEBUG("CACHE - invalid sup_grp_cnt provided (%d), truncating to %d",
1380 elp
->el_sup_grp_cnt
, NGROUPS
);
1381 elp
->el_sup_grp_cnt
= NGROUPS
;
1383 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1384 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1385 kip
->ki_valid
|= KI_VALID_GROUPS
;
1386 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1388 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1389 kip
->ki_guid
= elp
->el_uguid
;
1390 kip
->ki_valid
|= KI_VALID_GUID
;
1392 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1393 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1394 kip
->ki_ntsid
= elp
->el_usid
;
1395 kip
->ki_valid
|= KI_VALID_NTSID
;
1397 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1398 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1399 const char *oname
= kip
->ki_name
;
1400 kip
->ki_name
= speculative_name
;
1401 speculative_name
= NULL
;
1402 kip
->ki_valid
|= KI_VALID_PWNAM
;
1405 * free oname (if any) outside
1408 speculative_name
= oname
;
1411 kauth_identity_lru(kip
);
1415 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1419 KAUTH_IDENTITY_UNLOCK();
1420 /* not found in cache, add new record */
1422 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1423 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1424 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1425 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1426 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1427 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1428 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1429 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1430 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1436 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1437 speculative_name
= NULL
;
1439 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1440 kauth_identity_register_and_free(kip
);
1445 /* group identity? (ignore, if we already processed it as a user) */
1446 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1447 KAUTH_IDENTITY_LOCK();
1448 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1449 /* matching record */
1450 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1451 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1452 kip
->ki_guid
= elp
->el_gguid
;
1453 kip
->ki_valid
|= KI_VALID_GUID
;
1455 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1456 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1457 kip
->ki_ntsid
= elp
->el_gsid
;
1458 kip
->ki_valid
|= KI_VALID_NTSID
;
1460 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1461 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1462 const char *oname
= kip
->ki_name
;
1463 kip
->ki_name
= speculative_name
;
1464 speculative_name
= NULL
;
1465 kip
->ki_valid
|= KI_VALID_GRNAM
;
1468 * free oname (if any) outside
1471 speculative_name
= oname
;
1474 kauth_identity_lru(kip
);
1478 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1482 KAUTH_IDENTITY_UNLOCK();
1483 /* not found in cache, add new record */
1485 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1486 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1487 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1488 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1489 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1490 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1491 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1492 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1493 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1499 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1500 speculative_name
= NULL
;
1502 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1503 kauth_identity_register_and_free(kip
);
1508 /* If we have a name reference to drop, drop it here */
1509 if (speculative_name
!= NULL
) {
1510 vfs_removename(speculative_name
);
1516 * Trim older entries from the identity cache.
1518 * Must be called with the identity cache lock held.
1521 kauth_identity_trimcache(int newsize
)
1523 struct kauth_identity
*kip
;
1525 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1527 while (kauth_identity_count
> newsize
) {
1528 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1529 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1530 kauth_identity_count
--;
1536 * kauth_identity_lru
1538 * Description: Promote the entry to the head of the LRU, assumes the cache
1541 * Parameters: kip kauth identity to move to the
1542 * head of the LRU list, if it's
1547 * Notes: This is called even if the entry has expired; typically an
1548 * expired entry that's been looked up is about to be revalidated,
1549 * and having it closer to the head of the LRU means finding it
1550 * quickly again when the revalidation comes through.
1553 kauth_identity_lru(struct kauth_identity
*kip
)
1555 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1556 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1557 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1563 * kauth_identity_guid_expired
1565 * Description: Handle lazy expiration of GUID translations.
1567 * Parameters: kip kauth identity to check for
1570 * Returns: 1 Expired
1574 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1579 * Expiration time of 0 means this entry is persistent.
1581 if (kip
->ki_guid_expiry
== 0) {
1586 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1588 return (kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1593 * kauth_identity_ntsid_expired
1595 * Description: Handle lazy expiration of NTSID translations.
1597 * Parameters: kip kauth identity to check for
1600 * Returns: 1 Expired
1604 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1609 * Expiration time of 0 means this entry is persistent.
1611 if (kip
->ki_ntsid_expiry
== 0) {
1616 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1618 return (kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0;
1622 * kauth_identity_groups_expired
1624 * Description: Handle lazy expiration of supplemental group translations.
1626 * Parameters: kip kauth identity to check for
1629 * Returns: 1 Expired
1633 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1638 * Expiration time of 0 means this entry is persistent.
1640 if (kip
->ki_groups_expiry
== 0) {
1645 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1647 return (kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0;
1651 * kauth_identity_find_uid
1653 * Description: Search for an entry by UID
1655 * Parameters: uid UID to find
1656 * kir Pointer to return area
1657 * getname Name buffer, if ki_name wanted
1663 * *klr Modified, if found
1666 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1668 struct kauth_identity
*kip
;
1670 KAUTH_IDENTITY_LOCK();
1671 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1672 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1673 kauth_identity_lru(kip
);
1674 /* Copy via structure assignment */
1676 /* If a name is wanted and one exists, copy it out */
1677 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1678 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1683 KAUTH_IDENTITY_UNLOCK();
1684 return (kip
== NULL
) ? ENOENT
: 0;
1689 * kauth_identity_find_gid
1691 * Description: Search for an entry by GID
1693 * Parameters: gid GID to find
1694 * kir Pointer to return area
1695 * getname Name buffer, if ki_name wanted
1701 * *klr Modified, if found
1704 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1706 struct kauth_identity
*kip
;
1708 KAUTH_IDENTITY_LOCK();
1709 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1710 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1711 kauth_identity_lru(kip
);
1712 /* Copy via structure assignment */
1714 /* If a name is wanted and one exists, copy it out */
1715 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1716 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1721 KAUTH_IDENTITY_UNLOCK();
1722 return (kip
== NULL
) ? ENOENT
: 0;
1727 * kauth_identity_find_guid
1729 * Description: Search for an entry by GUID
1731 * Parameters: guidp Pointer to GUID to find
1732 * kir Pointer to return area
1733 * getname Name buffer, if ki_name wanted
1739 * *klr Modified, if found
1741 * Note: The association may be expired, in which case the caller
1742 * may elect to call out to userland to revalidate.
1745 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1747 struct kauth_identity
*kip
;
1749 KAUTH_IDENTITY_LOCK();
1750 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1751 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1752 kauth_identity_lru(kip
);
1753 /* Copy via structure assignment */
1755 /* If a name is wanted and one exists, copy it out */
1756 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1757 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1762 KAUTH_IDENTITY_UNLOCK();
1763 return (kip
== NULL
) ? ENOENT
: 0;
1767 * kauth_identity_find_nam
1769 * Description: Search for an entry by name
1771 * Parameters: name Pointer to name to find
1772 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1773 * kir Pointer to return area
1779 * *klr Modified, if found
1782 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1784 struct kauth_identity
*kip
;
1786 KAUTH_IDENTITY_LOCK();
1787 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1788 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1789 kauth_identity_lru(kip
);
1790 /* Copy via structure assignment */
1795 KAUTH_IDENTITY_UNLOCK();
1796 return (kip
== NULL
) ? ENOENT
: 0;
1801 * kauth_identity_find_ntsid
1803 * Description: Search for an entry by NTSID
1805 * Parameters: ntsid Pointer to NTSID to find
1806 * kir Pointer to return area
1807 * getname Name buffer, if ki_name wanted
1813 * *klr Modified, if found
1815 * Note: The association may be expired, in which case the caller
1816 * may elect to call out to userland to revalidate.
1819 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1821 struct kauth_identity
*kip
;
1823 KAUTH_IDENTITY_LOCK();
1824 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1825 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1826 kauth_identity_lru(kip
);
1827 /* Copy via structure assignment */
1829 /* If a name is wanted and one exists, copy it out */
1830 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))) {
1831 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1836 KAUTH_IDENTITY_UNLOCK();
1837 return (kip
== NULL
) ? ENOENT
: 0;
1839 #endif /* CONFIG_EXT_RESOLVER */
1845 guid_t kauth_null_guid
;
1851 * Description: Determine the equality of two GUIDs
1853 * Parameters: guid1 Pointer to first GUID
1854 * guid2 Pointer to second GUID
1856 * Returns: 0 If GUIDs are unequal
1857 * !0 If GUIDs are equal
1860 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1862 return bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0;
1867 * kauth_wellknown_guid
1869 * Description: Determine if a GUID is a well-known GUID
1871 * Parameters: guid Pointer to GUID to check
1873 * Returns: KAUTH_WKG_NOT Not a well known GUID
1874 * KAUTH_WKG_EVERYBODY "Everybody"
1875 * KAUTH_WKG_NOBODY "Nobody"
1876 * KAUTH_WKG_OWNER "Other"
1877 * KAUTH_WKG_GROUP "Group"
1880 kauth_wellknown_guid(guid_t
*guid
)
1882 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1885 * All WKGs begin with the same 12 bytes.
1887 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1889 * The final 4 bytes are our code (in network byte order).
1891 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1894 return KAUTH_WKG_EVERYBODY
;
1896 return KAUTH_WKG_NOBODY
;
1898 return KAUTH_WKG_OWNER
;
1900 return KAUTH_WKG_GROUP
;
1903 return KAUTH_WKG_NOT
;
1910 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1912 * Parameters: sid1 Pointer to first NTSID
1913 * sid2 Pointer to second NTSID
1915 * Returns: 0 If GUIDs are unequal
1916 * !0 If GUIDs are equal
1919 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1921 /* check sizes for equality, also sanity-check size while we're at it */
1922 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1923 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1924 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0) {
1934 * We support four tokens representing identity:
1935 * - Credential reference
1938 * - NT security identifier
1940 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1947 * kauth_cred_change_egid
1949 * Description: Set EGID by changing the first element of cr_groups for the
1950 * passed credential; if the new EGID exists in the list of
1951 * groups already, then rotate the old EGID into its position,
1952 * otherwise replace it
1954 * Parameters: cred Pointer to the credential to modify
1955 * new_egid The new EGID to set
1957 * Returns: 0 The egid did not displace a member of
1958 * the supplementary group list
1959 * 1 The egid being set displaced a member
1960 * of the supplementary groups list
1962 * Note: Utility function; internal use only because of locking.
1964 * This function operates on the credential passed; the caller
1965 * must operate either on a newly allocated credential (one for
1966 * which there is no hash cache reference and no externally
1967 * visible pointer reference), or a template credential.
1970 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1976 #endif /* radar_4600026 */
1977 gid_t old_egid
= kauth_cred_getgid(cred
);
1978 posix_cred_t pcred
= posix_cred_get(cred
);
1980 /* Ignoring the first entry, scan for a match for the new egid */
1981 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1983 * If we find a match, swap them so we don't lose overall
1986 if (pcred
->cr_groups
[i
] == new_egid
) {
1987 pcred
->cr_groups
[i
] = old_egid
;
1988 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1995 #error Fix radar 4600026 first!!!
1998 * This is correct for memberd behaviour, but incorrect for POSIX; to address
1999 * this, we would need to automatically opt-out any SUID/SGID binary, and force
2000 * it to use initgroups to opt back in. We take the approach of considering it
2001 * opt'ed out in any group of 16 displacement instead, since it's a much more
2002 * conservative approach (i.e. less likely to cause things to break).
2006 * If we displaced a member of the supplementary groups list of the
2007 * credential, and we have not opted out of memberd, then if memberd
2008 * says that the credential is a member of the group, then it has not
2009 * actually been displaced.
2011 * NB: This is typically a cold code path.
2013 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
2014 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
2017 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
2019 #endif /* radar_4600026 */
2021 /* set the new EGID into the old spot */
2022 pcred
->cr_groups
[0] = new_egid
;
2031 * Description: Fetch UID from credential
2033 * Parameters: cred Credential to examine
2035 * Returns: (uid_t) UID associated with credential
2038 kauth_cred_getuid(kauth_cred_t cred
)
2040 NULLCRED_CHECK(cred
);
2041 return posix_cred_get(cred
)->cr_uid
;
2046 * kauth_cred_getruid
2048 * Description: Fetch RUID from credential
2050 * Parameters: cred Credential to examine
2052 * Returns: (uid_t) RUID associated with credential
2055 kauth_cred_getruid(kauth_cred_t cred
)
2057 NULLCRED_CHECK(cred
);
2058 return posix_cred_get(cred
)->cr_ruid
;
2063 * kauth_cred_getsvuid
2065 * Description: Fetch SVUID from credential
2067 * Parameters: cred Credential to examine
2069 * Returns: (uid_t) SVUID associated with credential
2072 kauth_cred_getsvuid(kauth_cred_t cred
)
2074 NULLCRED_CHECK(cred
);
2075 return posix_cred_get(cred
)->cr_svuid
;
2082 * Description: Fetch GID from credential
2084 * Parameters: cred Credential to examine
2086 * Returns: (gid_t) GID associated with credential
2089 kauth_cred_getgid(kauth_cred_t cred
)
2091 NULLCRED_CHECK(cred
);
2092 return posix_cred_get(cred
)->cr_gid
;
2097 * kauth_cred_getrgid
2099 * Description: Fetch RGID from credential
2101 * Parameters: cred Credential to examine
2103 * Returns: (gid_t) RGID associated with credential
2106 kauth_cred_getrgid(kauth_cred_t cred
)
2108 NULLCRED_CHECK(cred
);
2109 return posix_cred_get(cred
)->cr_rgid
;
2114 * kauth_cred_getsvgid
2116 * Description: Fetch SVGID from credential
2118 * Parameters: cred Credential to examine
2120 * Returns: (gid_t) SVGID associated with credential
2123 kauth_cred_getsvgid(kauth_cred_t cred
)
2125 NULLCRED_CHECK(cred
);
2126 return posix_cred_get(cred
)->cr_svgid
;
2130 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2132 #if CONFIG_EXT_RESOLVER == 0
2134 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
2137 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2139 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2140 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2141 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2142 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2147 case KI_VALID_UID
: {
2148 id_t uid
= htonl(*(id_t
*)src
);
2150 if (to
== KI_VALID_GUID
) {
2152 memcpy(uu
, _user_compat_prefix
, sizeof(_user_compat_prefix
));
2153 memcpy(&uu
[COMPAT_PREFIX_LEN
], &uid
, sizeof(uid
));
2158 case KI_VALID_GID
: {
2159 id_t gid
= htonl(*(id_t
*)src
);
2161 if (to
== KI_VALID_GUID
) {
2163 memcpy(uu
, _group_compat_prefix
, sizeof(_group_compat_prefix
));
2164 memcpy(&uu
[COMPAT_PREFIX_LEN
], &gid
, sizeof(gid
));
2169 case KI_VALID_GUID
: {
2170 const uint8_t *uu
= src
;
2172 if (to
== KI_VALID_UID
) {
2173 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2175 memcpy(&uid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(uid
));
2176 *(id_t
*)dst
= ntohl(uid
);
2179 } else if (to
== KI_VALID_GID
) {
2180 if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2182 memcpy(&gid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(gid
));
2183 *(id_t
*)dst
= ntohl(gid
);
2190 /* NOT IMPLEMENTED */
2197 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2199 * Structure to hold supplemental groups. Used for impedance matching with
2200 * kauth_cred_cache_lookup below.
2208 * kauth_cred_uid2groups
2210 * Description: Fetch supplemental GROUPS from UID
2212 * Parameters: uid UID to examine
2213 * groups pointer to an array of gid_ts
2214 * gcount pointer to the number of groups wanted/returned
2216 * Returns: 0 Success
2217 * kauth_cred_cache_lookup:EINVAL
2220 * *groups Modified, if successful
2221 * *gcount Modified, if successful
2225 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, int *gcount
)
2229 struct supgroups supgroups
;
2230 supgroups
.count
= gcount
;
2231 supgroups
.groups
= groups
;
2233 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2240 * kauth_cred_guid2pwnam
2242 * Description: Fetch PWNAM from GUID
2244 * Parameters: guidp Pointer to GUID to examine
2245 * pwnam Pointer to user@domain buffer
2247 * Returns: 0 Success
2248 * kauth_cred_cache_lookup:EINVAL
2251 * *pwnam Modified, if successful
2253 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2256 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2258 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
);
2263 * kauth_cred_guid2grnam
2265 * Description: Fetch GRNAM from GUID
2267 * Parameters: guidp Pointer to GUID to examine
2268 * grnam Pointer to group@domain buffer
2270 * Returns: 0 Success
2271 * kauth_cred_cache_lookup:EINVAL
2274 * *grnam Modified, if successful
2276 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2279 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2281 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
);
2286 * kauth_cred_pwnam2guid
2288 * Description: Fetch PWNAM from GUID
2290 * Parameters: pwnam String containing user@domain
2291 * guidp Pointer to buffer for GUID
2293 * Returns: 0 Success
2294 * kauth_cred_cache_lookup:EINVAL
2297 * *guidp Modified, if successful
2299 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2300 * bytes in size, including the NUL termination of the string.
2303 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2305 return kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
);
2310 * kauth_cred_grnam2guid
2312 * Description: Fetch GRNAM from GUID
2314 * Parameters: grnam String containing group@domain
2315 * guidp Pointer to buffer for GUID
2317 * Returns: 0 Success
2318 * kauth_cred_cache_lookup:EINVAL
2321 * *guidp Modified, if successful
2323 * Notes: grnam should not point to a request larger than MAXPATHLEN
2324 * bytes in size, including the NUL termination of the string.
2327 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2329 return kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
);
2334 * kauth_cred_guid2uid
2336 * Description: Fetch UID from GUID
2338 * Parameters: guidp Pointer to GUID to examine
2339 * uidp Pointer to buffer for UID
2341 * Returns: 0 Success
2342 * kauth_cred_cache_lookup:EINVAL
2345 * *uidp Modified, if successful
2348 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2350 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
);
2355 * kauth_cred_guid2gid
2357 * Description: Fetch GID from GUID
2359 * Parameters: guidp Pointer to GUID to examine
2360 * gidp Pointer to buffer for GID
2362 * Returns: 0 Success
2363 * kauth_cred_cache_lookup:EINVAL
2366 * *gidp Modified, if successful
2369 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2371 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
);
2375 * kauth_cred_nfs4domain2dsnode
2377 * Description: Fetch dsnode from nfs4domain
2379 * Parameters: nfs4domain Pointer to a string nfs4 domain
2380 * dsnode Pointer to buffer for dsnode
2382 * Returns: 0 Success
2383 * ENOENT For now just a stub that always fails
2386 * *dsnode Modified, if successuful
2389 kauth_cred_nfs4domain2dsnode(__unused
char *nfs4domain
, __unused
char *dsnode
)
2395 * kauth_cred_dsnode2nfs4domain
2397 * Description: Fetch nfs4domain from dsnode
2399 * Parameters: nfs4domain Pointer to string dsnode
2400 * dsnode Pointer to buffer for nfs4domain
2402 * Returns: 0 Success
2403 * ENOENT For now just a stub that always fails
2406 * *nfs4domain Modified, if successuful
2409 kauth_cred_dsnode2nfs4domain(__unused
char *dsnode
, __unused
char *nfs4domain
)
2415 * kauth_cred_ntsid2uid
2417 * Description: Fetch UID from NTSID
2419 * Parameters: sidp Pointer to NTSID to examine
2420 * uidp Pointer to buffer for UID
2422 * Returns: 0 Success
2423 * kauth_cred_cache_lookup:EINVAL
2426 * *uidp Modified, if successful
2429 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2431 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
);
2436 * kauth_cred_ntsid2gid
2438 * Description: Fetch GID from NTSID
2440 * Parameters: sidp Pointer to NTSID to examine
2441 * gidp Pointer to buffer for GID
2443 * Returns: 0 Success
2444 * kauth_cred_cache_lookup:EINVAL
2447 * *gidp Modified, if successful
2450 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2452 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
);
2457 * kauth_cred_ntsid2guid
2459 * Description: Fetch GUID from NTSID
2461 * Parameters: sidp Pointer to NTSID to examine
2462 * guidp Pointer to buffer for GUID
2464 * Returns: 0 Success
2465 * kauth_cred_cache_lookup:EINVAL
2468 * *guidp Modified, if successful
2471 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2473 return kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
);
2478 * kauth_cred_uid2guid
2480 * Description: Fetch GUID from UID
2482 * Parameters: uid UID to examine
2483 * guidp Pointer to buffer for GUID
2485 * Returns: 0 Success
2486 * kauth_cred_cache_lookup:EINVAL
2489 * *guidp Modified, if successful
2492 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2494 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
);
2499 * kauth_cred_getguid
2501 * Description: Fetch GUID from credential
2503 * Parameters: cred Credential to examine
2504 * guidp Pointer to buffer for GUID
2506 * Returns: 0 Success
2507 * kauth_cred_cache_lookup:EINVAL
2510 * *guidp Modified, if successful
2513 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2515 NULLCRED_CHECK(cred
);
2516 return kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
);
2521 * kauth_cred_getguid
2523 * Description: Fetch GUID from GID
2525 * Parameters: gid GID 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_gid2guid(gid_t gid
, guid_t
*guidp
)
2537 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
);
2542 * kauth_cred_uid2ntsid
2544 * Description: Fetch NTSID from UID
2546 * Parameters: uid UID to examine
2547 * sidp Pointer to buffer for NTSID
2549 * Returns: 0 Success
2550 * kauth_cred_cache_lookup:EINVAL
2553 * *sidp Modified, if successful
2556 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2558 return kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
);
2563 * kauth_cred_getntsid
2565 * Description: Fetch NTSID from credential
2567 * Parameters: cred Credential to examine
2568 * sidp Pointer to buffer for NTSID
2570 * Returns: 0 Success
2571 * kauth_cred_cache_lookup:EINVAL
2574 * *sidp Modified, if successful
2577 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2579 NULLCRED_CHECK(cred
);
2580 return kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
);
2585 * kauth_cred_gid2ntsid
2587 * Description: Fetch NTSID from GID
2589 * Parameters: gid GID 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_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2601 return kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
);
2606 * kauth_cred_guid2ntsid
2608 * Description: Fetch NTSID from GUID
2610 * Parameters: guidp Pointer to GUID to examine
2611 * sidp Pointer to buffer for NTSID
2613 * Returns: 0 Success
2614 * kauth_cred_cache_lookup:EINVAL
2617 * *sidp Modified, if successful
2620 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2622 return kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
);
2627 * kauth_cred_cache_lookup
2629 * Description: Lookup a translation in the cache; if one is not found, and
2630 * the attempt was not fatal, submit the request to the resolver
2631 * instead, and wait for it to complete or be aborted.
2633 * Parameters: from Identity information we have
2634 * to Identity information we want
2635 * src Pointer to buffer containing
2636 * the source identity
2637 * dst Pointer to buffer to receive
2638 * the target identity
2640 * Returns: 0 Success
2641 * EINVAL Unknown source identity type
2643 #if CONFIG_EXT_RESOLVER
2645 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2647 struct kauth_identity ki
;
2648 struct kauth_identity_extlookup el
;
2650 uint64_t extend_data
= 0ULL;
2651 int (* expired
)(struct kauth_identity
*kip
);
2652 char *namebuf
= NULL
;
2654 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2657 * Look for an existing cache entry for this association.
2658 * If the entry has not expired, return the cached information.
2659 * We do not cache user@domain translations here; they use too
2660 * much memory to hold onto forever, and can not be updated
2663 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2673 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2676 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2679 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2681 case KI_VALID_NTSID
:
2682 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2684 case KI_VALID_PWNAM
:
2685 case KI_VALID_GRNAM
:
2686 /* Names are unique in their 'from' space */
2687 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2692 /* If we didn't get what we're asking for. Call the resolver */
2693 if (!error
&& !(to
& ki
.ki_valid
)) {
2696 /* lookup failure or error */
2698 /* any other error is fatal */
2699 if (error
!= ENOENT
) {
2700 /* XXX bogus check - this is not possible */
2701 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2705 /* found a valid cached entry, check expiry */
2708 expired
= kauth_identity_guid_expired
;
2710 case KI_VALID_NTSID
:
2711 expired
= kauth_identity_ntsid_expired
;
2713 case KI_VALID_GROUPS
:
2714 expired
= kauth_identity_groups_expired
;
2719 expired
= kauth_identity_guid_expired
;
2721 case KI_VALID_NTSID
:
2722 expired
= kauth_identity_ntsid_expired
;
2730 * If no expiry function, or not expired, we have found
2734 if (!expired(&ki
)) {
2735 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2736 expired
= NULL
; /* must clear it is used as a flag */
2739 * We leave ki_valid set here; it contains a
2740 * translation but the TTL has expired. If we can't
2741 * get a result from the resolver, we will use it as
2742 * a better-than nothing alternative.
2745 KAUTH_DEBUG("CACHE - expired entry found");
2748 KAUTH_DEBUG("CACHE - no expiry function");
2752 /* do we have a translation? */
2753 if (ki
.ki_valid
& to
) {
2754 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2755 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2759 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2760 * If we went looking for a translation from GUID or NTSID and
2761 * found a translation that wasn't for our desired type, then
2762 * don't bother calling the resolver. We know that this
2763 * GUID/NTSID can't translate to our desired type.
2767 case KI_VALID_NTSID
:
2770 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2771 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2776 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2777 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2789 * We failed to find a cache entry; call the resolver.
2791 * Note: We ask for as much non-extended data as we can get,
2792 * and only provide (or ask for) extended information if
2793 * we have a 'from' (or 'to') which requires it. This
2794 * way we don't pay for the extra transfer overhead for
2795 * data we don't need.
2797 bzero(&el
, sizeof(el
));
2798 el
.el_info_pid
= current_proc()->p_pid
;
2801 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2802 el
.el_uid
= *(uid_t
*)src
;
2805 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2806 el
.el_gid
= *(gid_t
*)src
;
2809 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2810 el
.el_uguid
= *(guid_t
*)src
;
2811 el
.el_gguid
= *(guid_t
*)src
;
2813 case KI_VALID_NTSID
:
2814 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2815 el
.el_usid
= *(ntsid_t
*)src
;
2816 el
.el_gsid
= *(ntsid_t
*)src
;
2818 case KI_VALID_PWNAM
:
2819 /* extra overhead */
2820 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2821 extend_data
= CAST_USER_ADDR_T(src
);
2823 case KI_VALID_GRNAM
:
2824 /* extra overhead */
2825 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2826 extend_data
= CAST_USER_ADDR_T(src
);
2832 * Here we ask for everything all at once, to avoid having to work
2833 * out what we really want now, or might want soon.
2835 * Asking for SID translations when we don't know we need them right
2836 * now is going to cause excess work to be done if we're connected
2837 * to a network that thinks it can translate them. This list needs
2838 * to get smaller/smarter.
2840 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2841 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2842 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2843 if (to
== KI_VALID_PWNAM
) {
2844 /* extra overhead */
2845 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2846 extend_data
= CAST_USER_ADDR_T(dst
);
2848 if (to
== KI_VALID_GRNAM
) {
2849 /* extra overhead */
2850 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2851 extend_data
= CAST_USER_ADDR_T(dst
);
2853 if (to
== KI_VALID_GROUPS
) {
2854 /* Expensive and only useful for an NFS client not using kerberos */
2855 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2856 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2858 * Copy the current supplemental groups for the resolver.
2859 * The resolver should check these groups first and if
2860 * the user (uid) is still a member it should endeavor to
2861 * keep them in the list. Otherwise NFS clients could get
2862 * changing access to server file system objects on each
2865 if (ki
.ki_supgrpcnt
> NGROUPS
) {
2866 panic("kauth data structure corrupted. kauth identity 0x%p with %d groups, greater than max of %d",
2867 &ki
, ki
.ki_supgrpcnt
, NGROUPS
);
2870 el
.el_sup_grp_cnt
= ki
.ki_supgrpcnt
;
2872 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof(el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2873 /* Let the resolver know these were the previous valid groups */
2874 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2875 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2877 KAUTH_DEBUG("GROUPS: no valid groups to send");
2882 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2884 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2886 error
= kauth_resolver_submit(&el
, extend_data
);
2888 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2890 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2892 /* was the external lookup successful? */
2895 * Save the results from the lookup - we may have other
2896 * information, even if we didn't get a guid or the
2899 * If we came from a name, we know the extend_data is valid.
2901 if (from
== KI_VALID_PWNAM
) {
2902 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2903 } else if (from
== KI_VALID_GRNAM
) {
2904 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2907 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2910 * Check to see if we have a valid cache entry
2911 * originating from the result.
2913 if (!(ki
.ki_valid
& to
)) {
2922 * Copy from the appropriate struct kauth_identity cache entry
2923 * structure into the destination buffer area.
2927 *(uid_t
*)dst
= ki
.ki_uid
;
2930 *(gid_t
*)dst
= ki
.ki_gid
;
2933 *(guid_t
*)dst
= ki
.ki_guid
;
2935 case KI_VALID_NTSID
:
2936 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2938 case KI_VALID_GROUPS
: {
2939 struct supgroups
*gp
= (struct supgroups
*)dst
;
2940 u_int32_t limit
= ki
.ki_supgrpcnt
;
2943 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2947 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2950 case KI_VALID_PWNAM
:
2951 case KI_VALID_GRNAM
:
2952 /* handled in kauth_resolver_complete() */
2957 KAUTH_DEBUG("CACHE - returned successfully");
2963 * Group membership cache.
2965 * XXX the linked-list implementation here needs to be optimized.
2971 * Description: Initialize the groups cache
2973 * Parameters: (void)
2977 * Notes: Initialize the groups cache for use; the group cache is used
2978 * to avoid unnecessary calls out to user space.
2980 * This function is called from kauth_init() in the file
2981 * kern_authorization.c.
2984 kauth_groups_init(void)
2986 TAILQ_INIT(&kauth_groups
);
2987 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
2992 * kauth_groups_expired
2994 * Description: Handle lazy expiration of group membership cache entries
2996 * Parameters: gm group membership entry to
2997 * check for expiration
2999 * Returns: 1 Expired
3003 kauth_groups_expired(struct kauth_group_membership
*gm
)
3008 * Expiration time of 0 means this entry is persistent.
3010 if (gm
->gm_expiry
== 0) {
3016 return (gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0;
3023 * Description: Promote the entry to the head of the LRU, assumes the cache
3026 * Parameters: kip group membership entry to move
3027 * to the head of the LRU list,
3028 * if it's not already there
3032 * Notes: This is called even if the entry has expired; typically an
3033 * expired entry that's been looked up is about to be revalidated,
3034 * and having it closer to the head of the LRU means finding it
3035 * quickly again when the revalidation comes through.
3038 kauth_groups_lru(struct kauth_group_membership
*gm
)
3040 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
3041 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3042 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3048 * kauth_groups_updatecache
3050 * Description: Given a lookup result, add any group cache associations that
3051 * we don't currently have.
3053 * Parameters: elp External lookup result from
3054 * user space daemon to kernel
3055 * rkip pointer to returned kauth
3061 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
3063 struct kauth_group_membership
*gm
;
3066 /* need a valid response if we are to cache anything */
3068 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
3069 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) {
3076 * Search for an existing record for this association before inserting
3077 * a new one; if we find one, update it instead of creating a new one
3079 KAUTH_GROUPS_LOCK();
3080 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3081 if ((el
->el_uid
== gm
->gm_uid
) &&
3082 (el
->el_gid
== gm
->gm_gid
)) {
3083 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3084 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3086 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3088 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3089 kauth_groups_lru(gm
);
3093 KAUTH_GROUPS_UNLOCK();
3095 /* if we found an entry to update, stop here */
3100 /* allocate a new record */
3101 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
3103 gm
->gm_uid
= el
->el_uid
;
3104 gm
->gm_gid
= el
->el_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;
3114 * Insert the new entry. Note that it's possible to race ourselves
3115 * here and end up with duplicate entries in the list. Wasteful, but
3116 * harmless since the first into the list will never be looked up,
3117 * and thus will eventually just fall off the end.
3119 KAUTH_GROUPS_LOCK();
3120 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3121 if (++kauth_groups_count
> kauth_groups_cachemax
) {
3122 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3123 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3124 kauth_groups_count
--;
3128 KAUTH_GROUPS_UNLOCK();
3130 /* free expired cache entry */
3137 * Trim older entries from the group membership cache.
3139 * Must be called with the group cache lock held.
3142 kauth_groups_trimcache(int new_size
)
3144 struct kauth_group_membership
*gm
;
3146 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
3148 while (kauth_groups_count
> new_size
) {
3149 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3150 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3151 kauth_groups_count
--;
3155 #endif /* CONFIG_EXT_RESOLVER */
3158 * Group membership KPI
3162 * kauth_cred_ismember_gid
3164 * Description: Given a credential and a GID, determine if the GID is a member
3165 * of one of the supplementary groups associated with the given
3168 * Parameters: cred Credential to check in
3169 * gid GID to check for membership
3170 * resultp Pointer to int to contain the
3171 * result of the call
3173 * Returns: 0 Success
3174 * ENOENT Could not perform lookup
3175 * kauth_resolver_submit:EWOULDBLOCK
3176 * kauth_resolver_submit:EINTR
3177 * kauth_resolver_submit:ENOMEM
3178 * kauth_resolver_submit:ENOENT User space daemon did not vend
3180 * kauth_resolver_submit:??? Unlikely error from user space
3183 * *resultp (modified) 1 Is member
3186 * Notes: This function guarantees not to modify resultp when returning
3189 * This function effectively checks the EGID as well, since the
3190 * EGID is cr_groups[0] as an implementation detail.
3193 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3195 posix_cred_t pcred
= posix_cred_get(cred
);
3199 * Check the per-credential list of override groups.
3201 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3202 * the cache should be used for that case.
3204 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3205 if (gid
== pcred
->cr_groups
[i
]) {
3212 * If we don't have a UID for group membership checks, the in-cred list
3213 * was authoritative and we can stop here.
3215 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3220 #if CONFIG_EXT_RESOLVER
3221 struct kauth_group_membership
*gm
;
3222 struct kauth_identity_extlookup el
;
3226 * If the resolver hasn't checked in yet, we are early in the boot
3227 * phase and the local group list is complete and authoritative.
3229 if (!kauth_resolver_registered
) {
3235 /* XXX check supplementary groups */
3236 /* XXX check whiteout groups */
3237 /* XXX nesting of supplementary/whiteout groups? */
3240 * Check the group cache.
3242 KAUTH_GROUPS_LOCK();
3243 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3244 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3245 kauth_groups_lru(gm
);
3250 /* did we find a membership entry? */
3252 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3254 KAUTH_GROUPS_UNLOCK();
3256 /* if we did, we can return now */
3258 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3262 /* nothing in the cache, need to go to userland */
3263 bzero(&el
, sizeof(el
));
3264 el
.el_info_pid
= current_proc()->p_pid
;
3265 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3266 el
.el_uid
= pcred
->cr_gmuid
;
3268 el
.el_member_valid
= 0; /* XXX set by resolver? */
3270 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3272 error
= kauth_resolver_submit(&el
, 0ULL);
3274 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3279 /* save the results from the lookup */
3280 kauth_groups_updatecache(&el
);
3282 /* if we successfully ascertained membership, report */
3283 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3284 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3296 * kauth_cred_ismember_guid
3298 * Description: Determine whether the supplied credential is a member of the
3299 * group nominated by GUID.
3301 * Parameters: cred Credential to check in
3302 * guidp Pointer to GUID whose group
3303 * we are testing for membership
3304 * resultp Pointer to int to contain the
3305 * result of the call
3307 * Returns: 0 Success
3308 * kauth_cred_guid2gid:EINVAL
3309 * kauth_cred_ismember_gid:ENOENT
3310 * kauth_resolver_submit:ENOENT User space daemon did not vend
3312 * kauth_cred_ismember_gid:EWOULDBLOCK
3313 * kauth_cred_ismember_gid:EINTR
3314 * kauth_cred_ismember_gid:ENOMEM
3315 * kauth_cred_ismember_gid:??? Unlikely error from user space
3318 * *resultp (modified) 1 Is member
3322 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3326 switch (kauth_wellknown_guid(guidp
)) {
3327 case KAUTH_WKG_NOBODY
:
3330 case KAUTH_WKG_EVERYBODY
:
3336 #if CONFIG_EXT_RESOLVER
3337 struct kauth_identity ki
;
3340 * Grovel the identity cache looking for this GUID.
3341 * If we find it, and it is for a user record, return
3342 * false because it's not a group.
3344 * This is necessary because we don't have -ve caching
3345 * of group memberships, and we really want to avoid
3346 * calling out to the resolver if at all possible.
3348 * Because we're called by the ACL evaluator, and the
3349 * ACL evaluator is likely to encounter ACEs for users,
3350 * this is expected to be a common case.
3353 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3354 !kauth_identity_guid_expired(&ki
)) {
3355 if (ki
.ki_valid
& KI_VALID_GID
) {
3356 /* It's a group after all... */
3360 if (ki
.ki_valid
& KI_VALID_UID
) {
3365 #endif /* CONFIG_EXT_RESOLVER */
3367 * Attempt to translate the GUID to a GID. Even if
3368 * this fails, we will have primed the cache if it is
3369 * a user record and we'll see it above the next time
3372 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3374 * If we have no guid -> gid translation, it's not a group and
3375 * thus the cred can't be a member.
3377 if (error
== ENOENT
) {
3382 #if CONFIG_EXT_RESOLVER
3384 #endif /* CONFIG_EXT_RESOLVER */
3385 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3394 * kauth_cred_gid_subset
3396 * Description: Given two credentials, determine if all GIDs associated with
3397 * the first are also associated with the second
3399 * Parameters: cred1 Credential to check for
3400 * cred2 Credential to check in
3401 * resultp Pointer to int to contain the
3402 * result of the call
3404 * Returns: 0 Success
3405 * non-zero See kauth_cred_ismember_gid for
3409 * *resultp (modified) 1 Is subset
3412 * Notes: This function guarantees not to modify resultp when returning
3416 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3418 int i
, err
, res
= 1;
3420 posix_cred_t pcred1
= posix_cred_get(cred1
);
3421 posix_cred_t pcred2
= posix_cred_get(cred2
);
3423 /* First, check the local list of groups */
3424 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3425 gid
= pcred1
->cr_groups
[i
];
3426 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3430 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3436 /* Check real gid */
3437 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3441 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3442 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3447 /* Finally, check saved gid */
3448 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0) {
3452 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3453 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3464 * kauth_cred_issuser
3466 * Description: Fast replacement for issuser()
3468 * Parameters: cred Credential to check for super
3471 * Returns: 0 Not super user
3474 * Notes: This function uses a magic number which is not a manifest
3475 * constant; this is bad practice.
3478 kauth_cred_issuser(kauth_cred_t cred
)
3480 return kauth_cred_getuid(cred
) == 0;
3488 /* lock protecting credential hash table */
3489 static lck_mtx_t kauth_cred_hash_mtx
;
3490 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(&kauth_cred_hash_mtx);
3491 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(&kauth_cred_hash_mtx);
3492 #define KAUTH_CRED_HASH_LOCK_ASSERT() LCK_MTX_ASSERT(&kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3498 * Description: Initialize the credential hash cache
3500 * Parameters: (void)
3504 * Notes: Intialize the credential hash cache for use; the credential
3505 * hash cache is used convert duplicate credentials into a
3506 * single reference counted credential in order to save wired
3507 * kernel memory. In practice, this generally means a desktop
3508 * system runs with a few tens of credentials, instead of one
3509 * per process, one per thread, one per vnode cache entry, and
3510 * so on. This generally results in savings of 200K or more
3511 * (potentially much more on server systems).
3513 * The hash cache internally has a reference on the credential
3514 * for itself as a means of avoiding a reclaim race for a
3515 * credential in the process of having it's last non-hash
3516 * reference released. This would otherwise result in the
3517 * possibility of a freed credential that was still in uses due
3518 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3520 * On final release, the hash reference is droped, and the
3521 * credential is freed back to the system.
3523 * This function is called from kauth_init() in the file
3524 * kern_authorization.c.
3527 kauth_cred_init(void)
3529 lck_mtx_init(&kauth_cred_hash_mtx
, kauth_lck_grp
, 0 /*LCK_ATTR_NULL*/);
3531 for (int i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
3532 LIST_INIT(&kauth_cred_table_anchor
[i
]);
3540 * Description: Get the current thread's effective UID.
3542 * Parameters: (void)
3544 * Returns: (uid_t) The effective UID of the
3550 return kauth_cred_getuid(kauth_cred_get());
3557 * Description: Get the current thread's real UID.
3559 * Parameters: (void)
3561 * Returns: (uid_t) The real UID of the current
3567 return kauth_cred_getruid(kauth_cred_get());
3574 * Description: Get the current thread's effective GID.
3576 * Parameters: (void)
3578 * Returns: (gid_t) The effective GID of the
3584 return kauth_cred_getgid(kauth_cred_get());
3591 * Description: Get the current thread's real GID.
3593 * Parameters: (void)
3595 * Returns: (gid_t) The real GID of the current
3601 return kauth_cred_getrgid(kauth_cred_get());
3608 * Description: Returns a pointer to the current thread's credential
3610 * Parameters: (void)
3612 * Returns: (kauth_cred_t) Pointer to the current thread's
3615 * Notes: This function does not take a reference; because of this, the
3616 * caller MUST NOT do anything that would let the thread's
3617 * credential change while using the returned value, without
3618 * first explicitly taking their own reference.
3620 * If a caller intends to take a reference on the resulting
3621 * credential pointer from calling this function, it is strongly
3622 * recommended that the caller use kauth_cred_get_with_ref()
3623 * instead, to protect against any future changes to the cred
3624 * locking protocols; such changes could otherwise potentially
3625 * introduce race windows in the callers code.
3628 kauth_cred_get(void)
3631 struct uthread
*uthread
;
3633 uthread
= get_bsdthread_info(current_thread());
3635 if (uthread
== NULL
) {
3636 panic("thread wants credential but has no BSD thread info");
3639 * We can lazy-bind credentials to threads, as long as their processes
3642 * XXX If we later inline this function, the code in this block
3643 * XXX should probably be called out in a function.
3645 if (uthread
->uu_ucred
== NOCRED
) {
3646 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
) {
3647 panic("thread wants credential but has no BSD process");
3649 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3651 return uthread
->uu_ucred
;
3655 mach_kauth_cred_uthread_update(void)
3660 uthread
= get_bsdthread_info(current_thread());
3661 proc
= current_proc();
3663 kauth_cred_uthread_update(uthread
, proc
);
3667 * kauth_cred_uthread_update
3669 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3670 * late-bind the uthread cred to the proc cred.
3672 * Parameters: uthread_t The uthread to update
3673 * proc_t The process to update to
3677 * Notes: This code is common code called from system call or trap entry
3678 * in the case that the process thread may have been changed
3679 * since the last time the thread entered the kernel. It is
3680 * generally only called with the current uthread and process as
3684 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3686 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3687 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3688 kauth_cred_t old
= uthread
->uu_ucred
;
3689 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3690 if (IS_VALID_CRED(old
)) {
3691 kauth_cred_unref(&old
);
3698 * kauth_cred_get_with_ref
3700 * Description: Takes a reference on the current thread's credential, and then
3701 * returns a pointer to it to the caller.
3703 * Parameters: (void)
3705 * Returns: (kauth_cred_t) Pointer to the current thread's
3706 * newly referenced credential
3708 * Notes: This function takes a reference on the credential before
3709 * returning it to the caller.
3711 * It is the responsibility of the calling code to release this
3712 * reference when the credential is no longer in use.
3714 * Since the returned reference may be a persistent reference
3715 * (e.g. one cached in another data structure with a lifetime
3716 * longer than the calling function), this release may be delayed
3717 * until such time as the persistent reference is to be destroyed.
3718 * An example of this would be the per vnode credential cache used
3719 * to accelerate lookup operations.
3722 kauth_cred_get_with_ref(void)
3724 struct uthread
*uthread
= current_uthread();
3727 * We can lazy-bind credentials to threads, as long as their processes
3730 * XXX If we later inline this function, the code in this block
3731 * XXX should probably be called out in a function.
3733 if (uthread
->uu_ucred
== NOCRED
) {
3734 /* take reference for new cred in thread */
3735 uthread
->uu_ucred
= kauth_cred_proc_ref(current_proc());
3737 /* take a reference for our caller */
3738 kauth_cred_ref(uthread
->uu_ucred
);
3739 return uthread
->uu_ucred
;
3744 * kauth_cred_proc_ref
3746 * Description: Takes a reference on the current process's credential, and
3747 * then returns a pointer to it to the caller.
3749 * Parameters: procp Process whose credential we
3750 * intend to take a reference on
3752 * Returns: (kauth_cred_t) Pointer to the process's
3753 * newly referenced credential
3755 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3756 * after the refeence is taken to protect the p_ucred field of
3757 * the process referred to by procp.
3759 * Notes: This function takes a reference on the credential before
3760 * returning it to the caller.
3762 * It is the responsibility of the calling code to release this
3763 * reference when the credential is no longer in use.
3765 * Since the returned reference may be a persistent reference
3766 * (e.g. one cached in another data structure with a lifetime
3767 * longer than the calling function), this release may be delayed
3768 * until such time as the persistent reference is to be destroyed.
3769 * An example of this would be the per vnode credential cache used
3770 * to accelerate lookup operations.
3773 kauth_cred_proc_ref(proc_t procp
)
3777 proc_ucred_lock(procp
);
3778 cred
= proc_ucred(procp
);
3779 kauth_cred_ref(cred
);
3780 proc_ucred_unlock(procp
);
3787 * Description: Allocate a new credential
3789 * Parameters: (void)
3791 * Returns: !NULL Newly allocated credential
3792 * NULL Insufficient memory
3794 * Notes: The newly allocated credential is zero'ed as part of the
3795 * allocation process, with the exception of the reference
3796 * count, which is set to 0 to indicate the caller still has
3797 * to call kauth_cred_add().
3799 * Since newly allocated credentials have no external pointers
3800 * referencing them, prior to making them visible in an externally
3801 * visible pointer (e.g. by adding them to the credential hash
3802 * cache) is the only legal time in which an existing credential
3803 * can be safely iinitialized or modified directly.
3805 * After initialization, the caller is expected to call the
3806 * function kauth_cred_add() to add the credential to the hash
3807 * cache, after which time it's frozen and becomes publically
3810 * The release protocol depends on kauth_hash_add() being called
3811 * before kauth_cred_rele() (there is a diagnostic panic which
3812 * will trigger if this protocol is not observed).
3814 * XXX: This function really ought to be static, rather than being
3815 * exported as KPI, since a failure of kauth_cred_add() can only
3816 * be handled by an explicit free of the credential; such frees
3817 * depend on knowlegdge of the allocation method used, which is
3818 * permitted to change between kernel revisions.
3820 * XXX: In the insufficient resource case, this code panic's rather
3821 * than returning a NULL pointer; the code that calls this
3822 * function needs to be audited before this can be changed.
3825 kauth_cred_alloc(void)
3827 kauth_cred_t newcred
;
3829 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
| M_ZERO
);
3832 posix_cred_t newpcred
= posix_cred_get(newcred
);
3833 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3834 /* must do this, or cred has same group membership as uid 0 */
3835 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3837 mac_cred_label_init(newcred
);
3847 * Description: Destroy a credential
3849 * Parameters: cred Credential to destroy.
3852 kauth_cred_free(kauth_cred_t cred
)
3854 assert(os_atomic_load(&cred
->cr_ref
, relaxed
) == 0);
3856 mac_cred_label_destroy(cred
);
3858 AUDIT_SESSION_UNREF(cred
);
3859 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
3865 * Description: Look to see if we already have a known credential in the hash
3866 * cache; if one is found, bump the reference count and return
3867 * it. If there are no credentials that match the given
3868 * credential, then allocate a new credential.
3870 * Parameters: cred Template for credential to
3873 * Returns: (kauth_cred_t) The credential that was found
3874 * in the hash or created
3875 * NULL kauth_cred_add() failed, or
3876 * there was not an egid specified
3878 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3879 * maintain this field, we can't expect callers to know how it
3880 * needs to be set. Callers should be prepared for this field
3881 * to be overwritten.
3884 kauth_cred_create(kauth_cred_t cred
)
3886 kauth_cred_t found_cred
, new_cred
= NULL
;
3887 posix_cred_t pcred
= posix_cred_get(cred
);
3890 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3891 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3894 * If the template credential is not opting out of external
3895 * group membership resolution, then we need to check that
3896 * the UID we will be using is resolvable by the external
3897 * resolver. If it's not, then we opt it out anyway, since
3898 * all future external resolution requests will be failing
3899 * anyway, and potentially taking a long time to do it. We
3900 * use gid 0 because we always know it will exist and not
3901 * trigger additional lookups. This is OK, because we end up
3902 * precatching the information here as a result.
3904 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3906 * It's a recognized value; we don't really care about
3907 * the answer, so long as it's something the external
3908 * resolver could have vended.
3910 pcred
->cr_gmuid
= pcred
->cr_uid
;
3913 * It's not something the external resolver could
3914 * have vended, so we don't want to ask it more
3915 * questions about the credential in the future. This
3916 * speeds up future lookups, as long as the caller
3917 * caches results; otherwise, it the same recurring
3918 * cost. Since most credentials are used multiple
3919 * times, we still get some performance win from this.
3921 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3922 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3926 /* Caller *must* specify at least the egid in cr_groups[0] */
3927 if (pcred
->cr_ngroups
< 1) {
3931 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
3933 KAUTH_CRED_HASH_LOCK();
3934 found_cred
= kauth_cred_find_and_ref(cred
, bucket
);
3935 KAUTH_CRED_HASH_UNLOCK();
3936 if (found_cred
!= NULL
) {
3941 * No existing credential found. Create one and add it to
3944 new_cred
= kauth_cred_alloc();
3945 if (new_cred
!= NULL
) {
3946 *posix_cred_get(new_cred
) = *pcred
;
3948 new_cred
->cr_audit
= cred
->cr_audit
;
3950 new_cred
= kauth_cred_add(new_cred
, bucket
);
3958 * kauth_cred_setresuid
3960 * Description: Update the given credential using the UID arguments. The given
3961 * UIDs are used to set the effective UID, real UID, saved UID,
3962 * and GMUID (used for group membership checking).
3964 * Parameters: cred The original credential
3965 * ruid The new real UID
3966 * euid The new effective UID
3967 * svuid The new saved UID
3968 * gmuid KAUTH_UID_NONE -or- the new
3969 * group membership UID
3971 * Returns: (kauth_cred_t) The updated credential
3973 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3974 * setting, so if you don't want it to change, pass it the
3975 * previous value, explicitly.
3977 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3978 * if it returns a credential other than the one it is passed,
3979 * will have dropped the reference on the passed credential. All
3980 * callers should be aware of this, and treat this function as an
3981 * unref + ref, potentially on different credentials.
3983 * Because of this, the caller is expected to take its own
3984 * reference on the credential passed as the first parameter,
3985 * and be prepared to release the reference on the credential
3986 * that is returned to them, if it is not intended to be a
3987 * persistent reference.
3990 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3992 struct ucred temp_cred
;
3993 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3994 posix_cred_t pcred
= posix_cred_get(cred
);
3996 NULLCRED_CHECK(cred
);
3999 * We don't need to do anything if the UIDs we are changing are
4000 * already the same as the UIDs passed in
4002 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
4003 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
4004 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
4005 (pcred
->cr_gmuid
== gmuid
)) {
4006 /* no change needed */
4011 * Look up in cred hash table to see if we have a matching credential
4012 * with the new values; this is done by calling kauth_cred_update().
4014 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4015 if (euid
!= KAUTH_UID_NONE
) {
4016 temp_pcred
->cr_uid
= euid
;
4018 if (ruid
!= KAUTH_UID_NONE
) {
4019 temp_pcred
->cr_ruid
= ruid
;
4021 if (svuid
!= KAUTH_UID_NONE
) {
4022 temp_pcred
->cr_svuid
= svuid
;
4026 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
4027 * opt out of participation in external group resolution, unless we
4028 * unless we explicitly opt back in later.
4030 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
4031 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4034 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4039 * kauth_cred_setresgid
4041 * Description: Update the given credential using the GID arguments. The given
4042 * GIDs are used to set the effective GID, real GID, and saved
4045 * Parameters: cred The original credential
4046 * rgid The new real GID
4047 * egid The new effective GID
4048 * svgid The new saved GID
4050 * Returns: (kauth_cred_t) The updated credential
4052 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4053 * if it returns a credential other than the one it is passed,
4054 * will have dropped the reference on the passed credential. All
4055 * callers should be aware of this, and treat this function as an
4056 * unref + ref, potentially on different credentials.
4058 * Because of this, the caller is expected to take its own
4059 * reference on the credential passed as the first parameter,
4060 * and be prepared to release the reference on the credential
4061 * that is returned to them, if it is not intended to be a
4062 * persistent reference.
4065 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
4067 struct ucred temp_cred
;
4068 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4069 posix_cred_t pcred
= posix_cred_get(cred
);
4071 NULLCRED_CHECK(cred
);
4072 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
4075 * We don't need to do anything if the given GID are already the
4076 * same as the GIDs in the credential.
4078 if (pcred
->cr_groups
[0] == egid
&&
4079 pcred
->cr_rgid
== rgid
&&
4080 pcred
->cr_svgid
== svgid
) {
4081 /* no change needed */
4086 * Look up in cred hash table to see if we have a matching credential
4087 * with the new values; this is done by calling kauth_cred_update().
4089 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4090 if (egid
!= KAUTH_GID_NONE
) {
4091 /* displacing a supplementary group opts us out of memberd */
4092 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
4093 DEBUG_CRED_CHANGE("displaced!\n");
4094 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4095 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4097 DEBUG_CRED_CHANGE("not displaced\n");
4100 if (rgid
!= KAUTH_GID_NONE
) {
4101 temp_pcred
->cr_rgid
= rgid
;
4103 if (svgid
!= KAUTH_GID_NONE
) {
4104 temp_pcred
->cr_svgid
= svgid
;
4107 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4112 * Update the given credential with the given groups. We only allocate a new
4113 * credential when the given gid actually results in changes to the existing
4115 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4116 * which will be used for group membership checking.
4119 * kauth_cred_setgroups
4121 * Description: Update the given credential using the provide supplementary
4122 * group list and group membership UID
4124 * Parameters: cred The original credential
4125 * groups Pointer to gid_t array which
4126 * contains the new group list
4127 * groupcount The count of valid groups which
4128 * are contained in 'groups'
4129 * gmuid KAUTH_UID_NONE -or- the new
4130 * group membership UID
4132 * Returns: (kauth_cred_t) The updated credential
4134 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4135 * setting, so if you don't want it to change, pass it the
4136 * previous value, explicitly.
4138 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4139 * if it returns a credential other than the one it is passed,
4140 * will have dropped the reference on the passed credential. All
4141 * callers should be aware of this, and treat this function as an
4142 * unref + ref, potentially on different credentials.
4144 * Because of this, the caller is expected to take its own
4145 * reference on the credential passed as the first parameter,
4146 * and be prepared to release the reference on the credential
4147 * that is returned to them, if it is not intended to be a
4148 * persistent reference.
4150 * XXX: Changes are determined in ordinal order - if the caller passes
4151 * in the same groups list that is already present in the
4152 * credential, but the members are in a different order, even if
4153 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4154 * is considered a modification to the credential, and a new
4155 * credential is created.
4157 * This should perhaps be better optimized, but it is considered
4158 * to be the caller's problem.
4161 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
4164 struct ucred temp_cred
;
4165 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4168 NULLCRED_CHECK(cred
);
4170 pcred
= posix_cred_get(cred
);
4173 * We don't need to do anything if the given list of groups does not
4176 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4177 for (i
= 0; i
< groupcount
; i
++) {
4178 if (pcred
->cr_groups
[i
] != groups
[i
]) {
4182 if (i
== groupcount
) {
4183 /* no change needed */
4189 * Look up in cred hash table to see if we have a matching credential
4190 * with new values. If we are setting or clearing the gmuid, then
4191 * update the cr_flags, since clearing it is sticky. This permits an
4192 * opt-out of memberd processing using setgroups(), and an opt-in
4193 * using initgroups(). This is required for POSIX conformance.
4195 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4196 temp_pcred
->cr_ngroups
= groupcount
;
4197 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
4198 temp_pcred
->cr_gmuid
= gmuid
;
4199 if (gmuid
== KAUTH_UID_NONE
) {
4200 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4202 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4205 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4209 * Notes: The return value exists to account for the possibility of a
4210 * kauth_cred_t without a POSIX label. This will be the case in
4211 * the future (see posix_cred_get() below, for more details).
4213 #if CONFIG_EXT_RESOLVER
4214 int kauth_external_supplementary_groups_supported
= 1;
4216 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4220 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
4222 int limit
= NGROUPS
;
4225 pcred
= posix_cred_get(cred
);
4227 #if CONFIG_EXT_RESOLVER
4229 * If we've not opted out of using the resolver, then convert the cred to a list
4230 * of supplemental groups. We do this only if there has been a resolver to talk to,
4231 * since we may be too early in boot, or in an environment that isn't using DS.
4233 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4234 uid_t uid
= kauth_cred_getuid(cred
);
4237 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4242 /* On error just fall through */
4243 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4245 #endif /* CONFIG_EXT_RESOLVER */
4248 * If they just want a copy of the groups list, they may not care
4249 * about the actual count. If they specify an input count, however,
4250 * treat it as an indicator of the buffer size available in grouplist,
4251 * and limit the returned list to that size.
4254 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4258 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4265 * kauth_cred_setuidgid
4267 * Description: Update the given credential using the UID and GID arguments.
4268 * The given UID is used to set the effective UID, real UID, and
4269 * saved UID. The given GID is used to set the effective GID,
4270 * real GID, and saved GID.
4272 * Parameters: cred The original credential
4273 * uid The new UID to use
4274 * gid The new GID to use
4276 * Returns: (kauth_cred_t) The updated credential
4278 * Notes: We set the gmuid to uid if the credential we are inheriting
4279 * from has not opted out of memberd participation; otherwise
4280 * we set it to KAUTH_UID_NONE
4282 * This code is only ever called from the per-thread credential
4283 * code path in the "set per thread credential" case; and in
4284 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4287 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4288 * if it returns a credential other than the one it is passed,
4289 * will have dropped the reference on the passed credential. All
4290 * callers should be aware of this, and treat this function as an
4291 * unref + ref, potentially on different credentials.
4293 * Because of this, the caller is expected to take its own
4294 * reference on the credential passed as the first parameter,
4295 * and be prepared to release the reference on the credential
4296 * that is returned to them, if it is not intended to be a
4297 * persistent reference.
4300 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4302 struct ucred temp_cred
;
4303 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4306 NULLCRED_CHECK(cred
);
4308 pcred
= posix_cred_get(cred
);
4311 * We don't need to do anything if the effective, real and saved
4312 * user IDs are already the same as the user ID passed into us.
4314 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4315 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4316 /* no change needed */
4321 * Look up in cred hash table to see if we have a matching credential
4322 * with the new values.
4324 bzero(&temp_cred
, sizeof(temp_cred
));
4325 temp_pcred
->cr_uid
= uid
;
4326 temp_pcred
->cr_ruid
= uid
;
4327 temp_pcred
->cr_svuid
= uid
;
4328 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4329 /* inherit the opt-out of memberd */
4330 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4331 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4332 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4334 temp_pcred
->cr_gmuid
= uid
;
4335 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4337 temp_pcred
->cr_ngroups
= 1;
4338 /* displacing a supplementary group opts us out of memberd */
4339 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4340 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4341 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4343 temp_pcred
->cr_rgid
= gid
;
4344 temp_pcred
->cr_svgid
= gid
;
4346 temp_cred
.cr_label
= cred
->cr_label
;
4349 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4354 * kauth_cred_setsvuidgid
4356 * Description: Function used by execve to set the saved uid and gid values
4357 * for suid/sgid programs
4359 * Parameters: cred The credential to update
4360 * uid The saved uid to set
4361 * gid The saved gid to set
4363 * Returns: (kauth_cred_t) The updated credential
4365 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4366 * if it returns a credential other than the one it is passed,
4367 * will have dropped the reference on the passed credential. All
4368 * callers should be aware of this, and treat this function as an
4369 * unref + ref, potentially on different credentials.
4371 * Because of this, the caller is expected to take its own
4372 * reference on the credential passed as the first parameter,
4373 * and be prepared to release the reference on the credential
4374 * that is returned to them, if it is not intended to be a
4375 * persistent reference.
4378 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4380 struct ucred temp_cred
;
4381 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4384 NULLCRED_CHECK(cred
);
4386 pcred
= posix_cred_get(cred
);
4388 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4391 * We don't need to do anything if the effective, real and saved
4392 * uids are already the same as the uid provided. This check is
4393 * likely insufficient.
4395 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4396 /* no change needed */
4399 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4401 /* look up in cred hash table to see if we have a matching credential
4404 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4405 temp_pcred
->cr_svuid
= uid
;
4406 temp_pcred
->cr_svgid
= gid
;
4408 return kauth_cred_update(cred
, &temp_cred
, TRUE
);
4413 * kauth_cred_setauditinfo
4415 * Description: Update the given credential using the given au_session_t.
4417 * Parameters: cred The original credential
4418 * auditinfo_p Pointer to ne audit information
4420 * Returns: (kauth_cred_t) The updated credential
4422 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4423 * if it returns a credential other than the one it is passed,
4424 * will have dropped the reference on the passed credential. All
4425 * callers should be aware of this, and treat this function as an
4426 * unref + ref, potentially on different credentials.
4428 * Because of this, the caller is expected to take its own
4429 * reference on the credential passed as the first parameter,
4430 * and be prepared to release the reference on the credential
4431 * that is returned to them, if it is not intended to be a
4432 * persistent reference.
4435 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4437 struct ucred temp_cred
;
4439 NULLCRED_CHECK(cred
);
4442 * We don't need to do anything if the audit info is already the
4443 * same as the audit info in the credential provided.
4445 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4446 /* no change needed */
4450 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4451 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4453 return kauth_cred_update(cred
, &temp_cred
, FALSE
);
4458 * kauth_cred_label_update
4460 * Description: Update the MAC label associated with a credential
4462 * Parameters: cred The original credential
4463 * label The MAC label to set
4465 * Returns: (kauth_cred_t) The updated credential
4467 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4468 * if it returns a credential other than the one it is passed,
4469 * will have dropped the reference on the passed credential. All
4470 * callers should be aware of this, and treat this function as an
4471 * unref + ref, potentially on different credentials.
4473 * Because of this, the caller is expected to take its own
4474 * reference on the credential passed as the first parameter,
4475 * and be prepared to release the reference on the credential
4476 * that is returned to them, if it is not intended to be a
4477 * persistent reference.
4480 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4482 kauth_cred_t newcred
;
4483 struct ucred temp_cred
;
4485 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4487 mac_cred_label_init(&temp_cred
);
4488 mac_cred_label_associate(cred
, &temp_cred
);
4489 mac_cred_label_update(&temp_cred
, label
);
4491 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4492 mac_cred_label_destroy(&temp_cred
);
4497 * kauth_cred_label_update_execve
4499 * Description: Update the MAC label associated with a credential as
4502 * Parameters: cred The original credential
4504 * scriptl The script MAC label
4505 * execl The executable MAC label
4506 * disjointp Pointer to flag to set if old
4507 * and returned credentials are
4510 * Returns: (kauth_cred_t) The updated credential
4513 * *disjointp Set to 1 for disjoint creds
4515 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4516 * if it returns a credential other than the one it is passed,
4517 * will have dropped the reference on the passed credential. All
4518 * callers should be aware of this, and treat this function as an
4519 * unref + ref, potentially on different credentials.
4521 * Because of this, the caller is expected to take its own
4522 * reference on the credential passed as the first parameter,
4523 * and be prepared to release the reference on the credential
4524 * that is returned to them, if it is not intended to be a
4525 * persistent reference.
4530 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4531 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4532 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4534 kauth_cred_t newcred
;
4535 struct ucred temp_cred
;
4537 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4539 mac_cred_label_init(&temp_cred
);
4540 mac_cred_label_associate(cred
, &temp_cred
);
4541 mac_cred_label_update_execve(ctx
, &temp_cred
,
4542 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4543 macextensions
, disjointp
, labelupdateerror
);
4545 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4546 mac_cred_label_destroy(&temp_cred
);
4551 * kauth_proc_label_update
4553 * Description: Update the label inside the credential associated with the process.
4555 * Parameters: p The process to modify
4556 * label The label to place in the process credential
4558 * Notes: The credential associated with the process may change as a result
4559 * of this call. The caller should not assume the process reference to
4560 * the old credential still exists.
4563 kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4565 kauth_cred_t my_cred
, my_new_cred
;
4567 my_cred
= kauth_cred_proc_ref(p
);
4569 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4571 /* get current credential and take a reference while we muck with it */
4574 * Set the credential with new info. If there is no change,
4575 * we get back the same credential we passed in; if there is
4576 * a change, we drop the reference on the credential we
4577 * passed in. The subsequent compare is safe, because it is
4578 * a pointer compare rather than a contents compare.
4580 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4581 if (my_cred
!= my_new_cred
) {
4582 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
);
4586 * We need to protect for a race where another thread
4587 * also changed the credential after we took our
4588 * reference. If p_ucred has changed then we should
4589 * restart this again with the new cred.
4591 if (p
->p_ucred
!= my_cred
) {
4592 proc_ucred_unlock(p
);
4593 kauth_cred_unref(&my_new_cred
);
4594 my_cred
= kauth_cred_proc_ref(p
);
4598 p
->p_ucred
= my_new_cred
;
4599 /* update cred on proc */
4600 PROC_UPDATE_CREDS_ONPROC(p
);
4602 proc_ucred_unlock(p
);
4606 /* Drop old proc reference or our extra reference */
4607 kauth_cred_unref(&my_cred
);
4613 * kauth_proc_label_update_execve
4615 * Description: Update the label inside the credential associated with the
4616 * process as part of a transitioning execve. The label will
4617 * be updated by the policies as part of this processing, not
4618 * provided up front.
4620 * Parameters: p The process to modify
4621 * ctx The context of the exec
4622 * vp The vnode being exec'ed
4623 * scriptl The script MAC label
4624 * execl The executable MAC label
4625 * lupdateerror The error place holder for MAC label authority
4626 * to update about possible termination
4628 * Returns: 0 Label update did not make credential
4630 * 1 Label update caused credential to be
4633 * Notes: The credential associated with the process WILL change as a
4634 * result of this call. The caller should not assume the process
4635 * reference to the old credential still exists.
4639 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4640 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4641 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4643 kauth_cred_t my_cred
, my_new_cred
;
4644 my_cred
= kauth_cred_proc_ref(p
);
4646 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4648 /* get current credential and take a reference while we muck with it */
4651 * Set the credential with new info. If there is no change,
4652 * we get back the same credential we passed in; if there is
4653 * a change, we drop the reference on the credential we
4654 * passed in. The subsequent compare is safe, because it is
4655 * a pointer compare rather than a contents compare.
4657 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4658 if (my_cred
!= my_new_cred
) {
4659 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
);
4663 * We need to protect for a race where another thread
4664 * also changed the credential after we took our
4665 * reference. If p_ucred has changed then we should
4666 * restart this again with the new cred.
4668 if (p
->p_ucred
!= my_cred
) {
4669 proc_ucred_unlock(p
);
4670 kauth_cred_unref(&my_new_cred
);
4671 my_cred
= kauth_cred_proc_ref(p
);
4675 p
->p_ucred
= my_new_cred
;
4676 /* update cred on proc */
4677 PROC_UPDATE_CREDS_ONPROC(p
);
4678 proc_ucred_unlock(p
);
4682 /* Drop old proc reference or our extra reference */
4683 kauth_cred_unref(&my_cred
);
4688 * for temporary binary compatibility
4690 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4692 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4694 return kauth_cred_label_update(cred
, label
);
4697 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4699 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4701 return kauth_proc_label_update(p
, label
);
4707 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4708 * Since we cannot build our export lists based on the kernel configuration we need
4712 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4718 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4725 * for temporary binary compatibility
4727 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4729 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4734 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4736 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4743 // TODO: move to os_refcnt once the ABI issue is resolved
4745 #define KAUTH_CRED_REF_MAX 0x0ffffffful
4747 __attribute__((noinline
, cold
, noreturn
))
4749 kauth_cred_panic_resurrection(kauth_cred_t cred
)
4751 panic("kauth_cred_unref: cred %p resurrected", cred
);
4752 __builtin_unreachable();
4755 __attribute__((noinline
, cold
, noreturn
))
4757 kauth_cred_panic_over_released(kauth_cred_t cred
)
4759 panic("kauth_cred_unref: cred %p over-released", cred
);
4760 __builtin_unreachable();
4763 __attribute__((noinline
, cold
, noreturn
))
4765 kauth_cred_panic_over_retain(kauth_cred_t cred
)
4767 panic("kauth_cred_ref: cred %p over-retained", cred
);
4768 __builtin_unreachable();
4774 * Description: Tries to take a reference, used from kauth_cred_find_and_ref
4775 * to debounce the race with kauth_cred_unref.
4777 * Parameters: cred The credential to reference
4779 * Returns: (bool) Whether the reference was taken
4782 kauth_cred_tryref(kauth_cred_t cred
)
4784 u_long old_ref
, new_ref
;
4785 os_atomic_rmw_loop(&cred
->cr_ref
, old_ref
, new_ref
, relaxed
, {
4787 os_atomic_rmw_loop_give_up(return false);
4789 new_ref
= old_ref
+ 1;
4791 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4792 kauth_cred_panic_over_retain(cred
);
4795 #if 0 // use this to watch a specific credential
4796 if (is_target_cred( *credp
) != 0) {
4807 * Description: Add a reference to the passed credential
4809 * Parameters: cred The credential to reference
4814 kauth_cred_ref(kauth_cred_t cred
)
4816 u_long old_ref
= os_atomic_inc_orig(&cred
->cr_ref
, relaxed
);
4818 if (__improbable(old_ref
< 1)) {
4819 kauth_cred_panic_resurrection(cred
);
4821 if (__improbable(old_ref
>= KAUTH_CRED_REF_MAX
)) {
4822 kauth_cred_panic_over_retain(cred
);
4825 #if 0 // use this to watch a specific credential
4826 if (is_target_cred( cred
) != 0) {
4833 * kauth_cred_unref_fast
4835 * Description: Release a credential reference.
4837 * Parameters: credp Pointer to address containing
4838 * credential to be freed
4840 * Returns: true This was the last reference.
4841 * false The object has more refs.
4845 kauth_cred_unref_fast(kauth_cred_t cred
)
4847 u_long old_ref
= os_atomic_dec_orig(&cred
->cr_ref
, relaxed
);
4849 #if 0 // use this to watch a specific credential
4850 if (is_target_cred( *credp
) != 0) {
4855 if (__improbable(old_ref
<= 0)) {
4856 kauth_cred_panic_over_released(cred
);
4858 return old_ref
== 1;
4864 * Description: Release a credential reference.
4865 * Frees the credential if it is the last ref.
4867 * Parameters: credp Pointer to address containing
4868 * credential to be freed
4873 * *credp Set to NOCRED
4877 kauth_cred_unref(kauth_cred_t
*credp
)
4879 if (kauth_cred_unref_fast(*credp
)) {
4880 KAUTH_CRED_HASH_LOCK();
4881 kauth_cred_remove_locked(*credp
);
4882 KAUTH_CRED_HASH_UNLOCK();
4883 kauth_cred_free(*credp
);
4894 * Description: release a credential reference; when the last reference is
4895 * released, the credential will be freed
4897 * Parameters: cred Credential to release
4901 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4902 * clear the pointer in the caller to avoid multiple releases of
4903 * the same credential. The currently recommended interface is
4904 * kauth_cred_unref().
4907 kauth_cred_rele(kauth_cred_t cred
)
4909 kauth_cred_unref(&cred
);
4911 #endif /* !__LP64__ */
4917 * Description: Duplicate a credential via alloc and copy; the new credential
4920 * Parameters: cred The credential to duplicate
4922 * Returns: (kauth_cred_t) The duplicate credential
4924 * Notes: The typical value to calling this routine is if you are going
4925 * to modify an existing credential, and expect to need a new one
4926 * from the hash cache.
4928 * This should probably not be used in the majority of cases;
4929 * if you are using it instead of kauth_cred_create(), you are
4930 * likely making a mistake.
4932 * The newly allocated credential is copied as part of the
4933 * allocation process, with the exception of the reference
4934 * count, which is set to 0 to indicate the caller still has
4935 * to call kauth_cred_add().
4937 * Since newly allocated credentials have no external pointers
4938 * referencing them, prior to making them visible in an externally
4939 * visible pointer (e.g. by adding them to the credential hash
4940 * cache) is the only legal time in which an existing credential
4941 * can be safely initialized or modified directly.
4943 * After initialization, the caller is expected to call the
4944 * function kauth_cred_add() to add the credential to the hash
4945 * cache, after which time it's frozen and becomes publicly
4948 * The release protocol depends on kauth_hash_add() being called
4949 * before kauth_cred_rele() (there is a diagnostic panic which
4950 * will trigger if this protocol is not observed).
4954 kauth_cred_dup(kauth_cred_t cred
)
4956 kauth_cred_t newcred
;
4958 assert(cred
!= NOCRED
&& cred
!= FSCRED
);
4959 newcred
= kauth_cred_alloc();
4960 if (newcred
!= NULL
) {
4961 newcred
->cr_posix
= cred
->cr_posix
;
4963 newcred
->cr_audit
= cred
->cr_audit
;
4966 mac_cred_label_associate(cred
, newcred
);
4968 AUDIT_SESSION_REF(cred
);
4974 * kauth_cred_copy_real
4976 * Description: Returns a credential based on the passed credential but which
4977 * reflects the real rather than effective UID and GID.
4979 * Parameters: cred The credential from which to
4980 * derive the new credential
4982 * Returns: (kauth_cred_t) The copied credential
4984 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4985 * result, the caller is responsible for dropping BOTH the
4986 * additional reference on the passed cred (if any), and the
4987 * credential returned by this function. The drop should be
4988 * via the kauth_cred_unref() KPI.
4991 kauth_cred_copy_real(kauth_cred_t cred
)
4993 kauth_cred_t newcred
= NULL
, found_cred
;
4994 struct ucred temp_cred
;
4995 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4996 posix_cred_t pcred
= posix_cred_get(cred
);
4998 /* if the credential is already 'real', just take a reference */
4999 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
5000 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
5001 kauth_cred_ref(cred
);
5006 * Look up in cred hash table to see if we have a matching credential
5007 * with the new values.
5009 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
5010 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
5011 /* displacing a supplementary group opts us out of memberd */
5012 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
5013 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
5014 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
5017 * If the cred is not opted out, make sure we are using the r/euid
5020 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
) {
5021 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
5024 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
5026 KAUTH_CRED_HASH_LOCK();
5027 found_cred
= kauth_cred_find_and_ref(&temp_cred
, bucket
);
5028 KAUTH_CRED_HASH_UNLOCK();
5035 * Must allocate a new credential, copy in old credential
5036 * data and update the real user and group IDs.
5038 newcred
= kauth_cred_dup(&temp_cred
);
5039 return kauth_cred_add(newcred
, bucket
);
5046 * Description: Common code to update a credential
5048 * Parameters: old_cred Reference counted credential
5050 * model_cred Non-reference counted model
5051 * credential to apply to the
5052 * credential to be updated
5053 * retain_auditinfo Flag as to whether or not the
5054 * audit information should be
5055 * copied from the old_cred into
5058 * Returns: (kauth_cred_t) The updated credential
5060 * IMPORTANT: This function will potentially return a credential other than
5061 * the one it is passed, and if so, it will have dropped the
5062 * reference on the passed credential. All callers should be
5063 * aware of this, and treat this function as an unref + ref,
5064 * potentially on different credentials.
5066 * Because of this, the caller is expected to take its own
5067 * reference on the credential passed as the first parameter,
5068 * and be prepared to release the reference on the credential
5069 * that is returned to them, if it is not intended to be a
5070 * persistent reference.
5073 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
5074 boolean_t retain_auditinfo
)
5079 * Make sure we carry the auditinfo forward to the new credential
5080 * unless we are actually updating the auditinfo.
5082 if (retain_auditinfo
) {
5083 model_cred
->cr_audit
= old_cred
->cr_audit
;
5086 if (kauth_cred_is_equal(old_cred
, model_cred
)) {
5090 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(model_cred
);
5092 KAUTH_CRED_HASH_LOCK();
5093 cred
= kauth_cred_find_and_ref(model_cred
, bucket
);
5096 * We found a hit, so we can get rid of the old_cred.
5097 * If we didn't, then we need to keep the old_cred around,
5098 * because `model_cred` has copies of things such as the cr_label
5099 * or audit session that it has not refcounts for.
5101 bool needs_free
= kauth_cred_unref_fast(old_cred
);
5103 kauth_cred_remove_locked(old_cred
);
5105 KAUTH_CRED_HASH_UNLOCK();
5107 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n",
5110 kauth_cred_free(old_cred
);
5115 KAUTH_CRED_HASH_UNLOCK();
5118 * Must allocate a new credential using the model. also
5119 * adds the new credential to the credential hash table.
5121 cred
= kauth_cred_dup(model_cred
);
5122 cred
= kauth_cred_add(cred
, bucket
);
5123 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n",
5128 * This can't be done before the kauth_cred_dup() as the model_cred
5129 * has pointers that old_cred owns references for.
5131 kauth_cred_unref(&old_cred
);
5139 * Description: Add the given credential to our credential hash table and
5140 * take an initial reference to account for the object being
5143 * Parameters: new_cred Credential to insert into cred
5144 * hash cache, or to destroy when
5145 * a collision is detected.
5147 * Returns: (kauth_thread_t) The inserted cred, or the
5148 * collision that was found.
5150 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5153 kauth_cred_add(kauth_cred_t new_cred
, struct kauth_cred_entry_head
*bucket
)
5155 kauth_cred_t found_cred
;
5158 KAUTH_CRED_HASH_LOCK();
5159 found_cred
= kauth_cred_find_and_ref(new_cred
, bucket
);
5161 KAUTH_CRED_HASH_UNLOCK();
5162 kauth_cred_free(new_cred
);
5166 old_ref
= os_atomic_xchg(&new_cred
->cr_ref
, 1, relaxed
);
5168 panic("kauth_cred_add: invalid cred %p", new_cred
);
5171 /* insert the credential into the hash table */
5172 LIST_INSERT_HEAD(bucket
, new_cred
, cr_link
);
5174 KAUTH_CRED_HASH_UNLOCK();
5179 * kauth_cred_remove_locked
5181 * Description: Remove the given credential from our credential hash table.
5183 * Parameters: cred Credential to remove.
5185 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5188 kauth_cred_remove_locked(kauth_cred_t cred
)
5190 KAUTH_CRED_HASH_LOCK_ASSERT();
5192 if (cred
->cr_link
.le_prev
== NULL
) {
5193 panic("kauth_cred_unref: cred %p never added", cred
);
5196 LIST_REMOVE(cred
, cr_link
);
5200 * kauth_cred_is_equal
5202 * Description: Returns whether two credentions are identical.
5204 * Parameters: cred1 Credential to compare
5205 * cred2 Credential to compare
5207 * Returns: true Credentials are equal
5208 * false Credentials are different
5211 kauth_cred_is_equal(kauth_cred_t cred1
, kauth_cred_t cred2
)
5213 posix_cred_t pcred1
= posix_cred_get(cred1
);
5214 posix_cred_t pcred2
= posix_cred_get(cred2
);
5217 * don't worry about the label unless the flags in
5218 * either credential tell us to.
5220 if (memcmp(pcred1
, pcred2
, sizeof(*pcred1
))) {
5223 if (memcmp(&cred1
->cr_audit
, &cred2
->cr_audit
, sizeof(cred1
->cr_audit
))) {
5227 /* Note: we know the flags are equal, so we only need to test one */
5228 if (pcred1
->cr_flags
& CRF_MAC_ENFORCE
) {
5229 if (!mac_cred_label_compare(cred1
->cr_label
, cred2
->cr_label
)) {
5238 * kauth_cred_find_and_ref
5240 * Description: Using the given credential data, look for a match in our
5241 * credential hash table
5243 * Parameters: cred Credential to lookup in cred
5246 * Returns: NULL Not found
5247 * !NULL Matching credential already in
5248 * cred hash cache, with a +1 ref
5250 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5253 kauth_cred_find_and_ref(kauth_cred_t cred
, struct kauth_cred_entry_head
*bucket
)
5255 kauth_cred_t found_cred
;
5257 KAUTH_CRED_HASH_LOCK_ASSERT();
5259 /* Find cred in the credential hash table */
5260 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5261 if (kauth_cred_is_equal(found_cred
, cred
)) {
5263 * newer entries are inserted at the head,
5264 * no hit further in the chain can possibly
5265 * be successfully retained.
5267 if (!kauth_cred_tryref(found_cred
)) {
5280 * Description: This interface is sadly KPI but people can't possibly use it,
5281 * as they need to hold a lock that isn't exposed.
5283 * Parameters: cred Credential to lookup in cred
5286 * Returns: NULL Not found
5287 * !NULL Matching credential already in
5290 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5293 kauth_cred_find(kauth_cred_t cred
)
5295 struct kauth_cred_entry_head
*bucket
= kauth_cred_get_bucket(cred
);
5296 kauth_cred_t found_cred
;
5298 KAUTH_CRED_HASH_LOCK_ASSERT();
5300 /* Find cred in the credential hash table */
5301 LIST_FOREACH(found_cred
, bucket
, cr_link
) {
5302 if (kauth_cred_is_equal(found_cred
, cred
)) {
5314 * Description: Generates a hash key using data that makes up a credential;
5317 * Parameters: datap Pointer to data to hash
5318 * data_len Count of bytes to hash
5319 * start_key Start key value
5321 * Returns: (u_long) Returned hash key
5323 static inline u_long
5324 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5326 u_long hash_key
= start_key
;
5329 while (data_len
> 0) {
5330 hash_key
= (hash_key
<< 4) + *datap
++;
5331 temp
= hash_key
& 0xF0000000;
5333 hash_key
^= temp
>> 24;
5343 * kauth_cred_get_bucket
5345 * Description: Generate a hash key using data that makes up a credential;
5346 * based on ElfHash. We hash on the entire credential data,
5347 * not including the ref count or the TAILQ, which are mutable;
5348 * everything else isn't.
5350 * Returns the bucket correspondong to this hash key.
5352 * Parameters: cred Credential for which hash is
5355 * Returns: (kauth_cred_entry_head *) Returned bucket.
5357 * Notes: When actually moving the POSIX credential into a real label,
5358 * remember to update this hash computation.
5360 static struct kauth_cred_entry_head
*
5361 kauth_cred_get_bucket(kauth_cred_t cred
)
5364 posix_cred_t pcred
= posix_cred_get(cred
);
5366 u_long hash_key
= 0;
5368 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5369 sizeof(struct posix_cred
),
5371 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5372 sizeof(struct au_session
),
5375 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5376 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5377 sizeof(struct label
),
5382 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5383 return &kauth_cred_table_anchor
[hash_key
];
5391 * Description: Print out an individual credential's contents for debugging
5394 * Parameters: cred The credential to print out
5398 * Implicit returns: Results in console output
5401 kauth_cred_print(kauth_cred_t cred
)
5405 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
);
5406 printf("group count %d gids ", cred
->cr_ngroups
);
5407 for (i
= 0; i
< NGROUPS
; i
++) {
5411 printf("%d ", cred
->cr_groups
[i
]);
5413 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5414 printf("auditinfo_addr %d %d %d %d %d %d\n",
5415 cred
->cr_audit
.s_aia_p
->ai_auid
,
5416 cred
->cr_audit
.as_mask
.am_success
,
5417 cred
->cr_audit
.as_mask
.am_failure
,
5418 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5419 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5420 cred
->cr_audit
.as_aia_p
->ai_asid
);
5424 is_target_cred( kauth_cred_t the_cred
)
5426 if (the_cred
->cr_uid
!= 0) {
5429 if (the_cred
->cr_ruid
!= 0) {
5432 if (the_cred
->cr_svuid
!= 0) {
5435 if (the_cred
->cr_ngroups
!= 11) {
5438 if (the_cred
->cr_groups
[0] != 11) {
5441 if (the_cred
->cr_groups
[1] != 81) {
5444 if (the_cred
->cr_groups
[2] != 63947) {
5447 if (the_cred
->cr_groups
[3] != 80288) {
5450 if (the_cred
->cr_groups
[4] != 89006) {
5453 if (the_cred
->cr_groups
[5] != 52173) {
5456 if (the_cred
->cr_groups
[6] != 84524) {
5459 if (the_cred
->cr_groups
[7] != 79) {
5462 if (the_cred
->cr_groups
[8] != 80292) {
5465 if (the_cred
->cr_groups
[9] != 80) {
5468 if (the_cred
->cr_groups
[10] != 90824) {
5471 if (the_cred
->cr_rgid
!= 11) {
5474 if (the_cred
->cr_svgid
!= 11) {
5477 if (the_cred
->cr_gmuid
!= 3475) {
5480 if (the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475) {
5484 * if ( the_cred->cr_audit.as_mask.am_success != 0 )
5486 * if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5488 * if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5490 * if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5492 * if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5494 * if ( the_cred->cr_flags != 0 )
5497 return -1; // found target cred
5501 get_backtrace( void )
5504 void * my_stack
[MAX_STACK_DEPTH
];
5507 if (cred_debug_buf_p
== NULL
) {
5508 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5509 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5512 if (cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1)) {
5513 /* buffer is full */
5517 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5518 if (my_depth
== 0) {
5519 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5523 /* fill new backtrace */
5524 my_slot
= cred_debug_buf_p
->next_slot
;
5525 cred_debug_buf_p
->next_slot
++;
5526 cred_debug_buf_p
->stack_buffer
[my_slot
].depth
= my_depth
;
5527 for (i
= 0; i
< my_depth
; i
++) {
5528 cred_debug_buf_p
->stack_buffer
[my_slot
].stack
[i
] = my_stack
[i
];
5535 /* subset of struct ucred for use in sysctl_dump_creds */
5536 struct debug_ucred
{
5538 u_long cr_ref
; /* reference count */
5539 uid_t cr_uid
; /* effective user id */
5540 uid_t cr_ruid
; /* real user id */
5541 uid_t cr_svuid
; /* saved user id */
5542 short cr_ngroups
; /* number of groups in advisory list */
5543 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5544 gid_t cr_rgid
; /* real group id */
5545 gid_t cr_svgid
; /* saved group id */
5546 uid_t cr_gmuid
; /* UID for group membership purposes */
5547 struct auditinfo_addr cr_audit
; /* user auditing data. */
5548 void *cr_label
; /* MACF label */
5549 int cr_flags
; /* flags on credential */
5551 typedef struct debug_ucred debug_ucred
;
5553 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5554 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5557 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5561 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5563 int i
, j
, counter
= 0;
5566 kauth_cred_t found_cred
;
5567 debug_ucred
* cred_listp
;
5568 debug_ucred
* nextp
;
5570 /* This is a readonly node. */
5571 if (req
->newptr
!= USER_ADDR_NULL
) {
5575 /* calculate space needed */
5576 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5577 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5582 /* they are querying us so just return the space required. */
5583 if (req
->oldptr
== USER_ADDR_NULL
) {
5584 counter
+= 10; // add in some padding;
5585 req
->oldidx
= counter
* sizeof(debug_ucred
);
5589 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5590 if (cred_listp
== NULL
) {
5594 /* fill in creds to send back */
5597 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5598 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5599 nextp
->credp
= found_cred
;
5600 nextp
->cr_ref
= found_cred
->cr_ref
;
5601 nextp
->cr_uid
= found_cred
->cr_uid
;
5602 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5603 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5604 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5605 for (j
= 0; j
< nextp
->cr_ngroups
; j
++) {
5606 nextp
->cr_groups
[j
] = found_cred
->cr_groups
[j
];
5608 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5609 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5610 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5611 nextp
->cr_audit
.ai_auid
=
5612 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5613 nextp
->cr_audit
.ai_mask
.am_success
=
5614 found_cred
->cr_audit
.as_mask
.am_success
;
5615 nextp
->cr_audit
.ai_mask
.am_failure
=
5616 found_cred
->cr_audit
.as_mask
.am_failure
;
5617 nextp
->cr_audit
.ai_termid
.at_port
=
5618 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5619 nextp
->cr_audit
.ai_termid
.at_type
=
5620 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5621 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5622 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5623 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5624 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5625 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5626 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5627 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5628 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5629 nextp
->cr_audit
.ai_asid
=
5630 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5631 nextp
->cr_audit
.ai_flags
=
5632 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5633 nextp
->cr_label
= found_cred
->cr_label
;
5634 nextp
->cr_flags
= found_cred
->cr_flags
;
5636 space
+= sizeof(debug_ucred
);
5637 if (space
> req
->oldlen
) {
5638 FREE(cred_listp
, M_TEMP
);
5643 req
->oldlen
= space
;
5644 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5645 FREE(cred_listp
, M_TEMP
);
5650 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5651 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5654 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5658 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5663 cred_debug_buffer
* bt_bufp
;
5664 cred_backtrace
* nextp
;
5666 /* This is a readonly node. */
5667 if (req
->newptr
!= USER_ADDR_NULL
) {
5671 if (cred_debug_buf_p
== NULL
) {
5675 /* calculate space needed */
5676 space
= sizeof(cred_debug_buf_p
->next_slot
);
5677 space
+= (sizeof(cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5679 /* they are querying us so just return the space required. */
5680 if (req
->oldptr
== USER_ADDR_NULL
) {
5681 req
->oldidx
= space
;
5685 if (space
> req
->oldlen
) {
5689 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5690 if (bt_bufp
== NULL
) {
5694 /* fill in backtrace info to send back */
5695 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5696 space
= sizeof(bt_bufp
->next_slot
);
5698 nextp
= &bt_bufp
->stack_buffer
[0];
5699 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5700 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[i
].depth
;
5701 for (j
= 0; j
< nextp
->depth
; j
++) {
5702 nextp
->stack
[j
] = cred_debug_buf_p
->stack_buffer
[i
].stack
[j
];
5704 space
+= sizeof(*nextp
);
5707 req
->oldlen
= space
;
5708 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5709 FREE(bt_bufp
, M_TEMP
);
5713 #endif /* DEBUG_CRED */
5717 **********************************************************************
5718 * The following routines will be moved to a policy_posix.c module at
5719 * some future point.
5720 **********************************************************************
5726 * Description: Helper function to create a kauth_cred_t credential that is
5727 * initally labelled with a specific POSIX credential label
5729 * Parameters: pcred The posix_cred_t to use as the initial
5732 * Returns: (kauth_cred_t) The credential that was found in the
5734 * NULL kauth_cred_add() failed, or there was
5735 * no egid specified, or we failed to
5736 * attach a label to the new credential
5738 * Notes: This function currently wraps kauth_cred_create(), and is the
5739 * only consumer of that ill-fated function, apart from bsd_init().
5740 * It exists solely to support the NFS server code creation of
5741 * credentials based on the over-the-wire RPC calls containing
5742 * traditional POSIX credential information being tunneled to
5743 * the server host from the client machine.
5745 * In the future, we hope this function goes away.
5747 * In the short term, it creates a temporary credential, puts
5748 * the POSIX information from NFS into it, and then calls
5749 * kauth_cred_create(), as an internal implementation detail.
5751 * If we have to keep it around in the medium term, it will
5752 * create a new kauth_cred_t, then label it with a POSIX label
5753 * corresponding to the contents of the kauth_cred_t. If the
5754 * policy_posix MACF module is not loaded, it will instead
5755 * substitute a posix_cred_t which GRANTS all access (effectively
5756 * a "root" credential) in order to not prevent NFS from working
5757 * in the case that we are not supporting POSIX credentials.
5760 posix_cred_create(posix_cred_t pcred
)
5762 struct ucred temp_cred
;
5764 bzero(&temp_cred
, sizeof(temp_cred
));
5765 temp_cred
.cr_posix
= *pcred
;
5767 return kauth_cred_create(&temp_cred
);
5774 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5775 * any, which is associated with it.
5777 * Parameters: cred The credential to obtain the label from
5779 * Returns: posix_cred_t The POSIX credential label
5781 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5782 * this function will return a pointer to a posix_cred_t which
5783 * GRANTS all access (effectively, a "root" credential). This is
5784 * necessary to support legacy code which insists on tightly
5785 * integrating POSIX credentials into its APIs, including, but
5786 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5787 * NFSv3, signals, dtrace, and a large number of kauth routines
5788 * used to implement POSIX permissions related system calls.
5790 * In the event that the policy_posix MACF module IS loaded, and
5791 * there is no POSIX label on the kauth_cred_t credential, this
5792 * function will return a pointer to a posix_cred_t which DENIES
5793 * all access (effectively, a "deny rights granted by POSIX"
5794 * credential). This is necessary to support the concept of a
5795 * transiently loaded POSIX policy, or kauth_cred_t credentials
5796 * which can not be used in conjunctions with POSIX permissions
5799 * This function currently returns the address of the cr_posix
5800 * field of the supplied kauth_cred_t credential, and as such
5801 * currently can not fail. In the future, this will not be the
5805 posix_cred_get(kauth_cred_t cred
)
5807 return &cred
->cr_posix
;
5814 * Description: Label a kauth_cred_t with a POSIX credential label
5816 * Parameters: cred The credential to label
5817 * pcred The POSIX credential t label it with
5821 * Notes: This function is currently void in order to permit it to fit
5822 * in with the current MACF framework label methods which allow
5823 * labeling to fail silently. This is like acceptable for
5824 * mandatory access controls, but not for POSIX, since those
5825 * access controls are advisory. We will need to consider a
5826 * return value in a future version of the MACF API.
5828 * This operation currently cannot fail, as currently the POSIX
5829 * credential is a subfield of the kauth_cred_t (ucred), which
5830 * MUST be valid. In the future, this will not be the case.
5833 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5835 cred
->cr_posix
= *pcred
; /* structure assign for now */
5842 * Description: Perform a POSIX access check for a protected object
5844 * Parameters: cred The credential to check
5845 * object_uid The POSIX UID of the protected object
5846 * object_gid The POSIX GID of the protected object
5847 * object_mode The POSIX mode of the protected object
5848 * mode_req The requested POSIX access rights
5850 * Returns 0 Access is granted
5851 * EACCES Access is denied
5853 * Notes: This code optimizes the case where the world and group rights
5854 * would both grant the requested rights to avoid making a group
5855 * membership query. This is a big performance win in the case
5856 * where this is true.
5859 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5862 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5863 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5864 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5867 * Check first for owner rights
5869 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
) {
5874 * Combined group and world rights check, if we don't have owner rights
5876 * OPTIMIZED: If group and world rights would grant the same bits, and
5877 * they set of requested bits is in both, then we can simply check the
5878 * world rights, avoiding a group membership check, which is expensive.
5880 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5884 * NON-OPTIMIZED: requires group membership check.
5886 if ((mode_req
& mode_group
) != mode_req
) {
5888 * exclusion group : treat errors as "is a member"
5890 * NON-OPTIMIZED: +group would deny; must check group
5892 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5894 * DENY: +group denies
5898 if ((mode_req
& mode_world
) != mode_req
) {
5900 * DENY: both -group & world would deny
5905 * ALLOW: allowed by -group and +world
5912 * inclusion group; treat errors as "not a member"
5914 * NON-OPTIMIZED: +group allows, world denies; must
5917 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5919 * ALLOW: allowed by +group
5923 if ((mode_req
& mode_world
) != mode_req
) {
5925 * DENY: both -group & world would deny
5930 * ALLOW: allowed by -group and +world