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 <libkern/OSAtomic.h>
63 #include <kern/task.h>
64 #include <kern/lock.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 void mach_kauth_cred_uthread_update( void );
79 #define CRED_DIAGNOSTIC 0
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) *(int *)&_u.g_guid[0],*(int *)&_u.g_guid[4],*(int *)&_u.g_guid[8],*(int *)&_u.g_guid[12]
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 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
190 static int kauth_resolver_complete(user_addr_t message
);
191 static int kauth_resolver_getwork(user_addr_t message
);
192 static int kauth_resolver_getwork2(user_addr_t message
);
193 static __attribute__((noinline
)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
194 struct kauth_resolver_work
*);
196 #define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
198 struct kauth_identity
{
199 TAILQ_ENTRY(kauth_identity
) ki_link
;
204 gid_t ki_supgrps
[NGROUPS
];
207 const char *ki_name
; /* string name from string cache */
209 * Expiry times are the earliest time at which we will disregard the
210 * cached state and go to userland. Before then if the valid bit is
211 * set, we will return the cached value. If it's not set, we will
212 * not go to userland to resolve, just assume that there is no answer
215 time_t ki_groups_expiry
;
216 time_t ki_guid_expiry
;
217 time_t ki_ntsid_expiry
;
220 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
221 static lck_mtx_t
*kauth_identity_mtx
;
222 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
223 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
224 #define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
225 static int kauth_identity_cachemax
= KAUTH_IDENTITY_CACHEMAX_DEFAULT
;
226 static int kauth_identity_count
;
228 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
229 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
230 const char *name
, int nametype
);
231 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
232 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
233 static void kauth_identity_trimcache(int newsize
);
234 static void kauth_identity_lru(struct kauth_identity
*kip
);
235 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
236 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
237 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
238 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
239 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
240 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
241 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
243 struct kauth_group_membership
{
244 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
245 uid_t gm_uid
; /* the identity whose membership we're recording */
246 gid_t gm_gid
; /* group of which they are a member */
247 time_t gm_expiry
; /* TTL for the membership, or 0 for persistent entries */
249 #define KAUTH_GROUP_ISMEMBER (1<<0)
252 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
253 static lck_mtx_t
*kauth_groups_mtx
;
254 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
255 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
256 #define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
257 static int kauth_groups_cachemax
= KAUTH_GROUPS_CACHEMAX_DEFAULT
;
258 static int kauth_groups_count
;
260 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
261 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
262 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
263 static void kauth_groups_trimcache(int newsize
);
265 #endif /* CONFIG_EXT_RESOLVER */
267 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
268 static int kauth_cred_primes_index
= 0;
269 static int kauth_cred_table_size
= 0;
271 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
272 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
274 #define KAUTH_CRED_HASH_DEBUG 0
276 static int kauth_cred_add(kauth_cred_t new_cred
);
277 static boolean_t
kauth_cred_remove(kauth_cred_t cred
);
278 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
279 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
280 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
281 static boolean_t
kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
283 #if KAUTH_CRED_HASH_DEBUG
284 static int kauth_cred_count
= 0;
285 static void kauth_cred_hash_print(void);
286 static void kauth_cred_print(kauth_cred_t cred
);
289 #if CONFIG_EXT_RESOLVER
292 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
294 * Description: Waits for the user space daemon to respond to the request
295 * we made. Function declared non inline to be visible in
296 * stackshots and spindumps as well as debugging.
298 * Parameters: workp Work queue entry.
300 * Returns: 0 on Success.
301 * EIO if Resolver is dead.
302 * EINTR thread interrupted in msleep
303 * EWOULDBLOCK thread timed out in msleep
304 * ERESTART returned by msleep.
307 static __attribute__((noinline
)) int
308 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
309 struct kauth_resolver_work
*workp
)
314 /* we could compute a better timeout here */
315 ts
.tv_sec
= kauth_resolver_timeout
;
317 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
318 /* request has been completed? */
319 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
321 /* woken because the resolver has died? */
322 if (kauth_resolver_identity
== 0) {
335 * kauth_resolver_init
337 * Description: Initialize the daemon side of the credential identity resolver
343 * Notes: Initialize the credential identity resolver for use; the
344 * credential identity resolver is the KPI used by the user
345 * space credential identity resolver daemon to communicate
346 * with the kernel via the identitysvc() system call..
348 * This is how membership in more than 16 groups (1 effective
349 * and 15 supplementary) is supported, and also how UID's,
350 * UUID's, and so on, are translated to/from POSIX credential
353 * The credential identity resolver operates by attempting to
354 * determine identity first from the credential, then from
355 * the kernel credential identity cache, and finally by
356 * enqueueing a request to a user space daemon.
358 * This function is called from kauth_init() in the file
359 * kern_authorization.c.
362 kauth_resolver_init(void)
364 TAILQ_INIT(&kauth_resolver_unsubmitted
);
365 TAILQ_INIT(&kauth_resolver_submitted
);
366 TAILQ_INIT(&kauth_resolver_done
);
367 kauth_resolver_sequence
= 31337;
368 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
373 * kauth_resolver_submit
375 * Description: Submit an external credential identity resolution request to
376 * the user space daemon.
378 * Parameters: lkp A pointer to an external
380 * extend_data extended data for kr_extend
383 * EWOULDBLOCK No resolver registered
384 * EINTR Operation interrupted (e.g. by
386 * ENOMEM Could not allocate work item
387 * copyinstr:EFAULT Bad message from user space
388 * workp->kr_result:??? An error from the user space
389 * daemon (includes ENOENT!)
394 * Notes: Allocate a work queue entry, submit the work and wait for
395 * the operation to either complete or time out. Outstanding
396 * operations may also be cancelled.
398 * Submission is by means of placing the item on a work queue
399 * which is serviced by an external resolver thread calling
400 * into the kernel. The caller then sleeps until timeout,
401 * cancellation, or an external resolver thread calls in with
402 * a result message to kauth_resolver_complete(). All of these
403 * events wake the caller back up.
405 * This code is called from either kauth_cred_ismember_gid()
406 * for a group membership request, or it is called from
407 * kauth_cred_cache_lookup() when we get a cache miss.
410 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
412 struct kauth_resolver_work
*workp
, *killp
;
414 int error
, shouldfree
;
416 /* no point actually blocking if the resolver isn't up yet */
417 if (kauth_resolver_identity
== 0) {
419 * We've already waited an initial <kauth_resolver_timeout>
420 * seconds with no result.
422 * Sleep on a stack address so no one wakes us before timeout;
423 * we sleep a half a second in case we are a high priority
424 * process, so that memberd doesn't starve while we are in a
425 * tight loop between user and kernel, eating all the CPU.
427 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
428 if (kauth_resolver_identity
== 0) {
430 * if things haven't changed while we were asleep,
431 * tell the caller we couldn't get an authoritative
438 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
442 workp
->kr_work
= *lkp
;
443 workp
->kr_extend
= extend_data
;
445 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
446 workp
->kr_result
= 0;
449 * We insert the request onto the unsubmitted queue, the call in from
450 * the resolver will it to the submitted thread when appropriate.
452 KAUTH_RESOLVER_LOCK();
453 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
454 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
457 * XXX We *MUST NOT* attempt to coalesce identical work items due to
458 * XXX the inability to ensure order of update of the request item
459 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
460 * XXX for each item repeat the update when they wake up.
462 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
465 * Wake up an external resolver thread to deal with the new work; one
466 * may not be available, and if not, then the request will be grabbed
467 * when a resolver thread comes back into the kernel to request new
470 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
471 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
473 /* if the request was processed, copy the result */
475 *lkp
= workp
->kr_work
;
477 if (error
== EWOULDBLOCK
) {
478 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
479 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
480 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
483 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
485 * If the request timed out and was never collected, the resolver
486 * is dead and probably not coming back anytime soon. In this
487 * case we revert to no-resolver behaviour, and punt all the other
488 * sleeping requests to clear the backlog.
490 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
493 * Make the current resolver non-authoritative, and mark it as
494 * no longer registered to prevent kauth_cred_ismember_gid()
495 * enqueueing more work until a new one is registered. This
496 * mitigates the damage a crashing resolver may inflict.
498 kauth_resolver_identity
= 0;
499 kauth_resolver_registered
= 0;
501 /* kill all the other requestes that are waiting as well */
502 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
504 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
506 /* Cause all waiting-for-work threads to return EIO */
507 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
512 * drop our reference on the work item, and note whether we should
515 if (--workp
->kr_refs
<= 0) {
516 /* work out which list we have to remove it from */
517 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
518 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
519 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
520 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
521 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
522 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
524 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
528 /* someone else still has a reference on this request */
532 /* collect request result */
534 error
= workp
->kr_result
;
536 KAUTH_RESOLVER_UNLOCK();
539 * If we dropped the last reference, free the request.
542 FREE(workp
, M_KAUTH
);
545 KAUTH_DEBUG("RESOLVER - returning %d", error
);
553 * Description: System call interface for the external identity resolver.
555 * Parameters: uap->message Message from daemon to kernel
557 * Returns: 0 Successfully became resolver
558 * EPERM Not the resolver process
559 * kauth_authorize_generic:EPERM Not root user
560 * kauth_resolver_complete:EIO
561 * kauth_resolver_complete:EFAULT
562 * kauth_resolver_getwork:EINTR
563 * kauth_resolver_getwork:EFAULT
565 * Notes: This system call blocks until there is work enqueued, at
566 * which time the kernel wakes it up, and a message from the
567 * kernel is copied out to the identity resolution daemon, which
568 * proceed to attempt to resolve it. When the resolution has
569 * completed (successfully or not), the daemon called back into
570 * this system call to give the result to the kernel, and wait
571 * for the next request.
574 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
576 int opcode
= uap
->opcode
;
577 user_addr_t message
= uap
->message
;
578 struct kauth_resolver_work
*workp
;
579 struct kauth_cache_sizes sz_arg
;
584 * New server registering itself.
586 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
587 new_id
= current_proc()->p_pid
;
588 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
589 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
592 KAUTH_RESOLVER_LOCK();
593 if (kauth_resolver_identity
!= new_id
) {
594 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
596 * We have a new server, so assume that all the old requests have been lost.
598 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
599 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
600 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
601 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
602 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
605 * Allow user space resolver to override the
606 * external resolution timeout
608 if (message
> 30 && message
< 10000) {
609 kauth_resolver_timeout
= message
;
610 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
612 kauth_resolver_identity
= new_id
;
613 kauth_resolver_registered
= 1;
614 kauth_identitysvc_has_registered
= 1;
615 wakeup(&kauth_resolver_unsubmitted
);
617 KAUTH_RESOLVER_UNLOCK();
622 * Beyond this point, we must be the resolver process.
624 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
625 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
629 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
630 KAUTH_IDENTITY_LOCK();
631 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
632 KAUTH_IDENTITY_UNLOCK();
635 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
636 KAUTH_GROUPS_UNLOCK();
638 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof (sz_arg
))) != 0) {
643 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
644 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof (sz_arg
))) != 0) {
648 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
649 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
653 KAUTH_IDENTITY_LOCK();
654 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
655 kauth_identity_trimcache(kauth_identity_cachemax
);
656 KAUTH_IDENTITY_UNLOCK();
659 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
660 kauth_groups_trimcache(kauth_groups_cachemax
);
661 KAUTH_GROUPS_UNLOCK();
664 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
665 KAUTH_IDENTITY_LOCK();
666 kauth_identity_trimcache(0);
667 KAUTH_IDENTITY_UNLOCK();
670 kauth_groups_trimcache(0);
671 KAUTH_GROUPS_UNLOCK();
672 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
674 * Terminate outstanding requests; without an authoritative
675 * resolver, we are now back on our own authority.
677 struct kauth_resolver_work
*killp
;
679 KAUTH_RESOLVER_LOCK();
682 * Clear the identity, but also mark it as unregistered so
683 * there is no explicit future expectation of us getting a
684 * new resolver any time soon.
686 kauth_resolver_identity
= 0;
687 kauth_resolver_registered
= 0;
689 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
691 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
693 /* Cause all waiting-for-work threads to return EIO */
694 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
695 KAUTH_RESOLVER_UNLOCK();
699 * Got a result returning?
701 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
702 if ((error
= kauth_resolver_complete(message
)) != 0)
707 * Caller wants to take more work?
709 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
710 if ((error
= kauth_resolver_getwork(message
)) != 0)
719 * kauth_resolver_getwork_continue
721 * Description: Continuation for kauth_resolver_getwork
723 * Parameters: result Error code or 0 for the sleep
724 * that got us to this function
727 * EINTR Interrupted (e.g. by signal)
728 * kauth_resolver_getwork2:EFAULT
730 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
734 kauth_resolver_getwork_continue(int result
)
741 KAUTH_RESOLVER_UNLOCK();
746 * If we lost a race with another thread/memberd restarting, then we
747 * need to go back to sleep to look for more work. If it was memberd
748 * restarting, then the msleep0() will error out here, as our thread
749 * will already be "dead".
751 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
754 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
756 * If this is a wakeup from another thread in the resolver
757 * deregistering it, error out the request-for-work thread
759 if (!kauth_resolver_identity
)
761 KAUTH_RESOLVER_UNLOCK();
765 thread
= current_thread();
766 ut
= get_bsdthread_info(thread
);
767 message
= ut
->uu_kevent
.uu_kauth
.message
;
768 return(kauth_resolver_getwork2(message
));
773 * kauth_resolver_getwork2
775 * Decription: Common utility function to copy out a identity resolver work
776 * item from the kernel to user space as part of the user space
777 * identity resolver requesting work.
779 * Parameters: message message to user space
782 * EFAULT Bad user space message address
784 * Notes: This common function exists to permit the use of continuations
785 * in the identity resolution process. This frees up the stack
786 * while we are waiting for the user space resolver to complete
787 * a request. This is specifically used so that our per thread
788 * cost can be small, and we will therefore be willing to run a
789 * larger number of threads in the user space identity resolver.
792 kauth_resolver_getwork2(user_addr_t message
)
794 struct kauth_resolver_work
*workp
;
798 * Note: We depend on the caller protecting us from a NULL work item
799 * queue, since we must have the kauth resolver lock on entry to this
802 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
805 * Copy out the external lookup structure for the request, not
806 * including the el_extend field, which contains the address of the
807 * external buffer provided by the external resolver into which we
808 * copy the extension request information.
811 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
812 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
816 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
817 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
818 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
819 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
824 * Handle extended requests here; if we have a request of a type where
825 * the kernel wants a translation of extended information, then we need
826 * to copy it out into the extended buffer, assuming the buffer is
827 * valid; we only attempt to get the buffer address if we have request
828 * data to copy into it.
832 * translate a user@domain string into a uid/gid/whatever
834 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
837 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
839 size_t actual
; /* not used */
841 * Use copyoutstr() to reduce the copy size; we let
842 * this catch a NULL uaddr because we shouldn't be
843 * asking in that case anyway.
845 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
848 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
852 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
853 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
854 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
855 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
858 KAUTH_RESOLVER_UNLOCK();
864 * kauth_resolver_getwork
866 * Description: Get a work item from the enqueued requests from the kernel and
867 * give it to the user space daemon.
869 * Parameters: message message to user space
872 * EINTR Interrupted (e.g. by signal)
873 * kauth_resolver_getwork2:EFAULT
875 * Notes: This function blocks in a continuation if there are no work
876 * items available for processing at the time the user space
877 * identity resolution daemon makes a request for work. This
878 * permits a large number of threads to be used by the daemon,
879 * without using a lot of wired kernel memory when there are no
880 * actual request outstanding.
883 kauth_resolver_getwork(user_addr_t message
)
885 struct kauth_resolver_work
*workp
;
888 KAUTH_RESOLVER_LOCK();
890 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
891 thread_t thread
= current_thread();
892 struct uthread
*ut
= get_bsdthread_info(thread
);
894 ut
->uu_kevent
.uu_kauth
.message
= message
;
895 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
896 KAUTH_RESOLVER_UNLOCK();
898 * If this is a wakeup from another thread in the resolver
899 * deregistering it, error out the request-for-work thread
901 if (!kauth_resolver_identity
)
905 return kauth_resolver_getwork2(message
);
910 * kauth_resolver_complete
912 * Description: Return a result from userspace.
914 * Parameters: message message from user space
917 * EIO The resolver is dead
918 * copyin:EFAULT Bad message from user space
921 kauth_resolver_complete(user_addr_t message
)
923 struct kauth_identity_extlookup extl
;
924 struct kauth_resolver_work
*workp
;
925 struct kauth_resolver_work
*killp
;
929 * Copy in the mesage, including the extension field, since we are
930 * copying into a local variable.
932 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
933 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
937 KAUTH_RESOLVER_LOCK();
941 switch (extl
.el_result
) {
942 case KAUTH_EXTLOOKUP_INPROG
:
946 /* XXX this should go away once memberd is updated */
948 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
954 case KAUTH_EXTLOOKUP_SUCCESS
:
957 case KAUTH_EXTLOOKUP_FATAL
:
958 /* fatal error means the resolver is dead */
959 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
961 * Terminate outstanding requests; without an authoritative
962 * resolver, we are now back on our own authority. Tag the
963 * resolver unregistered to prevent kauth_cred_ismember_gid()
964 * enqueueing more work until a new one is registered. This
965 * mitigates the damage a crashing resolver may inflict.
967 kauth_resolver_identity
= 0;
968 kauth_resolver_registered
= 0;
970 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
972 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
974 /* Cause all waiting-for-work threads to return EIO */
975 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
976 /* and return EIO to the caller */
980 case KAUTH_EXTLOOKUP_BADRQ
:
981 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
985 case KAUTH_EXTLOOKUP_FAILURE
:
986 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
991 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
997 * In the case of a fatal error, we assume that the resolver will
998 * restart quickly and re-collect all of the outstanding requests.
999 * Thus, we don't complete the request which returned the fatal
1002 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1003 /* scan our list for this request */
1004 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1006 if (workp
->kr_seqno
== extl
.el_seqno
) {
1009 * Get the request of the submitted queue so
1010 * that it is not cleaned up out from under
1013 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1014 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1015 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1016 workp
->kr_result
= result
;
1018 /* Copy the result message to the work item. */
1019 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1022 * Check if we have a result in the extension
1023 * field; if we do, then we need to separately
1024 * copy the data from the message el_extend
1025 * into the request buffer that's in the work
1026 * item. We have to do it here because we do
1027 * not want to wake up the waiter until the
1028 * data is in their buffer, and because the
1029 * actual request response may be destroyed
1030 * by the time the requester wakes up, and they
1031 * do not have access to the user space buffer
1034 * It is safe to drop and reacquire the lock
1035 * here because we've already removed the item
1036 * from the submission queue, but have not yet
1037 * moved it to the completion queue. Note that
1038 * near simultaneous requests may result in
1039 * duplication of requests for items in this
1040 * window. This should not be a performance
1041 * issue and is easily detectable by comparing
1042 * time to live on last response vs. time of
1043 * next request in the resolver logs.
1045 if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1046 size_t actual
; /* notused */
1048 KAUTH_RESOLVER_UNLOCK();
1049 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1050 KAUTH_RESOLVER_LOCK();
1054 * Move the completed work item to the
1055 * completion queue and wake up requester(s)
1057 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1064 * Note that it's OK for us not to find anything; if the request has
1065 * timed out the work record will be gone.
1067 KAUTH_RESOLVER_UNLOCK();
1071 #endif /* CONFIG_EXT_RESOLVER */
1078 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1079 #define KI_VALID_GID (1<<1)
1080 #define KI_VALID_GUID (1<<2)
1081 #define KI_VALID_NTSID (1<<3)
1082 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1083 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1084 #define KI_VALID_GROUPS (1<<6)
1086 #if CONFIG_EXT_RESOLVER
1088 * kauth_identity_init
1090 * Description: Initialize the kernel side of the credential identity resolver
1092 * Parameters: (void)
1096 * Notes: Initialize the credential identity resolver for use; the
1097 * credential identity resolver is the KPI used to communicate
1098 * with a user space credential identity resolver daemon.
1100 * This function is called from kauth_init() in the file
1101 * kern_authorization.c.
1104 kauth_identity_init(void)
1106 TAILQ_INIT(&kauth_identities
);
1107 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1112 * kauth_identity_alloc
1114 * Description: Allocate and fill out a kauth_identity structure for
1115 * translation between {UID|GID}/GUID/NTSID
1119 * Returns: NULL Insufficient memory to satisfy
1121 * !NULL A pointer to the allocated
1122 * structure, filled in
1124 * Notes: It is illegal to translate between UID and GID; any given UUID
1125 * or NTSID can only refer to an NTSID or UUID (respectively),
1126 * and *either* a UID *or* a GID, but not both.
1128 static struct kauth_identity
*
1129 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1130 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1131 const char *name
, int nametype
)
1133 struct kauth_identity
*kip
;
1135 /* get and fill in a new identity */
1136 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1138 if (gid
!= KAUTH_GID_NONE
) {
1140 kip
->ki_valid
= KI_VALID_GID
;
1142 if (uid
!= KAUTH_UID_NONE
) {
1143 if (kip
->ki_valid
& KI_VALID_GID
)
1144 panic("can't allocate kauth identity with both uid and gid");
1146 kip
->ki_valid
= KI_VALID_UID
;
1149 assert(supgrpcnt
<= NGROUPS
);
1150 assert(supgrps
!= NULL
);
1151 if (kip
->ki_valid
& KI_VALID_GID
)
1152 panic("can't allocate kauth identity with both gid and supplementary groups");
1153 kip
->ki_supgrpcnt
= supgrpcnt
;
1154 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1155 kip
->ki_valid
|= KI_VALID_GROUPS
;
1157 kip
->ki_groups_expiry
= groups_expiry
;
1158 if (guidp
!= NULL
) {
1159 kip
->ki_guid
= *guidp
;
1160 kip
->ki_valid
|= KI_VALID_GUID
;
1162 kip
->ki_guid_expiry
= guid_expiry
;
1163 if (ntsidp
!= NULL
) {
1164 kip
->ki_ntsid
= *ntsidp
;
1165 kip
->ki_valid
|= KI_VALID_NTSID
;
1167 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1169 kip
->ki_name
= name
;
1170 kip
->ki_valid
|= nametype
;
1178 * kauth_identity_register_and_free
1180 * Description: Register an association between identity tokens. The passed
1181 * 'kip' is consumed by this function.
1183 * Parameters: kip Pointer to kauth_identity
1184 * structure to register
1188 * Notes: The memory pointer to by 'kip' is assumed to have been
1189 * previously allocated via kauth_identity_alloc().
1192 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1194 struct kauth_identity
*ip
;
1197 * We search the cache for the UID listed in the incoming association.
1198 * If we already have an entry, the new information is merged.
1201 KAUTH_IDENTITY_LOCK();
1202 if (kip
->ki_valid
& KI_VALID_UID
) {
1203 if (kip
->ki_valid
& KI_VALID_GID
)
1204 panic("kauth_identity: can't insert record with both UID and GID as key");
1205 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1206 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1208 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1209 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1210 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1213 panic("kauth_identity: can't insert record without UID or GID as key");
1217 /* we already have an entry, merge/overwrite */
1218 if (kip
->ki_valid
& KI_VALID_GUID
) {
1219 ip
->ki_guid
= kip
->ki_guid
;
1220 ip
->ki_valid
|= KI_VALID_GUID
;
1222 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1223 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1224 ip
->ki_ntsid
= kip
->ki_ntsid
;
1225 ip
->ki_valid
|= KI_VALID_NTSID
;
1227 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1228 /* a valid ki_name field overwrites the previous name field */
1229 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1230 /* if there's an old one, discard it */
1231 const char *oname
= NULL
;
1232 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1233 oname
= ip
->ki_name
;
1234 ip
->ki_name
= kip
->ki_name
;
1235 kip
->ki_name
= oname
;
1237 /* and discard the incoming entry */
1241 * if we don't have any information on this identity, add it;
1242 * if it pushes us over our limit, discard the oldest one.
1244 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1245 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1246 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1247 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1248 kauth_identity_count
--;
1251 KAUTH_IDENTITY_UNLOCK();
1252 /* have to drop lock before freeing expired entry (it may be in use) */
1254 /* if the ki_name field is used, clear it first */
1255 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1256 vfs_removename(ip
->ki_name
);
1257 /* free the expired entry */
1264 * kauth_identity_updatecache
1266 * Description: Given a lookup result, add any associations that we don't
1267 * currently have; replace ones which have changed.
1269 * Parameters: elp External lookup result from
1270 * user space daemon to kernel
1271 * rkip pointer to returned kauth
1273 * extend_data Extended data (can vary)
1278 * *rkip Modified (if non-NULL)
1280 * Notes: For extended information requests, this code relies on the fact
1281 * that elp->el_flags is never used as an rvalue, and is only
1282 * ever bit-tested for valid lookup information we are willing
1285 * XXX: We may have to do the same in the case that extended data was
1286 * passed out to user space to ensure that the request string
1287 * gets cached; we may also be able to use the rkip as an
1288 * input to avoid this. The jury is still out.
1290 * XXX: This codes performance could be improved for multiple valid
1291 * results by combining the loop iteration in a single loop.
1294 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1297 struct kauth_identity
*kip
;
1298 const char *speculative_name
= NULL
;
1303 * If there is extended data, and that data represents a name rather
1304 * than something else, speculatively create an entry for it in the
1305 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1306 * over the allocation later.
1308 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1309 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1310 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1313 /* user identity? */
1314 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1315 KAUTH_IDENTITY_LOCK();
1316 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1317 /* matching record */
1318 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1319 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1320 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1321 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1322 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1323 kip
->ki_valid
|= KI_VALID_GROUPS
;
1324 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1326 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1327 kip
->ki_guid
= elp
->el_uguid
;
1328 kip
->ki_valid
|= KI_VALID_GUID
;
1330 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1331 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1332 kip
->ki_ntsid
= elp
->el_usid
;
1333 kip
->ki_valid
|= KI_VALID_NTSID
;
1335 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1336 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1337 const char *oname
= kip
->ki_name
;
1338 kip
->ki_name
= speculative_name
;
1339 speculative_name
= NULL
;
1340 kip
->ki_valid
|= KI_VALID_PWNAM
;
1343 * free oname (if any) outside
1346 speculative_name
= oname
;
1349 kauth_identity_lru(kip
);
1352 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1356 KAUTH_IDENTITY_UNLOCK();
1357 /* not found in cache, add new record */
1359 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1360 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1361 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1362 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1363 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1364 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1365 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1366 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1367 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1372 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1373 speculative_name
= NULL
;
1374 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1375 kauth_identity_register_and_free(kip
);
1380 /* group identity? (ignore, if we already processed it as a user) */
1381 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1382 KAUTH_IDENTITY_LOCK();
1383 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1384 /* matching record */
1385 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1386 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1387 kip
->ki_guid
= elp
->el_gguid
;
1388 kip
->ki_valid
|= KI_VALID_GUID
;
1390 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1391 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1392 kip
->ki_ntsid
= elp
->el_gsid
;
1393 kip
->ki_valid
|= KI_VALID_NTSID
;
1395 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1396 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1397 const char *oname
= kip
->ki_name
;
1398 kip
->ki_name
= speculative_name
;
1399 speculative_name
= NULL
;
1400 kip
->ki_valid
|= KI_VALID_GRNAM
;
1403 * free oname (if any) outside
1406 speculative_name
= oname
;
1409 kauth_identity_lru(kip
);
1412 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1416 KAUTH_IDENTITY_UNLOCK();
1417 /* not found in cache, add new record */
1419 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1420 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1421 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1422 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1423 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1424 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1425 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1426 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1427 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1432 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1433 speculative_name
= NULL
;
1434 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1435 kauth_identity_register_and_free(kip
);
1440 /* If we have a name reference to drop, drop it here */
1441 if (speculative_name
!= NULL
) {
1442 vfs_removename(speculative_name
);
1448 * Trim older entries from the identity cache.
1450 * Must be called with the identity cache lock held.
1453 kauth_identity_trimcache(int newsize
) {
1454 struct kauth_identity
*kip
;
1456 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1458 while (kauth_identity_count
> newsize
) {
1459 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1460 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1461 kauth_identity_count
--;
1467 * kauth_identity_lru
1469 * Description: Promote the entry to the head of the LRU, assumes the cache
1472 * Parameters: kip kauth identity to move to the
1473 * head of the LRU list, if it's
1478 * Notes: This is called even if the entry has expired; typically an
1479 * expired entry that's been looked up is about to be revalidated,
1480 * and having it closer to the head of the LRU means finding it
1481 * quickly again when the revalidation comes through.
1484 kauth_identity_lru(struct kauth_identity
*kip
)
1486 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1487 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1488 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1494 * kauth_identity_guid_expired
1496 * Description: Handle lazy expiration of GUID translations.
1498 * Parameters: kip kauth identity to check for
1501 * Returns: 1 Expired
1505 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1510 * Expiration time of 0 means this entry is persistent.
1512 if (kip
->ki_guid_expiry
== 0)
1516 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1518 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1523 * kauth_identity_ntsid_expired
1525 * Description: Handle lazy expiration of NTSID translations.
1527 * Parameters: kip kauth identity to check for
1530 * Returns: 1 Expired
1534 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1539 * Expiration time of 0 means this entry is persistent.
1541 if (kip
->ki_ntsid_expiry
== 0)
1545 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1547 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1551 * kauth_identity_groups_expired
1553 * Description: Handle lazy expiration of supplemental group translations.
1555 * Parameters: kip kauth identity to check for
1558 * Returns: 1 Expired
1562 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1567 * Expiration time of 0 means this entry is persistent.
1569 if (kip
->ki_groups_expiry
== 0)
1573 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1575 return((kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0);
1579 * kauth_identity_find_uid
1581 * Description: Search for an entry by UID
1583 * Parameters: uid UID to find
1584 * kir Pointer to return area
1585 * getname Name buffer, if ki_name wanted
1591 * *klr Modified, if found
1594 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1596 struct kauth_identity
*kip
;
1598 KAUTH_IDENTITY_LOCK();
1599 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1600 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1601 kauth_identity_lru(kip
);
1602 /* Copy via structure assignment */
1604 /* If a name is wanted and one exists, copy it out */
1605 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1606 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1610 KAUTH_IDENTITY_UNLOCK();
1611 return((kip
== NULL
) ? ENOENT
: 0);
1616 * kauth_identity_find_gid
1618 * Description: Search for an entry by GID
1620 * Parameters: gid GID to find
1621 * kir Pointer to return area
1622 * getname Name buffer, if ki_name wanted
1628 * *klr Modified, if found
1631 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1633 struct kauth_identity
*kip
;
1635 KAUTH_IDENTITY_LOCK();
1636 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1637 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1638 kauth_identity_lru(kip
);
1639 /* Copy via structure assignment */
1641 /* If a name is wanted and one exists, copy it out */
1642 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1643 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1647 KAUTH_IDENTITY_UNLOCK();
1648 return((kip
== NULL
) ? ENOENT
: 0);
1653 * kauth_identity_find_guid
1655 * Description: Search for an entry by GUID
1657 * Parameters: guidp Pointer to GUID to find
1658 * kir Pointer to return area
1659 * getname Name buffer, if ki_name wanted
1665 * *klr Modified, if found
1667 * Note: The association may be expired, in which case the caller
1668 * may elect to call out to userland to revalidate.
1671 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1673 struct kauth_identity
*kip
;
1675 KAUTH_IDENTITY_LOCK();
1676 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1677 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1678 kauth_identity_lru(kip
);
1679 /* Copy via structure assignment */
1681 /* If a name is wanted and one exists, copy it out */
1682 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1683 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1687 KAUTH_IDENTITY_UNLOCK();
1688 return((kip
== NULL
) ? ENOENT
: 0);
1692 * kauth_identity_find_nam
1694 * Description: Search for an entry by name
1696 * Parameters: name Pointer to name to find
1697 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1698 * kir Pointer to return area
1704 * *klr Modified, if found
1707 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1709 struct kauth_identity
*kip
;
1711 KAUTH_IDENTITY_LOCK();
1712 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1713 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1714 kauth_identity_lru(kip
);
1715 /* Copy via structure assignment */
1720 KAUTH_IDENTITY_UNLOCK();
1721 return((kip
== NULL
) ? ENOENT
: 0);
1726 * kauth_identity_find_ntsid
1728 * Description: Search for an entry by NTSID
1730 * Parameters: ntsid Pointer to NTSID to find
1731 * kir Pointer to return area
1732 * getname Name buffer, if ki_name wanted
1738 * *klr Modified, if found
1740 * Note: The association may be expired, in which case the caller
1741 * may elect to call out to userland to revalidate.
1744 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1746 struct kauth_identity
*kip
;
1748 KAUTH_IDENTITY_LOCK();
1749 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1750 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1751 kauth_identity_lru(kip
);
1752 /* Copy via structure assignment */
1754 /* If a name is wanted and one exists, copy it out */
1755 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1756 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1760 KAUTH_IDENTITY_UNLOCK();
1761 return((kip
== NULL
) ? ENOENT
: 0);
1763 #endif /* CONFIG_EXT_RESOLVER */
1769 guid_t kauth_null_guid
;
1775 * Description: Determine the equality of two GUIDs
1777 * Parameters: guid1 Pointer to first GUID
1778 * guid2 Pointer to second GUID
1780 * Returns: 0 If GUIDs are unequal
1781 * !0 If GUIDs are equal
1784 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1786 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1791 * kauth_wellknown_guid
1793 * Description: Determine if a GUID is a well-known GUID
1795 * Parameters: guid Pointer to GUID to check
1797 * Returns: KAUTH_WKG_NOT Not a well known GUID
1798 * KAUTH_WKG_EVERYBODY "Everybody"
1799 * KAUTH_WKG_NOBODY "Nobody"
1800 * KAUTH_WKG_OWNER "Other"
1801 * KAUTH_WKG_GROUP "Group"
1804 kauth_wellknown_guid(guid_t
*guid
)
1806 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1809 * All WKGs begin with the same 12 bytes.
1811 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1813 * The final 4 bytes are our code (in network byte order).
1815 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1818 return(KAUTH_WKG_EVERYBODY
);
1820 return(KAUTH_WKG_NOBODY
);
1822 return(KAUTH_WKG_OWNER
);
1824 return(KAUTH_WKG_GROUP
);
1827 return(KAUTH_WKG_NOT
);
1834 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1836 * Parameters: sid1 Pointer to first NTSID
1837 * sid2 Pointer to second NTSID
1839 * Returns: 0 If GUIDs are unequal
1840 * !0 If GUIDs are equal
1843 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1845 /* check sizes for equality, also sanity-check size while we're at it */
1846 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1847 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1848 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1857 * We support four tokens representing identity:
1858 * - Credential reference
1861 * - NT security identifier
1863 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1870 * kauth_cred_change_egid
1872 * Description: Set EGID by changing the first element of cr_groups for the
1873 * passed credential; if the new EGID exists in the list of
1874 * groups already, then rotate the old EGID into its position,
1875 * otherwise replace it
1877 * Parameters: cred Pointer to the credential to modify
1878 * new_egid The new EGID to set
1880 * Returns: 0 The egid did not displace a member of
1881 * the supplementary group list
1882 * 1 The egid being set displaced a member
1883 * of the supplementary groups list
1885 * Note: Utility function; internal use only because of locking.
1887 * This function operates on the credential passed; the caller
1888 * must operate either on a newly allocated credential (one for
1889 * which there is no hash cache reference and no externally
1890 * visible pointer reference), or a template credential.
1893 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1899 #endif /* radar_4600026 */
1900 gid_t old_egid
= kauth_cred_getgid(cred
);
1901 posix_cred_t pcred
= posix_cred_get(cred
);
1903 /* Ignoring the first entry, scan for a match for the new egid */
1904 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1906 * If we find a match, swap them so we don't lose overall
1909 if (pcred
->cr_groups
[i
] == new_egid
) {
1910 pcred
->cr_groups
[i
] = old_egid
;
1911 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1918 #error Fix radar 4600026 first!!!
1921 This is correct for memberd behaviour, but incorrect for POSIX; to address
1922 this, we would need to automatically opt-out any SUID/SGID binary, and force
1923 it to use initgroups to opt back in. We take the approach of considering it
1924 opt'ed out in any group of 16 displacement instead, since it's a much more
1925 conservative approach (i.e. less likely to cause things to break).
1929 * If we displaced a member of the supplementary groups list of the
1930 * credential, and we have not opted out of memberd, then if memberd
1931 * says that the credential is a member of the group, then it has not
1932 * actually been displaced.
1934 * NB: This is typically a cold code path.
1936 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1937 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1940 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1942 #endif /* radar_4600026 */
1944 /* set the new EGID into the old spot */
1945 pcred
->cr_groups
[0] = new_egid
;
1954 * Description: Fetch UID from credential
1956 * Parameters: cred Credential to examine
1958 * Returns: (uid_t) UID associated with credential
1961 kauth_cred_getuid(kauth_cred_t cred
)
1963 NULLCRED_CHECK(cred
);
1964 return(posix_cred_get(cred
)->cr_uid
);
1969 * kauth_cred_getruid
1971 * Description: Fetch RUID from credential
1973 * Parameters: cred Credential to examine
1975 * Returns: (uid_t) RUID associated with credential
1978 kauth_cred_getruid(kauth_cred_t cred
)
1980 NULLCRED_CHECK(cred
);
1981 return(posix_cred_get(cred
)->cr_ruid
);
1986 * kauth_cred_getsvuid
1988 * Description: Fetch SVUID from credential
1990 * Parameters: cred Credential to examine
1992 * Returns: (uid_t) SVUID associated with credential
1995 kauth_cred_getsvuid(kauth_cred_t cred
)
1997 NULLCRED_CHECK(cred
);
1998 return(posix_cred_get(cred
)->cr_svuid
);
2005 * Description: Fetch GID from credential
2007 * Parameters: cred Credential to examine
2009 * Returns: (gid_t) GID associated with credential
2012 kauth_cred_getgid(kauth_cred_t cred
)
2014 NULLCRED_CHECK(cred
);
2015 return(posix_cred_get(cred
)->cr_gid
);
2020 * kauth_cred_getrgid
2022 * Description: Fetch RGID from credential
2024 * Parameters: cred Credential to examine
2026 * Returns: (gid_t) RGID associated with credential
2029 kauth_cred_getrgid(kauth_cred_t cred
)
2031 NULLCRED_CHECK(cred
);
2032 return(posix_cred_get(cred
)->cr_rgid
);
2037 * kauth_cred_getsvgid
2039 * Description: Fetch SVGID from credential
2041 * Parameters: cred Credential to examine
2043 * Returns: (gid_t) SVGID associated with credential
2046 kauth_cred_getsvgid(kauth_cred_t cred
)
2048 NULLCRED_CHECK(cred
);
2049 return(posix_cred_get(cred
)->cr_svgid
);
2053 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2055 #if CONFIG_EXT_RESOLVER == 0
2057 * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
2060 kauth_cred_cache_lookup(__unused
int from
, __unused
int to
,
2061 __unused
void *src
, __unused
void *dst
)
2063 return (EWOULDBLOCK
);
2068 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2070 * Structure to hold supplemental groups. Used for impedance matching with
2071 * kauth_cred_cache_lookup below.
2079 * kauth_cred_uid2groups
2081 * Description: Fetch supplemental GROUPS from UID
2083 * Parameters: uid UID to examine
2084 * groups pointer to an array of gid_ts
2085 * gcount pointer to the number of groups wanted/returned
2087 * Returns: 0 Success
2088 * kauth_cred_cache_lookup:EINVAL
2091 * *groups Modified, if successful
2092 * *gcount Modified, if successful
2096 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, int *gcount
)
2100 struct supgroups supgroups
;
2101 supgroups
.count
= gcount
;
2102 supgroups
.groups
= groups
;
2104 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2111 * kauth_cred_guid2pwnam
2113 * Description: Fetch PWNAM from GUID
2115 * Parameters: guidp Pointer to GUID to examine
2116 * pwnam Pointer to user@domain buffer
2118 * Returns: 0 Success
2119 * kauth_cred_cache_lookup:EINVAL
2122 * *pwnam Modified, if successful
2124 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2127 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2129 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
2134 * kauth_cred_guid2grnam
2136 * Description: Fetch GRNAM from GUID
2138 * Parameters: guidp Pointer to GUID to examine
2139 * grnam Pointer to group@domain buffer
2141 * Returns: 0 Success
2142 * kauth_cred_cache_lookup:EINVAL
2145 * *grnam Modified, if successful
2147 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2150 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2152 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
2157 * kauth_cred_pwnam2guid
2159 * Description: Fetch PWNAM from GUID
2161 * Parameters: pwnam String containing user@domain
2162 * guidp Pointer to buffer for GUID
2164 * Returns: 0 Success
2165 * kauth_cred_cache_lookup:EINVAL
2168 * *guidp Modified, if successful
2170 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2171 * bytes in size, including the NUL termination of the string.
2174 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2176 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
2181 * kauth_cred_grnam2guid
2183 * Description: Fetch GRNAM from GUID
2185 * Parameters: grnam String containing group@domain
2186 * guidp Pointer to buffer for GUID
2188 * Returns: 0 Success
2189 * kauth_cred_cache_lookup:EINVAL
2192 * *guidp Modified, if successful
2194 * Notes: grnam should not point to a request larger than MAXPATHLEN
2195 * bytes in size, including the NUL termination of the string.
2198 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2200 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
2205 * kauth_cred_guid2uid
2207 * Description: Fetch UID from GUID
2209 * Parameters: guidp Pointer to GUID to examine
2210 * uidp Pointer to buffer for UID
2212 * Returns: 0 Success
2213 * kauth_cred_cache_lookup:EINVAL
2216 * *uidp Modified, if successful
2219 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2221 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
2226 * kauth_cred_guid2gid
2228 * Description: Fetch GID from GUID
2230 * Parameters: guidp Pointer to GUID to examine
2231 * gidp Pointer to buffer for GID
2233 * Returns: 0 Success
2234 * kauth_cred_cache_lookup:EINVAL
2237 * *gidp Modified, if successful
2240 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2242 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2247 * kauth_cred_ntsid2uid
2249 * Description: Fetch UID from NTSID
2251 * Parameters: sidp Pointer to NTSID to examine
2252 * uidp Pointer to buffer for UID
2254 * Returns: 0 Success
2255 * kauth_cred_cache_lookup:EINVAL
2258 * *uidp Modified, if successful
2261 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2263 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2268 * kauth_cred_ntsid2gid
2270 * Description: Fetch GID from NTSID
2272 * Parameters: sidp Pointer to NTSID to examine
2273 * gidp Pointer to buffer for GID
2275 * Returns: 0 Success
2276 * kauth_cred_cache_lookup:EINVAL
2279 * *gidp Modified, if successful
2282 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2284 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2289 * kauth_cred_ntsid2guid
2291 * Description: Fetch GUID from NTSID
2293 * Parameters: sidp Pointer to NTSID to examine
2294 * guidp Pointer to buffer for GUID
2296 * Returns: 0 Success
2297 * kauth_cred_cache_lookup:EINVAL
2300 * *guidp Modified, if successful
2303 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2305 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2310 * kauth_cred_uid2guid
2312 * Description: Fetch GUID from UID
2314 * Parameters: uid UID to examine
2315 * guidp Pointer to buffer for GUID
2317 * Returns: 0 Success
2318 * kauth_cred_cache_lookup:EINVAL
2321 * *guidp Modified, if successful
2324 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2326 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2331 * kauth_cred_getguid
2333 * Description: Fetch GUID from credential
2335 * Parameters: cred Credential to examine
2336 * guidp Pointer to buffer for GUID
2338 * Returns: 0 Success
2339 * kauth_cred_cache_lookup:EINVAL
2342 * *guidp Modified, if successful
2345 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2347 NULLCRED_CHECK(cred
);
2348 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2353 * kauth_cred_getguid
2355 * Description: Fetch GUID from GID
2357 * Parameters: gid GID to examine
2358 * guidp Pointer to buffer for GUID
2360 * Returns: 0 Success
2361 * kauth_cred_cache_lookup:EINVAL
2364 * *guidp Modified, if successful
2367 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2369 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2374 * kauth_cred_uid2ntsid
2376 * Description: Fetch NTSID from UID
2378 * Parameters: uid UID to examine
2379 * sidp Pointer to buffer for NTSID
2381 * Returns: 0 Success
2382 * kauth_cred_cache_lookup:EINVAL
2385 * *sidp Modified, if successful
2388 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2390 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2395 * kauth_cred_getntsid
2397 * Description: Fetch NTSID from credential
2399 * Parameters: cred Credential to examine
2400 * sidp Pointer to buffer for NTSID
2402 * Returns: 0 Success
2403 * kauth_cred_cache_lookup:EINVAL
2406 * *sidp Modified, if successful
2409 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2411 NULLCRED_CHECK(cred
);
2412 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2417 * kauth_cred_gid2ntsid
2419 * Description: Fetch NTSID from GID
2421 * Parameters: gid GID to examine
2422 * sidp Pointer to buffer for NTSID
2424 * Returns: 0 Success
2425 * kauth_cred_cache_lookup:EINVAL
2428 * *sidp Modified, if successful
2431 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2433 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2438 * kauth_cred_guid2ntsid
2440 * Description: Fetch NTSID from GUID
2442 * Parameters: guidp Pointer to GUID to examine
2443 * sidp Pointer to buffer for NTSID
2445 * Returns: 0 Success
2446 * kauth_cred_cache_lookup:EINVAL
2449 * *sidp Modified, if successful
2452 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2454 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2459 * kauth_cred_cache_lookup
2461 * Description: Lookup a translation in the cache; if one is not found, and
2462 * the attempt was not fatal, submit the request to the resolver
2463 * instead, and wait for it to complete or be aborted.
2465 * Parameters: from Identity information we have
2466 * to Identity information we want
2467 * src Pointer to buffer containing
2468 * the source identity
2469 * dst Pointer to buffer to receive
2470 * the target identity
2472 * Returns: 0 Success
2473 * EINVAL Unknown source identity type
2475 #if CONFIG_EXT_RESOLVER
2477 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2479 struct kauth_identity ki
;
2480 struct kauth_identity_extlookup el
;
2482 uint64_t extend_data
= 0ULL;
2483 int (* expired
)(struct kauth_identity
*kip
);
2484 char *namebuf
= NULL
;
2486 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2489 * Look for an existing cache entry for this association.
2490 * If the entry has not expired, return the cached information.
2491 * We do not cache user@domain translations here; they use too
2492 * much memory to hold onto forever, and can not be updated
2495 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2501 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2504 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2507 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2509 case KI_VALID_NTSID
:
2510 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2512 case KI_VALID_PWNAM
:
2513 case KI_VALID_GRNAM
:
2514 /* Names are unique in their 'from' space */
2515 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2520 /* lookup failure or error */
2522 /* any other error is fatal */
2523 if (error
!= ENOENT
) {
2524 /* XXX bogus check - this is not possible */
2525 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2529 /* found a valid cached entry, check expiry */
2532 expired
= kauth_identity_guid_expired
;
2534 case KI_VALID_NTSID
:
2535 expired
= kauth_identity_ntsid_expired
;
2537 case KI_VALID_GROUPS
:
2538 expired
= kauth_identity_groups_expired
;
2543 expired
= kauth_identity_guid_expired
;
2545 case KI_VALID_NTSID
:
2546 expired
= kauth_identity_ntsid_expired
;
2554 * If no expiry function, or not expired, we have found
2558 if (!expired(&ki
)) {
2559 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2560 expired
= NULL
; /* must clear it is used as a flag */
2563 * We leave ki_valid set here; it contains a
2564 * translation but the TTL has expired. If we can't
2565 * get a result from the resolver, we will use it as
2566 * a better-than nothing alternative.
2569 KAUTH_DEBUG("CACHE - expired entry found");
2572 KAUTH_DEBUG("CACHE - no expiry function");
2576 /* do we have a translation? */
2577 if (ki
.ki_valid
& to
) {
2578 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2579 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2583 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2584 * If we went looking for a translation from GUID or NTSID and
2585 * found a translation that wasn't for our desired type, then
2586 * don't bother calling the resolver. We know that this
2587 * GUID/NTSID can't translate to our desired type.
2591 case KI_VALID_NTSID
:
2594 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2595 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2600 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2601 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2613 * We failed to find a cache entry; call the resolver.
2615 * Note: We ask for as much non-extended data as we can get,
2616 * and only provide (or ask for) extended information if
2617 * we have a 'from' (or 'to') which requires it. This
2618 * way we don't pay for the extra transfer overhead for
2619 * data we don't need.
2621 bzero(&el
, sizeof(el
));
2622 el
.el_info_pid
= current_proc()->p_pid
;
2625 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2626 el
.el_uid
= *(uid_t
*)src
;
2629 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2630 el
.el_gid
= *(gid_t
*)src
;
2633 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2634 el
.el_uguid
= *(guid_t
*)src
;
2635 el
.el_gguid
= *(guid_t
*)src
;
2637 case KI_VALID_NTSID
:
2638 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2639 el
.el_usid
= *(ntsid_t
*)src
;
2640 el
.el_gsid
= *(ntsid_t
*)src
;
2642 case KI_VALID_PWNAM
:
2643 /* extra overhead */
2644 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2645 extend_data
= CAST_USER_ADDR_T(src
);
2647 case KI_VALID_GRNAM
:
2648 /* extra overhead */
2649 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2650 extend_data
= CAST_USER_ADDR_T(src
);
2656 * Here we ask for everything all at once, to avoid having to work
2657 * out what we really want now, or might want soon.
2659 * Asking for SID translations when we don't know we need them right
2660 * now is going to cause excess work to be done if we're connected
2661 * to a network that thinks it can translate them. This list needs
2662 * to get smaller/smarter.
2664 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2665 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2666 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2667 if (to
== KI_VALID_PWNAM
) {
2668 /* extra overhead */
2669 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2670 extend_data
= CAST_USER_ADDR_T(dst
);
2672 if (to
== KI_VALID_GRNAM
) {
2673 /* extra overhead */
2674 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2675 extend_data
= CAST_USER_ADDR_T(dst
);
2677 if (to
== KI_VALID_GROUPS
) {
2678 /* Expensive and only useful for an NFS client not using kerberos */
2679 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2680 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2682 * Copy the current supplemental groups for the resolver.
2683 * The resolver should check these groups first and if
2684 * the user (uid) is still a member it should endeavor to
2685 * keep them in the list. Otherwise NFS clients could get
2686 * changing access to server file system objects on each
2689 el
.el_sup_grp_cnt
= ki
.ki_supgrpcnt
;
2691 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof (el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2692 /* Let the resolver know these were the previous valid groups */
2693 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2694 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2696 KAUTH_DEBUG("GROUPS: no valid groups to send");
2700 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2702 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2704 error
= kauth_resolver_submit(&el
, extend_data
);
2706 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2708 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2710 /* was the external lookup successful? */
2713 * Save the results from the lookup - we may have other
2714 * information, even if we didn't get a guid or the
2717 * If we came from a name, we know the extend_data is valid.
2719 if (from
== KI_VALID_PWNAM
)
2720 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2721 else if (from
== KI_VALID_GRNAM
)
2722 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2724 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2727 * Check to see if we have a valid cache entry
2728 * originating from the result.
2730 if (!(ki
.ki_valid
& to
)) {
2738 * Copy from the appropriate struct kauth_identity cache entry
2739 * structure into the destination buffer area.
2743 *(uid_t
*)dst
= ki
.ki_uid
;
2746 *(gid_t
*)dst
= ki
.ki_gid
;
2749 *(guid_t
*)dst
= ki
.ki_guid
;
2751 case KI_VALID_NTSID
:
2752 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2754 case KI_VALID_GROUPS
: {
2755 struct supgroups
*gp
= (struct supgroups
*)dst
;
2756 u_int32_t limit
= ki
.ki_supgrpcnt
;
2759 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2763 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2766 case KI_VALID_PWNAM
:
2767 case KI_VALID_GRNAM
:
2768 /* handled in kauth_resolver_complete() */
2773 KAUTH_DEBUG("CACHE - returned successfully");
2779 * Group membership cache.
2781 * XXX the linked-list implementation here needs to be optimized.
2787 * Description: Initialize the groups cache
2789 * Parameters: (void)
2793 * Notes: Initialize the groups cache for use; the group cache is used
2794 * to avoid unnecessary calls out to user space.
2796 * This function is called from kauth_init() in the file
2797 * kern_authorization.c.
2800 kauth_groups_init(void)
2802 TAILQ_INIT(&kauth_groups
);
2803 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2808 * kauth_groups_expired
2810 * Description: Handle lazy expiration of group membership cache entries
2812 * Parameters: gm group membership entry to
2813 * check for expiration
2815 * Returns: 1 Expired
2819 kauth_groups_expired(struct kauth_group_membership
*gm
)
2824 * Expiration time of 0 means this entry is persistent.
2826 if (gm
->gm_expiry
== 0)
2831 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2838 * Description: Promote the entry to the head of the LRU, assumes the cache
2841 * Parameters: kip group membership entry to move
2842 * to the head of the LRU list,
2843 * if it's not already there
2847 * Notes: This is called even if the entry has expired; typically an
2848 * expired entry that's been looked up is about to be revalidated,
2849 * and having it closer to the head of the LRU means finding it
2850 * quickly again when the revalidation comes through.
2853 kauth_groups_lru(struct kauth_group_membership
*gm
)
2855 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2856 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2857 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2863 * kauth_groups_updatecache
2865 * Description: Given a lookup result, add any group cache associations that
2866 * we don't currently have.
2868 * Parameters: elp External lookup result from
2869 * user space daemon to kernel
2870 * rkip pointer to returned kauth
2876 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2878 struct kauth_group_membership
*gm
;
2881 /* need a valid response if we are to cache anything */
2883 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2884 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2890 * Search for an existing record for this association before inserting
2891 * a new one; if we find one, update it instead of creating a new one
2893 KAUTH_GROUPS_LOCK();
2894 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2895 if ((el
->el_uid
== gm
->gm_uid
) &&
2896 (el
->el_gid
== gm
->gm_gid
)) {
2897 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2898 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2900 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2902 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2903 kauth_groups_lru(gm
);
2907 KAUTH_GROUPS_UNLOCK();
2909 /* if we found an entry to update, stop here */
2913 /* allocate a new record */
2914 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2916 gm
->gm_uid
= el
->el_uid
;
2917 gm
->gm_gid
= el
->el_gid
;
2918 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2919 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2921 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2923 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2927 * Insert the new entry. Note that it's possible to race ourselves
2928 * here and end up with duplicate entries in the list. Wasteful, but
2929 * harmless since the first into the list will never be looked up,
2930 * and thus will eventually just fall off the end.
2932 KAUTH_GROUPS_LOCK();
2933 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2934 if (++kauth_groups_count
> kauth_groups_cachemax
) {
2935 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2936 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2937 kauth_groups_count
--;
2941 KAUTH_GROUPS_UNLOCK();
2943 /* free expired cache entry */
2949 * Trim older entries from the group membership cache.
2951 * Must be called with the group cache lock held.
2954 kauth_groups_trimcache(int new_size
) {
2955 struct kauth_group_membership
*gm
;
2957 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
2959 while (kauth_groups_count
> new_size
) {
2960 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2961 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2962 kauth_groups_count
--;
2966 #endif /* CONFIG_EXT_RESOLVER */
2969 * Group membership KPI
2973 * kauth_cred_ismember_gid
2975 * Description: Given a credential and a GID, determine if the GID is a member
2976 * of one of the supplementary groups associated with the given
2979 * Parameters: cred Credential to check in
2980 * gid GID to check for membership
2981 * resultp Pointer to int to contain the
2982 * result of the call
2984 * Returns: 0 Success
2985 * ENOENT Could not perform lookup
2986 * kauth_resolver_submit:EWOULDBLOCK
2987 * kauth_resolver_submit:EINTR
2988 * kauth_resolver_submit:ENOMEM
2989 * kauth_resolver_submit:ENOENT User space daemon did not vend
2991 * kauth_resolver_submit:??? Unlikely error from user space
2994 * *resultp (modified) 1 Is member
2997 * Notes: This function guarantees not to modify resultp when returning
3000 * This function effectively checks the EGID as well, since the
3001 * EGID is cr_groups[0] as an implementation detail.
3004 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3006 posix_cred_t pcred
= posix_cred_get(cred
);
3010 * Check the per-credential list of override groups.
3012 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3013 * the cache should be used for that case.
3015 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3016 if (gid
== pcred
->cr_groups
[i
]) {
3023 * If we don't have a UID for group membership checks, the in-cred list
3024 * was authoritative and we can stop here.
3026 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3031 #if CONFIG_EXT_RESOLVER
3032 struct kauth_group_membership
*gm
;
3033 struct kauth_identity_extlookup el
;
3037 * If the resolver hasn't checked in yet, we are early in the boot
3038 * phase and the local group list is complete and authoritative.
3040 if (!kauth_resolver_registered
) {
3046 /* XXX check supplementary groups */
3047 /* XXX check whiteout groups */
3048 /* XXX nesting of supplementary/whiteout groups? */
3051 * Check the group cache.
3053 KAUTH_GROUPS_LOCK();
3054 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3055 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3056 kauth_groups_lru(gm
);
3061 /* did we find a membership entry? */
3063 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3064 KAUTH_GROUPS_UNLOCK();
3066 /* if we did, we can return now */
3068 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3072 /* nothing in the cache, need to go to userland */
3073 bzero(&el
, sizeof(el
));
3074 el
.el_info_pid
= current_proc()->p_pid
;
3075 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3076 el
.el_uid
= pcred
->cr_gmuid
;
3078 el
.el_member_valid
= 0; /* XXX set by resolver? */
3080 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3082 error
= kauth_resolver_submit(&el
, 0ULL);
3084 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3088 /* save the results from the lookup */
3089 kauth_groups_updatecache(&el
);
3091 /* if we successfully ascertained membership, report */
3092 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3093 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3105 * kauth_cred_ismember_guid
3107 * Description: Determine whether the supplied credential is a member of the
3108 * group nominated by GUID.
3110 * Parameters: cred Credential to check in
3111 * guidp Pointer to GUID whose group
3112 * we are testing for membership
3113 * resultp Pointer to int to contain the
3114 * result of the call
3116 * Returns: 0 Success
3117 * kauth_cred_guid2gid:EINVAL
3118 * kauth_cred_ismember_gid:ENOENT
3119 * kauth_resolver_submit:ENOENT User space daemon did not vend
3121 * kauth_cred_ismember_gid:EWOULDBLOCK
3122 * kauth_cred_ismember_gid:EINTR
3123 * kauth_cred_ismember_gid:ENOMEM
3124 * kauth_cred_ismember_gid:??? Unlikely error from user space
3127 * *resultp (modified) 1 Is member
3131 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3135 switch (kauth_wellknown_guid(guidp
)) {
3136 case KAUTH_WKG_NOBODY
:
3139 case KAUTH_WKG_EVERYBODY
:
3143 #if CONFIG_EXT_RESOLVER
3145 struct kauth_identity ki
;
3149 * Grovel the identity cache looking for this GUID.
3150 * If we find it, and it is for a user record, return
3151 * false because it's not a group.
3153 * This is necessary because we don't have -ve caching
3154 * of group memberships, and we really want to avoid
3155 * calling out to the resolver if at all possible.
3157 * Because we're called by the ACL evaluator, and the
3158 * ACL evaluator is likely to encounter ACEs for users,
3159 * this is expected to be a common case.
3162 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3163 !kauth_identity_guid_expired(&ki
)) {
3164 if (ki
.ki_valid
& KI_VALID_GID
) {
3165 /* It's a group after all... */
3169 if (ki
.ki_valid
& KI_VALID_UID
) {
3174 #endif /* 6603280 */
3176 * Attempt to translate the GUID to a GID. Even if
3177 * this fails, we will have primed the cache if it is
3178 * a user record and we'll see it above the next time
3181 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3183 * If we have no guid -> gid translation, it's not a group and
3184 * thus the cred can't be a member.
3186 if (error
== ENOENT
) {
3192 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3195 #else /* CONFIG_EXT_RESOLVER */
3197 #endif /* CONFIG_EXT_RESOLVER */
3204 * kauth_cred_gid_subset
3206 * Description: Given two credentials, determine if all GIDs associated with
3207 * the first are also associated with the second
3209 * Parameters: cred1 Credential to check for
3210 * cred2 Credential to check in
3211 * resultp Pointer to int to contain the
3212 * result of the call
3214 * Returns: 0 Success
3215 * non-zero See kauth_cred_ismember_gid for
3219 * *resultp (modified) 1 Is subset
3222 * Notes: This function guarantees not to modify resultp when returning
3226 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3228 int i
, err
, res
= 1;
3230 posix_cred_t pcred1
= posix_cred_get(cred1
);
3231 posix_cred_t pcred2
= posix_cred_get(cred2
);
3233 /* First, check the local list of groups */
3234 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3235 gid
= pcred1
->cr_groups
[i
];
3236 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3240 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3246 /* Check real gid */
3247 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3251 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3252 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3257 /* Finally, check saved gid */
3258 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
3262 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3263 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3274 * kauth_cred_issuser
3276 * Description: Fast replacement for issuser()
3278 * Parameters: cred Credential to check for super
3281 * Returns: 0 Not super user
3284 * Notes: This function uses a magic number which is not a manifest
3285 * constant; this is bad practice.
3288 kauth_cred_issuser(kauth_cred_t cred
)
3290 return(kauth_cred_getuid(cred
) == 0);
3298 /* lock protecting credential hash table */
3299 static lck_mtx_t
*kauth_cred_hash_mtx
;
3300 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3301 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
3302 #if KAUTH_CRED_HASH_DEBUG
3303 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3304 #else /* !KAUTH_CRED_HASH_DEBUG */
3305 #define KAUTH_CRED_HASH_LOCK_ASSERT()
3306 #endif /* !KAUTH_CRED_HASH_DEBUG */
3312 * Description: Initialize the credential hash cache
3314 * Parameters: (void)
3318 * Notes: Intialize the credential hash cache for use; the credential
3319 * hash cache is used convert duplicate credentials into a
3320 * single reference counted credential in order to save wired
3321 * kernel memory. In practice, this generally means a desktop
3322 * system runs with a few tens of credentials, instead of one
3323 * per process, one per thread, one per vnode cache entry, and
3324 * so on. This generally results in savings of 200K or more
3325 * (potentially much more on server systems).
3327 * The hash cache internally has a reference on the credential
3328 * for itself as a means of avoiding a reclaim race for a
3329 * credential in the process of having it's last non-hash
3330 * reference released. This would otherwise result in the
3331 * possibility of a freed credential that was still in uses due
3332 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3334 * On final release, the hash reference is droped, and the
3335 * credential is freed back to the system.
3337 * This function is called from kauth_init() in the file
3338 * kern_authorization.c.
3341 kauth_cred_init(void)
3345 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3346 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
3348 /*allocate credential hash table */
3349 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3350 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
3351 M_KAUTH
, M_WAITOK
| M_ZERO
);
3352 if (kauth_cred_table_anchor
== NULL
)
3353 panic("startup: kauth_cred_init");
3354 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
3355 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3363 * Description: Get the current thread's effective UID.
3365 * Parameters: (void)
3367 * Returns: (uid_t) The effective UID of the
3373 return(kauth_cred_getuid(kauth_cred_get()));
3380 * Description: Get the current thread's real UID.
3382 * Parameters: (void)
3384 * Returns: (uid_t) The real UID of the current
3390 return(kauth_cred_getruid(kauth_cred_get()));
3397 * Description: Get the current thread's effective GID.
3399 * Parameters: (void)
3401 * Returns: (gid_t) The effective GID of the
3407 return(kauth_cred_getgid(kauth_cred_get()));
3414 * Description: Get the current thread's real GID.
3416 * Parameters: (void)
3418 * Returns: (gid_t) The real GID of the current
3424 return(kauth_cred_getrgid(kauth_cred_get()));
3431 * Description: Returns a pointer to the current thread's credential
3433 * Parameters: (void)
3435 * Returns: (kauth_cred_t) Pointer to the current thread's
3438 * Notes: This function does not take a reference; because of this, the
3439 * caller MUST NOT do anything that would let the thread's
3440 * credential change while using the returned value, without
3441 * first explicitly taking their own reference.
3443 * If a caller intends to take a reference on the resulting
3444 * credential pointer from calling this function, it is strongly
3445 * recommended that the caller use kauth_cred_get_with_ref()
3446 * instead, to protect against any future changes to the cred
3447 * locking protocols; such changes could otherwise potentially
3448 * introduce race windows in the callers code.
3451 kauth_cred_get(void)
3454 struct uthread
*uthread
;
3456 uthread
= get_bsdthread_info(current_thread());
3458 if (uthread
== NULL
)
3459 panic("thread wants credential but has no BSD thread info");
3461 * We can lazy-bind credentials to threads, as long as their processes
3464 * XXX If we later inline this function, the code in this block
3465 * XXX should probably be called out in a function.
3467 if (uthread
->uu_ucred
== NOCRED
) {
3468 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3469 panic("thread wants credential but has no BSD process");
3470 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3472 return(uthread
->uu_ucred
);
3476 mach_kauth_cred_uthread_update(void)
3481 uthread
= get_bsdthread_info(current_thread());
3482 proc
= current_proc();
3484 kauth_cred_uthread_update(uthread
, proc
);
3488 * kauth_cred_uthread_update
3490 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3491 * late-bind the uthread cred to the proc cred.
3493 * Parameters: uthread_t The uthread to update
3494 * proc_t The process to update to
3498 * Notes: This code is common code called from system call or trap entry
3499 * in the case that the process thread may have been changed
3500 * since the last time the thread entered the kernel. It is
3501 * generally only called with the current uthread and process as
3505 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3507 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3508 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3509 kauth_cred_t old
= uthread
->uu_ucred
;
3510 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3511 if (IS_VALID_CRED(old
))
3512 kauth_cred_unref(&old
);
3518 * kauth_cred_get_with_ref
3520 * Description: Takes a reference on the current thread's credential, and then
3521 * returns a pointer to it to the caller.
3523 * Parameters: (void)
3525 * Returns: (kauth_cred_t) Pointer to the current thread's
3526 * newly referenced credential
3528 * Notes: This function takes a reference on the credential before
3529 * returning it to the caller.
3531 * It is the responsibility of the calling code to release this
3532 * reference when the credential is no longer in use.
3534 * Since the returned reference may be a persistent reference
3535 * (e.g. one cached in another data structure with a lifetime
3536 * longer than the calling function), this release may be delayed
3537 * until such time as the persistent reference is to be destroyed.
3538 * An example of this would be the per vnode credential cache used
3539 * to accelerate lookup operations.
3542 kauth_cred_get_with_ref(void)
3545 struct uthread
*uthread
;
3547 uthread
= get_bsdthread_info(current_thread());
3549 if (uthread
== NULL
)
3550 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3551 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3552 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3555 * We can lazy-bind credentials to threads, as long as their processes
3558 * XXX If we later inline this function, the code in this block
3559 * XXX should probably be called out in a function.
3561 if (uthread
->uu_ucred
== NOCRED
) {
3562 /* take reference for new cred in thread */
3563 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3565 /* take a reference for our caller */
3566 kauth_cred_ref(uthread
->uu_ucred
);
3567 return(uthread
->uu_ucred
);
3572 * kauth_cred_proc_ref
3574 * Description: Takes a reference on the current process's credential, and
3575 * then returns a pointer to it to the caller.
3577 * Parameters: procp Process whose credential we
3578 * intend to take a reference on
3580 * Returns: (kauth_cred_t) Pointer to the process's
3581 * newly referenced credential
3583 * Locks: PROC_LOCK is held before taking the reference and released
3584 * after the refeence is taken to protect the p_ucred field of
3585 * the process referred to by procp.
3587 * Notes: This function takes a reference on the credential before
3588 * returning it to the caller.
3590 * It is the responsibility of the calling code to release this
3591 * reference when the credential is no longer in use.
3593 * Since the returned reference may be a persistent reference
3594 * (e.g. one cached in another data structure with a lifetime
3595 * longer than the calling function), this release may be delayed
3596 * until such time as the persistent reference is to be destroyed.
3597 * An example of this would be the per vnode credential cache used
3598 * to accelerate lookup operations.
3601 kauth_cred_proc_ref(proc_t procp
)
3606 cred
= proc_ucred(procp
);
3607 kauth_cred_ref(cred
);
3616 * Description: Allocate a new credential
3618 * Parameters: (void)
3620 * Returns: !NULL Newly allocated credential
3621 * NULL Insufficient memory
3623 * Notes: The newly allocated credential is zero'ed as part of the
3624 * allocation process, with the exception of the reference
3625 * count, which is set to 1 to indicate a single reference
3626 * held by the caller.
3628 * Since newly allocated credentials have no external pointers
3629 * referencing them, prior to making them visible in an externally
3630 * visible pointer (e.g. by adding them to the credential hash
3631 * cache) is the only legal time in which an existing credential
3632 * can be safely iinitialized or modified directly.
3634 * After initialization, the caller is expected to call the
3635 * function kauth_cred_add() to add the credential to the hash
3636 * cache, after which time it's frozen and becomes publically
3639 * The release protocol depends on kauth_hash_add() being called
3640 * before kauth_cred_rele() (there is a diagnostic panic which
3641 * will trigger if this protocol is not observed).
3643 * XXX: This function really ought to be static, rather than being
3644 * exported as KPI, since a failure of kauth_cred_add() can only
3645 * be handled by an explicit free of the credential; such frees
3646 * depend on knowlegdge of the allocation method used, which is
3647 * permitted to change between kernel revisions.
3649 * XXX: In the insufficient resource case, this code panic's rather
3650 * than returning a NULL pointer; the code that calls this
3651 * function needs to be audited before this can be changed.
3654 kauth_cred_alloc(void)
3656 kauth_cred_t newcred
;
3658 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3660 posix_cred_t newpcred
= posix_cred_get(newcred
);
3661 bzero(newcred
, sizeof(*newcred
));
3662 newcred
->cr_ref
= 1;
3663 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3664 /* must do this, or cred has same group membership as uid 0 */
3665 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3668 panic("kauth_cred_alloc: couldn't allocate credential");
3672 #if KAUTH_CRED_HASH_DEBUG
3677 mac_cred_label_init(newcred
);
3687 * Description: Look to see if we already have a known credential in the hash
3688 * cache; if one is found, bump the reference count and return
3689 * it. If there are no credentials that match the given
3690 * credential, then allocate a new credential.
3692 * Parameters: cred Template for credential to
3695 * Returns: (kauth_cred_t) The credential that was found
3696 * in the hash or created
3697 * NULL kauth_cred_add() failed, or
3698 * there was not an egid specified
3700 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3701 * maintain this field, we can't expect callers to know how it
3702 * needs to be set. Callers should be prepared for this field
3703 * to be overwritten.
3705 * XXX: This code will tight-loop if memory for a new credential is
3706 * persistently unavailable; this is perhaps not the wisest way
3707 * to handle this condition, but current callers do not expect
3711 kauth_cred_create(kauth_cred_t cred
)
3713 kauth_cred_t found_cred
, new_cred
= NULL
;
3714 posix_cred_t pcred
= posix_cred_get(cred
);
3717 KAUTH_CRED_HASH_LOCK_ASSERT();
3719 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3720 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3723 * If the template credential is not opting out of external
3724 * group membership resolution, then we need to check that
3725 * the UID we will be using is resolvable by the external
3726 * resolver. If it's not, then we opt it out anyway, since
3727 * all future external resolution requests will be failing
3728 * anyway, and potentially taking a long time to do it. We
3729 * use gid 0 because we always know it will exist and not
3730 * trigger additional lookups. This is OK, because we end up
3731 * precatching the information here as a result.
3733 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3735 * It's a recognized value; we don't really care about
3736 * the answer, so long as it's something the external
3737 * resolver could have vended.
3739 pcred
->cr_gmuid
= pcred
->cr_uid
;
3742 * It's not something the external resolver could
3743 * have vended, so we don't want to ask it more
3744 * questions about the credential in the future. This
3745 * speeds up future lookups, as long as the caller
3746 * caches results; otherwise, it the same recurring
3747 * cost. Since most credentials are used multiple
3748 * times, we still get some performance win from this.
3750 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3751 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3755 /* Caller *must* specify at least the egid in cr_groups[0] */
3756 if (pcred
->cr_ngroups
< 1)
3760 KAUTH_CRED_HASH_LOCK();
3761 found_cred
= kauth_cred_find(cred
);
3762 if (found_cred
!= NULL
) {
3764 * Found an existing credential so we'll bump
3765 * reference count and return
3767 kauth_cred_ref(found_cred
);
3768 KAUTH_CRED_HASH_UNLOCK();
3771 KAUTH_CRED_HASH_UNLOCK();
3774 * No existing credential found. Create one and add it to
3777 new_cred
= kauth_cred_alloc();
3778 if (new_cred
!= NULL
) {
3780 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3781 new_pcred
->cr_uid
= pcred
->cr_uid
;
3782 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3783 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3784 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3785 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3786 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3787 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3788 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3790 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3791 sizeof(new_cred
->cr_audit
));
3793 new_pcred
->cr_flags
= pcred
->cr_flags
;
3795 KAUTH_CRED_HASH_LOCK();
3796 err
= kauth_cred_add(new_cred
);
3797 KAUTH_CRED_HASH_UNLOCK();
3799 /* Retry if kauth_cred_add returns non zero value */
3803 mac_cred_label_destroy(new_cred
);
3805 AUDIT_SESSION_UNREF(new_cred
);
3807 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3817 * kauth_cred_setresuid
3819 * Description: Update the given credential using the UID arguments. The given
3820 * UIDs are used to set the effective UID, real UID, saved UID,
3821 * and GMUID (used for group membership checking).
3823 * Parameters: cred The original credential
3824 * ruid The new real UID
3825 * euid The new effective UID
3826 * svuid The new saved UID
3827 * gmuid KAUTH_UID_NONE -or- the new
3828 * group membership UID
3830 * Returns: (kauth_cred_t) The updated credential
3832 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3833 * setting, so if you don't want it to change, pass it the
3834 * previous value, explicitly.
3836 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3837 * if it returns a credential other than the one it is passed,
3838 * will have dropped the reference on the passed credential. All
3839 * callers should be aware of this, and treat this function as an
3840 * unref + ref, potentially on different credentials.
3842 * Because of this, the caller is expected to take its own
3843 * reference on the credential passed as the first parameter,
3844 * and be prepared to release the reference on the credential
3845 * that is returned to them, if it is not intended to be a
3846 * persistent reference.
3849 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3851 struct ucred temp_cred
;
3852 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3853 posix_cred_t pcred
= posix_cred_get(cred
);
3855 NULLCRED_CHECK(cred
);
3858 * We don't need to do anything if the UIDs we are changing are
3859 * already the same as the UIDs passed in
3861 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3862 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3863 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3864 (pcred
->cr_gmuid
== gmuid
)) {
3865 /* no change needed */
3870 * Look up in cred hash table to see if we have a matching credential
3871 * with the new values; this is done by calling kauth_cred_update().
3873 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3874 if (euid
!= KAUTH_UID_NONE
) {
3875 temp_pcred
->cr_uid
= euid
;
3877 if (ruid
!= KAUTH_UID_NONE
) {
3878 temp_pcred
->cr_ruid
= ruid
;
3880 if (svuid
!= KAUTH_UID_NONE
) {
3881 temp_pcred
->cr_svuid
= svuid
;
3885 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3886 * opt out of participation in external group resolution, unless we
3887 * unless we explicitly opt back in later.
3889 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3890 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3893 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3898 * kauth_cred_setresgid
3900 * Description: Update the given credential using the GID arguments. The given
3901 * GIDs are used to set the effective GID, real GID, and saved
3904 * Parameters: cred The original credential
3905 * rgid The new real GID
3906 * egid The new effective GID
3907 * svgid The new saved GID
3909 * Returns: (kauth_cred_t) The updated credential
3911 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3912 * if it returns a credential other than the one it is passed,
3913 * will have dropped the reference on the passed credential. All
3914 * callers should be aware of this, and treat this function as an
3915 * unref + ref, potentially on different credentials.
3917 * Because of this, the caller is expected to take its own
3918 * reference on the credential passed as the first parameter,
3919 * and be prepared to release the reference on the credential
3920 * that is returned to them, if it is not intended to be a
3921 * persistent reference.
3924 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3926 struct ucred temp_cred
;
3927 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3928 posix_cred_t pcred
= posix_cred_get(cred
);
3930 NULLCRED_CHECK(cred
);
3931 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3934 * We don't need to do anything if the given GID are already the
3935 * same as the GIDs in the credential.
3937 if (pcred
->cr_groups
[0] == egid
&&
3938 pcred
->cr_rgid
== rgid
&&
3939 pcred
->cr_svgid
== svgid
) {
3940 /* no change needed */
3945 * Look up in cred hash table to see if we have a matching credential
3946 * with the new values; this is done by calling kauth_cred_update().
3948 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3949 if (egid
!= KAUTH_GID_NONE
) {
3950 /* displacing a supplementary group opts us out of memberd */
3951 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3952 DEBUG_CRED_CHANGE("displaced!\n");
3953 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3954 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3956 DEBUG_CRED_CHANGE("not displaced\n");
3959 if (rgid
!= KAUTH_GID_NONE
) {
3960 temp_pcred
->cr_rgid
= rgid
;
3962 if (svgid
!= KAUTH_GID_NONE
) {
3963 temp_pcred
->cr_svgid
= svgid
;
3966 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3971 * Update the given credential with the given groups. We only allocate a new
3972 * credential when the given gid actually results in changes to the existing
3974 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3975 * which will be used for group membership checking.
3978 * kauth_cred_setgroups
3980 * Description: Update the given credential using the provide supplementary
3981 * group list and group membership UID
3983 * Parameters: cred The original credential
3984 * groups Pointer to gid_t array which
3985 * contains the new group list
3986 * groupcount The count of valid groups which
3987 * are contained in 'groups'
3988 * gmuid KAUTH_UID_NONE -or- the new
3989 * group membership UID
3991 * Returns: (kauth_cred_t) The updated credential
3993 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3994 * setting, so if you don't want it to change, pass it the
3995 * previous value, explicitly.
3997 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3998 * if it returns a credential other than the one it is passed,
3999 * will have dropped the reference on the passed credential. All
4000 * callers should be aware of this, and treat this function as an
4001 * unref + ref, potentially on different credentials.
4003 * Because of this, the caller is expected to take its own
4004 * reference on the credential passed as the first parameter,
4005 * and be prepared to release the reference on the credential
4006 * that is returned to them, if it is not intended to be a
4007 * persistent reference.
4009 * XXX: Changes are determined in ordinal order - if the caller passes
4010 * in the same groups list that is already present in the
4011 * credential, but the members are in a different order, even if
4012 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4013 * is considered a modification to the credential, and a new
4014 * credential is created.
4016 * This should perhaps be better optimized, but it is considered
4017 * to be the caller's problem.
4020 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
4023 struct ucred temp_cred
;
4024 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4027 NULLCRED_CHECK(cred
);
4029 pcred
= posix_cred_get(cred
);
4032 * We don't need to do anything if the given list of groups does not
4035 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4036 for (i
= 0; i
< groupcount
; i
++) {
4037 if (pcred
->cr_groups
[i
] != groups
[i
])
4040 if (i
== groupcount
) {
4041 /* no change needed */
4047 * Look up in cred hash table to see if we have a matching credential
4048 * with new values. If we are setting or clearing the gmuid, then
4049 * update the cr_flags, since clearing it is sticky. This permits an
4050 * opt-out of memberd processing using setgroups(), and an opt-in
4051 * using initgroups(). This is required for POSIX conformance.
4053 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4054 temp_pcred
->cr_ngroups
= groupcount
;
4055 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
4056 temp_pcred
->cr_gmuid
= gmuid
;
4057 if (gmuid
== KAUTH_UID_NONE
)
4058 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4060 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4062 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4066 * Notes: The return value exists to account for the possibility of a
4067 * kauth_cred_t without a POSIX label. This will be the case in
4068 * the future (see posix_cred_get() below, for more details).
4070 #if CONFIG_EXT_RESOLVER
4071 int kauth_external_supplementary_groups_supported
= 1;
4073 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4077 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
4079 int limit
= NGROUPS
;
4082 pcred
= posix_cred_get(cred
);
4084 #if CONFIG_EXT_RESOLVER
4086 * If we've not opted out of using the resolver, then convert the cred to a list
4087 * of supplemental groups. We do this only if there has been a resolver to talk to,
4088 * since we may be too early in boot, or in an environment that isn't using DS.
4090 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4091 uid_t uid
= kauth_cred_getuid(cred
);
4094 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4098 /* On error just fall through */
4099 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4101 #endif /* CONFIG_EXT_RESOLVER */
4104 * If they just want a copy of the groups list, they may not care
4105 * about the actual count. If they specify an input count, however,
4106 * treat it as an indicator of the buffer size available in grouplist,
4107 * and limit the returned list to that size.
4110 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4114 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4121 * kauth_cred_setuidgid
4123 * Description: Update the given credential using the UID and GID arguments.
4124 * The given UID is used to set the effective UID, real UID, and
4125 * saved UID. The given GID is used to set the effective GID,
4126 * real GID, and saved GID.
4128 * Parameters: cred The original credential
4129 * uid The new UID to use
4130 * gid The new GID to use
4132 * Returns: (kauth_cred_t) The updated credential
4134 * Notes: We set the gmuid to uid if the credential we are inheriting
4135 * from has not opted out of memberd participation; otherwise
4136 * we set it to KAUTH_UID_NONE
4138 * This code is only ever called from the per-thread credential
4139 * code path in the "set per thread credential" case; and in
4140 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4143 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4144 * if it returns a credential other than the one it is passed,
4145 * will have dropped the reference on the passed credential. All
4146 * callers should be aware of this, and treat this function as an
4147 * unref + ref, potentially on different credentials.
4149 * Because of this, the caller is expected to take its own
4150 * reference on the credential passed as the first parameter,
4151 * and be prepared to release the reference on the credential
4152 * that is returned to them, if it is not intended to be a
4153 * persistent reference.
4156 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4158 struct ucred temp_cred
;
4159 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4162 NULLCRED_CHECK(cred
);
4164 pcred
= posix_cred_get(cred
);
4167 * We don't need to do anything if the effective, real and saved
4168 * user IDs are already the same as the user ID passed into us.
4170 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4171 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4172 /* no change needed */
4177 * Look up in cred hash table to see if we have a matching credential
4178 * with the new values.
4180 bzero(&temp_cred
, sizeof(temp_cred
));
4181 temp_pcred
->cr_uid
= uid
;
4182 temp_pcred
->cr_ruid
= uid
;
4183 temp_pcred
->cr_svuid
= uid
;
4184 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4185 /* inherit the opt-out of memberd */
4186 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4187 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4188 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4190 temp_pcred
->cr_gmuid
= uid
;
4191 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4193 temp_pcred
->cr_ngroups
= 1;
4194 /* displacing a supplementary group opts us out of memberd */
4195 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4196 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4197 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4199 temp_pcred
->cr_rgid
= gid
;
4200 temp_pcred
->cr_svgid
= gid
;
4202 temp_cred
.cr_label
= cred
->cr_label
;
4205 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4210 * kauth_cred_setsvuidgid
4212 * Description: Function used by execve to set the saved uid and gid values
4213 * for suid/sgid programs
4215 * Parameters: cred The credential to update
4216 * uid The saved uid to set
4217 * gid The saved gid to set
4219 * Returns: (kauth_cred_t) The updated credential
4221 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4222 * if it returns a credential other than the one it is passed,
4223 * will have dropped the reference on the passed credential. All
4224 * callers should be aware of this, and treat this function as an
4225 * unref + ref, potentially on different credentials.
4227 * Because of this, the caller is expected to take its own
4228 * reference on the credential passed as the first parameter,
4229 * and be prepared to release the reference on the credential
4230 * that is returned to them, if it is not intended to be a
4231 * persistent reference.
4234 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4236 struct ucred temp_cred
;
4237 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4240 NULLCRED_CHECK(cred
);
4242 pcred
= posix_cred_get(cred
);
4244 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4247 * We don't need to do anything if the effective, real and saved
4248 * uids are already the same as the uid provided. This check is
4249 * likely insufficient.
4251 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4252 /* no change needed */
4255 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4257 /* look up in cred hash table to see if we have a matching credential
4260 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4261 temp_pcred
->cr_svuid
= uid
;
4262 temp_pcred
->cr_svgid
= gid
;
4264 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4269 * kauth_cred_setauditinfo
4271 * Description: Update the given credential using the given au_session_t.
4273 * Parameters: cred The original credential
4274 * auditinfo_p Pointer to ne audit information
4276 * Returns: (kauth_cred_t) The updated credential
4278 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4279 * if it returns a credential other than the one it is passed,
4280 * will have dropped the reference on the passed credential. All
4281 * callers should be aware of this, and treat this function as an
4282 * unref + ref, potentially on different credentials.
4284 * Because of this, the caller is expected to take its own
4285 * reference on the credential passed as the first parameter,
4286 * and be prepared to release the reference on the credential
4287 * that is returned to them, if it is not intended to be a
4288 * persistent reference.
4291 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4293 struct ucred temp_cred
;
4295 NULLCRED_CHECK(cred
);
4298 * We don't need to do anything if the audit info is already the
4299 * same as the audit info in the credential provided.
4301 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4302 /* no change needed */
4306 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4307 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4309 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
4314 * kauth_cred_label_update
4316 * Description: Update the MAC label associated with a credential
4318 * Parameters: cred The original credential
4319 * label The MAC label to set
4321 * Returns: (kauth_cred_t) The updated credential
4323 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4324 * if it returns a credential other than the one it is passed,
4325 * will have dropped the reference on the passed credential. All
4326 * callers should be aware of this, and treat this function as an
4327 * unref + ref, potentially on different credentials.
4329 * Because of this, the caller is expected to take its own
4330 * reference on the credential passed as the first parameter,
4331 * and be prepared to release the reference on the credential
4332 * that is returned to them, if it is not intended to be a
4333 * persistent reference.
4336 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4338 kauth_cred_t newcred
;
4339 struct ucred temp_cred
;
4341 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4343 mac_cred_label_init(&temp_cred
);
4344 mac_cred_label_associate(cred
, &temp_cred
);
4345 mac_cred_label_update(&temp_cred
, label
);
4347 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4348 mac_cred_label_destroy(&temp_cred
);
4353 * kauth_cred_label_update_execve
4355 * Description: Update the MAC label associated with a credential as
4358 * Parameters: cred The original credential
4360 * scriptl The script MAC label
4361 * execl The executable MAC label
4362 * disjointp Pointer to flag to set if old
4363 * and returned credentials are
4366 * Returns: (kauth_cred_t) The updated credential
4369 * *disjointp Set to 1 for disjoint creds
4371 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4372 * if it returns a credential other than the one it is passed,
4373 * will have dropped the reference on the passed credential. All
4374 * callers should be aware of this, and treat this function as an
4375 * unref + ref, potentially on different credentials.
4377 * Because of this, the caller is expected to take its own
4378 * reference on the credential passed as the first parameter,
4379 * and be prepared to release the reference on the credential
4380 * that is returned to them, if it is not intended to be a
4381 * persistent reference.
4385 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4386 struct vnode
*vp
, struct vnode
*scriptvp
, struct label
*scriptl
,
4387 struct label
*execl
, void *macextensions
, int *disjointp
)
4389 kauth_cred_t newcred
;
4390 struct ucred temp_cred
;
4392 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4394 mac_cred_label_init(&temp_cred
);
4395 mac_cred_label_associate(cred
, &temp_cred
);
4396 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
4397 vp
, scriptvp
, scriptl
, execl
,
4400 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4401 mac_cred_label_destroy(&temp_cred
);
4406 * kauth_proc_label_update
4408 * Description: Update the label inside the credential associated with the process.
4410 * Parameters: p The process to modify
4411 * label The label to place in the process credential
4413 * Notes: The credential associated with the process may change as a result
4414 * of this call. The caller should not assume the process reference to
4415 * the old credential still exists.
4417 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4419 kauth_cred_t my_cred
, my_new_cred
;
4421 my_cred
= kauth_cred_proc_ref(p
);
4423 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4425 /* get current credential and take a reference while we muck with it */
4429 * Set the credential with new info. If there is no change,
4430 * we get back the same credential we passed in; if there is
4431 * a change, we drop the reference on the credential we
4432 * passed in. The subsequent compare is safe, because it is
4433 * a pointer compare rather than a contents compare.
4435 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4436 if (my_cred
!= my_new_cred
) {
4438 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
);
4442 * We need to protect for a race where another thread
4443 * also changed the credential after we took our
4444 * reference. If p_ucred has changed then we should
4445 * restart this again with the new cred.
4447 if (p
->p_ucred
!= my_cred
) {
4449 kauth_cred_unref(&my_new_cred
);
4450 my_cred
= kauth_cred_proc_ref(p
);
4454 p
->p_ucred
= my_new_cred
;
4455 /* update cred on proc */
4456 PROC_UPDATE_CREDS_ONPROC(p
);
4458 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4463 /* Drop old proc reference or our extra reference */
4464 kauth_cred_unref(&my_cred
);
4470 * kauth_proc_label_update_execve
4472 * Description: Update the label inside the credential associated with the
4473 * process as part of a transitioning execve. The label will
4474 * be updated by the policies as part of this processing, not
4475 * provided up front.
4477 * Parameters: p The process to modify
4478 * ctx The context of the exec
4479 * vp The vnode being exec'ed
4480 * scriptl The script MAC label
4481 * execl The executable MAC label
4483 * Returns: 0 Label update did not make credential
4485 * 1 Label update caused credential to be
4488 * Notes: The credential associated with the process WILL change as a
4489 * result of this call. The caller should not assume the process
4490 * reference to the old credential still exists.
4493 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4494 struct vnode
*vp
, struct vnode
*scriptvp
, struct label
*scriptl
,
4495 struct label
*execl
, void *macextensions
)
4497 kauth_cred_t my_cred
, my_new_cred
;
4500 my_cred
= kauth_cred_proc_ref(p
);
4502 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4504 /* get current credential and take a reference while we muck with it */
4508 * Set the credential with new info. If there is no change,
4509 * we get back the same credential we passed in; if there is
4510 * a change, we drop the reference on the credential we
4511 * passed in. The subsequent compare is safe, because it is
4512 * a pointer compare rather than a contents compare.
4514 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptvp
, scriptl
, execl
, macextensions
, &disjoint
);
4515 if (my_cred
!= my_new_cred
) {
4517 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
);
4521 * We need to protect for a race where another thread
4522 * also changed the credential after we took our
4523 * reference. If p_ucred has changed then we should
4524 * restart this again with the new cred.
4526 if (p
->p_ucred
!= my_cred
) {
4528 kauth_cred_unref(&my_new_cred
);
4529 my_cred
= kauth_cred_proc_ref(p
);
4533 p
->p_ucred
= my_new_cred
;
4534 /* update cred on proc */
4535 PROC_UPDATE_CREDS_ONPROC(p
);
4536 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4541 /* Drop old proc reference or our extra reference */
4542 kauth_cred_unref(&my_cred
);
4549 * for temporary binary compatibility
4551 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4553 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4555 return kauth_cred_label_update(cred
, label
);
4558 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4560 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4562 return kauth_proc_label_update(p
, label
);
4568 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4569 * Since we cannot build our export lists based on the kernel configuration we need
4573 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4579 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4586 * for temporary binary compatibility
4588 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4590 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4595 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4597 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4607 * Description: Add a reference to the passed credential
4609 * Parameters: cred The credential to reference
4613 * Notes: This function adds a reference to the provided credential;
4614 * the existing reference on the credential is assumed to be
4615 * held stable over this operation by taking the appropriate
4616 * lock to protect the pointer from which it is being referenced,
4617 * if necessary (e.g. the proc lock is held over the call if the
4618 * credential being referenced is from p_ucred, the vnode lock
4619 * if from the per vnode name cache cred cache, and so on).
4621 * This is safe from the kauth_cred_unref() path, since an atomic
4622 * add is used, and the unref path specifically checks to see that
4623 * the value has not been changed to add a reference between the
4624 * time the credential is unreferenced by another pointer and the
4625 * time it is unreferenced from the cred hash cache.
4628 kauth_cred_ref(kauth_cred_t cred
)
4632 NULLCRED_CHECK(cred
);
4634 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4637 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4639 #if 0 // use this to watch a specific credential
4640 if ( is_target_cred( cred
) != 0 ) {
4650 * kauth_cred_unref_hashlocked
4652 * Description: release a credential reference; when the last reference is
4653 * released, the credential will be freed.
4655 * Parameters: credp Pointer to address containing
4656 * credential to be freed
4658 * Returns: TRUE if the credential must be destroyed by the caller.
4662 * *credp Set to NOCRED
4664 * Notes: This function assumes the credential hash lock is held.
4666 * This function is internal use only, since the hash lock is
4667 * scoped to this compilation unit.
4669 * This function destroys the contents of the pointer passed by
4670 * the caller to prevent the caller accidentally attempting to
4671 * release a given reference twice in error.
4673 * The last reference is considered to be released when a release
4674 * of a credential of a reference count of 2 occurs; this is an
4675 * intended effect, to take into account the reference held by
4676 * the credential hash, which is released at the same time.
4679 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4682 boolean_t destroy_it
= FALSE
;
4684 KAUTH_CRED_HASH_LOCK_ASSERT();
4685 NULLCRED_CHECK(*credp
);
4687 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4691 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4693 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4696 #if 0 // use this to watch a specific credential
4697 if ( is_target_cred( *credp
) != 0 ) {
4703 * If the old_value is 2, then we have just released the last external
4704 * reference to this credential
4706 if (old_value
< 3) {
4707 /* The last absolute reference is our credential hash table */
4708 destroy_it
= kauth_cred_remove(*credp
);
4711 if (destroy_it
== FALSE
) {
4715 return (destroy_it
);
4722 * Description: Release a credential reference while holding the credential
4723 * hash lock; when the last reference is released, the credential
4726 * Parameters: credp Pointer to address containing
4727 * credential to be freed
4732 * *credp Set to NOCRED
4734 * Notes: See kauth_cred_unref_hashlocked() for more information.
4738 kauth_cred_unref(kauth_cred_t
*credp
)
4740 boolean_t destroy_it
;
4742 KAUTH_CRED_HASH_LOCK();
4743 destroy_it
= kauth_cred_unref_hashlocked(credp
);
4744 KAUTH_CRED_HASH_UNLOCK();
4746 if (destroy_it
== TRUE
) {
4747 assert(*credp
!= NOCRED
);
4749 mac_cred_label_destroy(*credp
);
4751 AUDIT_SESSION_UNREF(*credp
);
4753 (*credp
)->cr_ref
= 0;
4754 FREE_ZONE(*credp
, sizeof(*(*credp
)), M_CRED
);
4764 * Description: release a credential reference; when the last reference is
4765 * released, the credential will be freed
4767 * Parameters: cred Credential to release
4771 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4772 * clear the pointer in the caller to avoid multiple releases of
4773 * the same credential. The currently recommended interface is
4774 * kauth_cred_unref().
4777 kauth_cred_rele(kauth_cred_t cred
)
4779 kauth_cred_unref(&cred
);
4781 #endif /* !__LP64__ */
4787 * Description: Duplicate a credential via alloc and copy; the new credential
4790 * Parameters: cred The credential to duplicate
4792 * Returns: (kauth_cred_t) The duplicate credential
4794 * Notes: The typical value to calling this routine is if you are going
4795 * to modify an existing credential, and expect to need a new one
4796 * from the hash cache.
4798 * This should probably not be used in the majority of cases;
4799 * if you are using it instead of kauth_cred_create(), you are
4800 * likely making a mistake.
4802 * The newly allocated credential is copied as part of the
4803 * allocation process, with the exception of the reference
4804 * count, which is set to 1 to indicate a single reference
4805 * held by the caller.
4807 * Since newly allocated credentials have no external pointers
4808 * referencing them, prior to making them visible in an externally
4809 * visible pointer (e.g. by adding them to the credential hash
4810 * cache) is the only legal time in which an existing credential
4811 * can be safely initialized or modified directly.
4813 * After initialization, the caller is expected to call the
4814 * function kauth_cred_add() to add the credential to the hash
4815 * cache, after which time it's frozen and becomes publicly
4818 * The release protocol depends on kauth_hash_add() being called
4819 * before kauth_cred_rele() (there is a diagnostic panic which
4820 * will trigger if this protocol is not observed).
4824 kauth_cred_dup(kauth_cred_t cred
)
4826 kauth_cred_t newcred
;
4828 struct label
*temp_label
;
4832 if (cred
== NOCRED
|| cred
== FSCRED
)
4833 panic("kauth_cred_dup: bad credential");
4835 newcred
= kauth_cred_alloc();
4836 if (newcred
!= NULL
) {
4838 temp_label
= newcred
->cr_label
;
4840 bcopy(cred
, newcred
, sizeof(*newcred
));
4842 newcred
->cr_label
= temp_label
;
4843 mac_cred_label_associate(cred
, newcred
);
4845 AUDIT_SESSION_REF(cred
);
4846 newcred
->cr_ref
= 1;
4852 * kauth_cred_copy_real
4854 * Description: Returns a credential based on the passed credential but which
4855 * reflects the real rather than effective UID and GID.
4857 * Parameters: cred The credential from which to
4858 * derive the new credential
4860 * Returns: (kauth_cred_t) The copied credential
4862 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4863 * result, the caller is responsible for dropping BOTH the
4864 * additional reference on the passed cred (if any), and the
4865 * credential returned by this function. The drop should be
4866 * via the kauth_cred_unref() KPI.
4869 kauth_cred_copy_real(kauth_cred_t cred
)
4871 kauth_cred_t newcred
= NULL
, found_cred
;
4872 struct ucred temp_cred
;
4873 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4874 posix_cred_t pcred
= posix_cred_get(cred
);
4876 /* if the credential is already 'real', just take a reference */
4877 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4878 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4879 kauth_cred_ref(cred
);
4884 * Look up in cred hash table to see if we have a matching credential
4885 * with the new values.
4887 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4888 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4889 /* displacing a supplementary group opts us out of memberd */
4890 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4891 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4892 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4895 * If the cred is not opted out, make sure we are using the r/euid
4898 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4899 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4904 KAUTH_CRED_HASH_LOCK();
4905 found_cred
= kauth_cred_find(&temp_cred
);
4906 if (found_cred
== cred
) {
4907 /* same cred so just bail */
4908 KAUTH_CRED_HASH_UNLOCK();
4911 if (found_cred
!= NULL
) {
4913 * Found a match so we bump reference count on new
4914 * one. We leave the old one alone.
4916 kauth_cred_ref(found_cred
);
4917 KAUTH_CRED_HASH_UNLOCK();
4922 * Must allocate a new credential, copy in old credential
4923 * data and update the real user and group IDs.
4925 newcred
= kauth_cred_dup(&temp_cred
);
4926 err
= kauth_cred_add(newcred
);
4927 KAUTH_CRED_HASH_UNLOCK();
4929 /* Retry if kauth_cred_add() fails */
4933 mac_cred_label_destroy(newcred
);
4935 AUDIT_SESSION_UNREF(newcred
);
4937 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4948 * Description: Common code to update a credential
4950 * Parameters: old_cred Reference counted credential
4952 * model_cred Non-reference counted model
4953 * credential to apply to the
4954 * credential to be updated
4955 * retain_auditinfo Flag as to whether or not the
4956 * audit information should be
4957 * copied from the old_cred into
4960 * Returns: (kauth_cred_t) The updated credential
4962 * IMPORTANT: This function will potentially return a credential other than
4963 * the one it is passed, and if so, it will have dropped the
4964 * reference on the passed credential. All callers should be
4965 * aware of this, and treat this function as an unref + ref,
4966 * potentially on different credentials.
4968 * Because of this, the caller is expected to take its own
4969 * reference on the credential passed as the first parameter,
4970 * and be prepared to release the reference on the credential
4971 * that is returned to them, if it is not intended to be a
4972 * persistent reference.
4975 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4976 boolean_t retain_auditinfo
)
4978 kauth_cred_t found_cred
, new_cred
= NULL
;
4981 * Make sure we carry the auditinfo forward to the new credential
4982 * unless we are actually updating the auditinfo.
4984 if (retain_auditinfo
) {
4985 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
4986 sizeof(model_cred
->cr_audit
));
4992 KAUTH_CRED_HASH_LOCK();
4993 found_cred
= kauth_cred_find(model_cred
);
4994 if (found_cred
== old_cred
) {
4995 /* same cred so just bail */
4996 KAUTH_CRED_HASH_UNLOCK();
4999 if (found_cred
!= NULL
) {
5000 boolean_t destroy_it
;
5002 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
5004 * Found a match so we bump reference count on new
5005 * one and decrement reference count on the old one.
5007 kauth_cred_ref(found_cred
);
5008 destroy_it
= kauth_cred_unref_hashlocked(&old_cred
);
5009 KAUTH_CRED_HASH_UNLOCK();
5010 if (destroy_it
== TRUE
) {
5011 assert(old_cred
!= NOCRED
);
5013 mac_cred_label_destroy(old_cred
);
5015 AUDIT_SESSION_UNREF(old_cred
);
5017 old_cred
->cr_ref
= 0;
5018 FREE_ZONE(old_cred
, sizeof(*old_cred
), M_CRED
);
5026 * Must allocate a new credential using the model. also
5027 * adds the new credential to the credential hash table.
5029 new_cred
= kauth_cred_dup(model_cred
);
5030 err
= kauth_cred_add(new_cred
);
5031 KAUTH_CRED_HASH_UNLOCK();
5033 /* retry if kauth_cred_add returns non zero value */
5037 mac_cred_label_destroy(new_cred
);
5039 AUDIT_SESSION_UNREF(new_cred
);
5041 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
5045 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
5046 kauth_cred_unref(&old_cred
);
5054 * Description: Add the given credential to our credential hash table and
5055 * take an additional reference to account for our use of the
5056 * credential in the hash table
5058 * Parameters: new_cred Credential to insert into cred
5061 * Returns: 0 Success
5062 * -1 Hash insertion failed: caller
5065 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5067 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5070 kauth_cred_add(kauth_cred_t new_cred
)
5074 KAUTH_CRED_HASH_LOCK_ASSERT();
5076 hash_key
= kauth_cred_get_hashkey(new_cred
);
5077 hash_key
%= kauth_cred_table_size
;
5079 /* race fix - there is a window where another matching credential
5080 * could have been inserted between the time this one was created and we
5081 * got the hash lock. If we find a match return an error and have the
5084 if (kauth_cred_find(new_cred
) != NULL
) {
5088 /* take a reference for our use in credential hash table */
5089 kauth_cred_ref(new_cred
);
5091 /* insert the credential into the hash table */
5092 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
5101 * Description: Remove the given credential from our credential hash table
5103 * Parameters: cred Credential to remove from cred
5106 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not.
5108 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5110 * Notes: The check for the reference increment after entry is generally
5111 * agree to be safe, since we use atomic operations, and the
5112 * following code occurs with the hash lock held; in theory, this
5113 * protects us from the 2->1 reference that gets us here.
5116 kauth_cred_remove(kauth_cred_t cred
)
5119 kauth_cred_t found_cred
;
5121 hash_key
= kauth_cred_get_hashkey(cred
);
5122 hash_key
%= kauth_cred_table_size
;
5125 if (cred
->cr_ref
< 1)
5126 panic("cred reference underflow");
5127 if (cred
->cr_ref
> 1)
5128 return (FALSE
); /* someone else got a ref */
5130 /* Find cred in the credential hash table */
5131 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5132 if (found_cred
== cred
) {
5133 /* found a match, remove it from the hash table */
5134 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
5135 #if KAUTH_CRED_HASH_DEBUG
5142 /* Did not find a match... this should not happen! XXX Make panic? */
5143 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
5151 * Description: Using the given credential data, look for a match in our
5152 * credential hash table
5154 * Parameters: cred Credential to lookup in cred
5157 * Returns: NULL Not found
5158 * !NULL Matching credential already in
5161 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5164 kauth_cred_find(kauth_cred_t cred
)
5167 kauth_cred_t found_cred
;
5168 posix_cred_t pcred
= posix_cred_get(cred
);
5170 KAUTH_CRED_HASH_LOCK_ASSERT();
5172 #if KAUTH_CRED_HASH_DEBUG
5173 static int test_count
= 0;
5176 if ((test_count
% 200) == 0) {
5177 kauth_cred_hash_print();
5181 hash_key
= kauth_cred_get_hashkey(cred
);
5182 hash_key
%= kauth_cred_table_size
;
5184 /* Find cred in the credential hash table */
5185 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5187 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
5190 * don't worry about the label unless the flags in
5191 * either credential tell us to.
5193 match
= (bcmp(found_pcred
, pcred
, sizeof (*pcred
)) == 0) ? TRUE
: FALSE
;
5194 match
= match
&& ((bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
5195 sizeof(cred
->cr_audit
)) == 0) ? TRUE
: FALSE
);
5197 if (((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) ||
5198 ((pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0)) {
5199 match
= match
&& mac_cred_label_compare(found_cred
->cr_label
,
5208 /* No match found */
5217 * Description: Generates a hash key using data that makes up a credential;
5220 * Parameters: datap Pointer to data to hash
5221 * data_len Count of bytes to hash
5222 * start_key Start key value
5224 * Returns: (u_long) Returned hash key
5226 static inline u_long
5227 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5229 u_long hash_key
= start_key
;
5232 while (data_len
> 0) {
5233 hash_key
= (hash_key
<< 4) + *datap
++;
5234 temp
= hash_key
& 0xF0000000;
5236 hash_key
^= temp
>> 24;
5246 * kauth_cred_get_hashkey
5248 * Description: Generate a hash key using data that makes up a credential;
5249 * based on ElfHash. We hash on the entire credential data,
5250 * not including the ref count or the TAILQ, which are mutable;
5251 * everything else isn't.
5253 * Parameters: cred Credential for which hash is
5256 * Returns: (u_long) Returned hash key
5258 * Notes: When actually moving the POSIX credential into a real label,
5259 * remember to update this hash computation.
5262 kauth_cred_get_hashkey(kauth_cred_t cred
)
5265 posix_cred_t pcred
= posix_cred_get(cred
);
5267 u_long hash_key
= 0;
5269 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5270 sizeof (struct posix_cred
),
5272 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5273 sizeof(struct au_session
),
5276 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5277 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5278 sizeof (struct label
),
5286 #if KAUTH_CRED_HASH_DEBUG
5288 * kauth_cred_hash_print
5290 * Description: Print out cred hash cache table information for debugging
5291 * purposes, including the credential contents
5293 * Parameters: (void)
5297 * Implicit returns: Results in console output
5300 kauth_cred_hash_print(void)
5303 kauth_cred_t found_cred
;
5305 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
5306 /* count slot hits, misses, collisions, and max depth */
5307 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5308 printf("[%02d] ", i
);
5310 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5315 kauth_cred_print(found_cred
);
5319 printf("NOCRED \n");
5323 #endif /* KAUTH_CRED_HASH_DEBUG */
5326 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5330 * Description: Print out an individual credential's contents for debugging
5333 * Parameters: cred The credential to print out
5337 * Implicit returns: Results in console output
5340 kauth_cred_print(kauth_cred_t cred
)
5344 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
);
5345 printf("group count %d gids ", cred
->cr_ngroups
);
5346 for (i
= 0; i
< NGROUPS
; i
++) {
5349 printf("%d ", cred
->cr_groups
[i
]);
5351 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5352 printf("auditinfo_addr %d %d %d %d %d %d\n",
5353 cred
->cr_audit
.s_aia_p
->ai_auid
,
5354 cred
->cr_audit
.as_mask
.am_success
,
5355 cred
->cr_audit
.as_mask
.am_failure
,
5356 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5357 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5358 cred
->cr_audit
.as_aia_p
->ai_asid
);
5361 int is_target_cred( kauth_cred_t the_cred
)
5363 if ( the_cred
->cr_uid
!= 0 )
5365 if ( the_cred
->cr_ruid
!= 0 )
5367 if ( the_cred
->cr_svuid
!= 0 )
5369 if ( the_cred
->cr_ngroups
!= 11 )
5371 if ( the_cred
->cr_groups
[0] != 11 )
5373 if ( the_cred
->cr_groups
[1] != 81 )
5375 if ( the_cred
->cr_groups
[2] != 63947 )
5377 if ( the_cred
->cr_groups
[3] != 80288 )
5379 if ( the_cred
->cr_groups
[4] != 89006 )
5381 if ( the_cred
->cr_groups
[5] != 52173 )
5383 if ( the_cred
->cr_groups
[6] != 84524 )
5385 if ( the_cred
->cr_groups
[7] != 79 )
5387 if ( the_cred
->cr_groups
[8] != 80292 )
5389 if ( the_cred
->cr_groups
[9] != 80 )
5391 if ( the_cred
->cr_groups
[10] != 90824 )
5393 if ( the_cred
->cr_rgid
!= 11 )
5395 if ( the_cred
->cr_svgid
!= 11 )
5397 if ( the_cred
->cr_gmuid
!= 3475 )
5399 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5402 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5404 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5406 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5408 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5410 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5412 if ( the_cred->cr_flags != 0 )
5415 return( -1 ); // found target cred
5418 void get_backtrace( void )
5421 void * my_stack
[ MAX_STACK_DEPTH
];
5424 if ( cred_debug_buf_p
== NULL
) {
5425 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5426 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5429 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5430 /* buffer is full */
5434 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5435 if ( my_depth
== 0 ) {
5436 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5440 /* fill new backtrace */
5441 my_slot
= cred_debug_buf_p
->next_slot
;
5442 cred_debug_buf_p
->next_slot
++;
5443 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5444 for ( i
= 0; i
< my_depth
; i
++ ) {
5445 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5452 /* subset of struct ucred for use in sysctl_dump_creds */
5453 struct debug_ucred
{
5455 u_long cr_ref
; /* reference count */
5456 uid_t cr_uid
; /* effective user id */
5457 uid_t cr_ruid
; /* real user id */
5458 uid_t cr_svuid
; /* saved user id */
5459 short cr_ngroups
; /* number of groups in advisory list */
5460 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5461 gid_t cr_rgid
; /* real group id */
5462 gid_t cr_svgid
; /* saved group id */
5463 uid_t cr_gmuid
; /* UID for group membership purposes */
5464 struct auditinfo_addr cr_audit
; /* user auditing data. */
5465 void *cr_label
; /* MACF label */
5466 int cr_flags
; /* flags on credential */
5468 typedef struct debug_ucred debug_ucred
;
5470 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5471 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5474 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5478 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5480 int i
, j
, counter
= 0;
5483 kauth_cred_t found_cred
;
5484 debug_ucred
* cred_listp
;
5485 debug_ucred
* nextp
;
5487 /* This is a readonly node. */
5488 if (req
->newptr
!= USER_ADDR_NULL
)
5491 /* calculate space needed */
5492 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5493 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5498 /* they are querying us so just return the space required. */
5499 if (req
->oldptr
== USER_ADDR_NULL
) {
5500 counter
+= 10; // add in some padding;
5501 req
->oldidx
= counter
* sizeof(debug_ucred
);
5505 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5506 if ( cred_listp
== NULL
) {
5510 /* fill in creds to send back */
5513 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5514 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5515 nextp
->credp
= found_cred
;
5516 nextp
->cr_ref
= found_cred
->cr_ref
;
5517 nextp
->cr_uid
= found_cred
->cr_uid
;
5518 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5519 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5520 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5521 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5522 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5524 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5525 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5526 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5527 nextp
->cr_audit
.ai_auid
=
5528 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5529 nextp
->cr_audit
.ai_mask
.am_success
=
5530 found_cred
->cr_audit
.as_mask
.am_success
;
5531 nextp
->cr_audit
.ai_mask
.am_failure
=
5532 found_cred
->cr_audit
.as_mask
.am_failure
;
5533 nextp
->cr_audit
.ai_termid
.at_port
=
5534 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5535 nextp
->cr_audit
.ai_termid
.at_type
=
5536 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5537 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5538 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5539 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5540 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5541 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5542 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5543 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5544 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5545 nextp
->cr_audit
.ai_asid
=
5546 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5547 nextp
->cr_audit
.ai_flags
=
5548 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5549 nextp
->cr_label
= found_cred
->cr_label
;
5550 nextp
->cr_flags
= found_cred
->cr_flags
;
5552 space
+= sizeof(debug_ucred
);
5553 if ( space
> req
->oldlen
) {
5554 FREE(cred_listp
, M_TEMP
);
5559 req
->oldlen
= space
;
5560 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5561 FREE(cred_listp
, M_TEMP
);
5566 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5567 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5570 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5574 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5579 cred_debug_buffer
* bt_bufp
;
5580 cred_backtrace
* nextp
;
5582 /* This is a readonly node. */
5583 if (req
->newptr
!= USER_ADDR_NULL
)
5586 if ( cred_debug_buf_p
== NULL
) {
5590 /* calculate space needed */
5591 space
= sizeof( cred_debug_buf_p
->next_slot
);
5592 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5594 /* they are querying us so just return the space required. */
5595 if (req
->oldptr
== USER_ADDR_NULL
) {
5596 req
->oldidx
= space
;
5600 if ( space
> req
->oldlen
) {
5604 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5605 if ( bt_bufp
== NULL
) {
5609 /* fill in backtrace info to send back */
5610 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5611 space
= sizeof(bt_bufp
->next_slot
);
5613 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5614 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5615 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5616 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5617 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5619 space
+= sizeof(*nextp
);
5622 req
->oldlen
= space
;
5623 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5624 FREE(bt_bufp
, M_TEMP
);
5628 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5632 **********************************************************************
5633 * The following routines will be moved to a policy_posix.c module at
5634 * some future point.
5635 **********************************************************************
5641 * Description: Helper function to create a kauth_cred_t credential that is
5642 * initally labelled with a specific POSIX credential label
5644 * Parameters: pcred The posix_cred_t to use as the initial
5647 * Returns: (kauth_cred_t) The credential that was found in the
5649 * NULL kauth_cred_add() failed, or there was
5650 * no egid specified, or we failed to
5651 * attach a label to the new credential
5653 * Notes: This function currently wraps kauth_cred_create(), and is the
5654 * only consumer of that ill-fated function, apart from bsd_init().
5655 * It exists solely to support the NFS server code creation of
5656 * credentials based on the over-the-wire RPC calls containing
5657 * traditional POSIX credential information being tunneled to
5658 * the server host from the client machine.
5660 * In the future, we hope this function goes away.
5662 * In the short term, it creates a temporary credential, puts
5663 * the POSIX information from NFS into it, and then calls
5664 * kauth_cred_create(), as an internal implementation detail.
5666 * If we have to keep it around in the medium term, it will
5667 * create a new kauth_cred_t, then label it with a POSIX label
5668 * corresponding to the contents of the kauth_cred_t. If the
5669 * policy_posix MACF module is not loaded, it will instead
5670 * substitute a posix_cred_t which GRANTS all access (effectively
5671 * a "root" credential) in order to not prevent NFS from working
5672 * in the case that we are not supporting POSIX credentials.
5675 posix_cred_create(posix_cred_t pcred
)
5677 struct ucred temp_cred
;
5679 bzero(&temp_cred
, sizeof(temp_cred
));
5680 temp_cred
.cr_posix
= *pcred
;
5682 return kauth_cred_create(&temp_cred
);
5689 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5690 * any, which is associated with it.
5692 * Parameters: cred The credential to obtain the label from
5694 * Returns: posix_cred_t The POSIX credential label
5696 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5697 * this function will return a pointer to a posix_cred_t which
5698 * GRANTS all access (effectively, a "root" credential). This is
5699 * necessary to support legacy code which insists on tightly
5700 * integrating POSIX credentials into its APIs, including, but
5701 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5702 * NFSv3, signals, dtrace, and a large number of kauth routines
5703 * used to implement POSIX permissions related system calls.
5705 * In the event that the policy_posix MACF module IS loaded, and
5706 * there is no POSIX label on the kauth_cred_t credential, this
5707 * function will return a pointer to a posix_cred_t which DENIES
5708 * all access (effectively, a "deny rights granted by POSIX"
5709 * credential). This is necessary to support the concept of a
5710 * transiently loaded POSIX policy, or kauth_cred_t credentials
5711 * which can not be used in conjunctions with POSIX permissions
5714 * This function currently returns the address of the cr_posix
5715 * field of the supplied kauth_cred_t credential, and as such
5716 * currently can not fail. In the future, this will not be the
5720 posix_cred_get(kauth_cred_t cred
)
5722 return(&cred
->cr_posix
);
5729 * Description: Label a kauth_cred_t with a POSIX credential label
5731 * Parameters: cred The credential to label
5732 * pcred The POSIX credential t label it with
5736 * Notes: This function is currently void in order to permit it to fit
5737 * in with the current MACF framework label methods which allow
5738 * labeling to fail silently. This is like acceptable for
5739 * mandatory access controls, but not for POSIX, since those
5740 * access controls are advisory. We will need to consider a
5741 * return value in a future version of the MACF API.
5743 * This operation currently cannot fail, as currently the POSIX
5744 * credential is a subfield of the kauth_cred_t (ucred), which
5745 * MUST be valid. In the future, this will not be the case.
5748 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5750 cred
->cr_posix
= *pcred
; /* structure assign for now */
5757 * Description: Perform a POSIX access check for a protected object
5759 * Parameters: cred The credential to check
5760 * object_uid The POSIX UID of the protected object
5761 * object_gid The POSIX GID of the protected object
5762 * object_mode The POSIX mode of the protected object
5763 * mode_req The requested POSIX access rights
5765 * Returns 0 Access is granted
5766 * EACCES Access is denied
5768 * Notes: This code optimizes the case where the world and group rights
5769 * would both grant the requested rights to avoid making a group
5770 * membership query. This is a big performance win in the case
5771 * where this is true.
5774 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5777 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5778 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5779 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5782 * Check first for owner rights
5784 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5788 * Combined group and world rights check, if we don't have owner rights
5790 * OPTIMIZED: If group and world rights would grant the same bits, and
5791 * they set of requested bits is in both, then we can simply check the
5792 * world rights, avoiding a group membership check, which is expensive.
5794 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5798 * NON-OPTIMIZED: requires group membership check.
5800 if ((mode_req
& mode_group
) != mode_req
) {
5802 * exclusion group : treat errors as "is a member"
5804 * NON-OPTIMIZED: +group would deny; must check group
5806 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5808 * DENY: +group denies
5812 if ((mode_req
& mode_world
) != mode_req
) {
5814 * DENY: both -group & world would deny
5819 * ALLOW: allowed by -group and +world
5826 * inclusion group; treat errors as "not a member"
5828 * NON-OPTIMIZED: +group allows, world denies; must
5831 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5833 * ALLOW: allowed by +group
5837 if ((mode_req
& mode_world
) != mode_req
) {
5839 * DENY: both -group & world would deny
5844 * ALLOW: allowed by -group and +world