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/locks.h>
68 #define MACH_ASSERT 1 /* XXX so bogus */
69 #include <kern/assert.h>
72 #include <security/mac.h>
73 #include <security/mac_framework.h>
74 #include <security/_label.h>
77 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. We verify this
623 * by confirming the resolver credential and pid.
625 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
626 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
630 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
631 KAUTH_IDENTITY_LOCK();
632 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
633 KAUTH_IDENTITY_UNLOCK();
636 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
637 KAUTH_GROUPS_UNLOCK();
639 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof (sz_arg
))) != 0) {
644 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
645 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof (sz_arg
))) != 0) {
649 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
650 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
654 KAUTH_IDENTITY_LOCK();
655 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
656 kauth_identity_trimcache(kauth_identity_cachemax
);
657 KAUTH_IDENTITY_UNLOCK();
660 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
661 kauth_groups_trimcache(kauth_groups_cachemax
);
662 KAUTH_GROUPS_UNLOCK();
665 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
666 KAUTH_IDENTITY_LOCK();
667 kauth_identity_trimcache(0);
668 KAUTH_IDENTITY_UNLOCK();
671 kauth_groups_trimcache(0);
672 KAUTH_GROUPS_UNLOCK();
673 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
675 * Terminate outstanding requests; without an authoritative
676 * resolver, we are now back on our own authority.
678 struct kauth_resolver_work
*killp
;
680 KAUTH_RESOLVER_LOCK();
683 * Clear the identity, but also mark it as unregistered so
684 * there is no explicit future expectation of us getting a
685 * new resolver any time soon.
687 kauth_resolver_identity
= 0;
688 kauth_resolver_registered
= 0;
690 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
692 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
694 /* Cause all waiting-for-work threads to return EIO */
695 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
696 KAUTH_RESOLVER_UNLOCK();
700 * Got a result returning?
702 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
703 if ((error
= kauth_resolver_complete(message
)) != 0)
708 * Caller wants to take more work?
710 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
711 if ((error
= kauth_resolver_getwork(message
)) != 0)
720 * kauth_resolver_getwork_continue
722 * Description: Continuation for kauth_resolver_getwork
724 * Parameters: result Error code or 0 for the sleep
725 * that got us to this function
728 * EINTR Interrupted (e.g. by signal)
729 * kauth_resolver_getwork2:EFAULT
731 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
735 kauth_resolver_getwork_continue(int result
)
742 KAUTH_RESOLVER_UNLOCK();
747 * If we lost a race with another thread/memberd restarting, then we
748 * need to go back to sleep to look for more work. If it was memberd
749 * restarting, then the msleep0() will error out here, as our thread
750 * will already be "dead".
752 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
755 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
757 * If this is a wakeup from another thread in the resolver
758 * deregistering it, error out the request-for-work thread
760 if (!kauth_resolver_identity
)
762 KAUTH_RESOLVER_UNLOCK();
766 thread
= current_thread();
767 ut
= get_bsdthread_info(thread
);
768 message
= ut
->uu_kevent
.uu_kauth
.message
;
769 return(kauth_resolver_getwork2(message
));
774 * kauth_resolver_getwork2
776 * Decription: Common utility function to copy out a identity resolver work
777 * item from the kernel to user space as part of the user space
778 * identity resolver requesting work.
780 * Parameters: message message to user space
783 * EFAULT Bad user space message address
785 * Notes: This common function exists to permit the use of continuations
786 * in the identity resolution process. This frees up the stack
787 * while we are waiting for the user space resolver to complete
788 * a request. This is specifically used so that our per thread
789 * cost can be small, and we will therefore be willing to run a
790 * larger number of threads in the user space identity resolver.
793 kauth_resolver_getwork2(user_addr_t message
)
795 struct kauth_resolver_work
*workp
;
799 * Note: We depend on the caller protecting us from a NULL work item
800 * queue, since we must have the kauth resolver lock on entry to this
803 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
806 * Copy out the external lookup structure for the request, not
807 * including the el_extend field, which contains the address of the
808 * external buffer provided by the external resolver into which we
809 * copy the extension request information.
812 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
813 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
817 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
818 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
819 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
820 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
825 * Handle extended requests here; if we have a request of a type where
826 * the kernel wants a translation of extended information, then we need
827 * to copy it out into the extended buffer, assuming the buffer is
828 * valid; we only attempt to get the buffer address if we have request
829 * data to copy into it.
833 * translate a user@domain string into a uid/gid/whatever
835 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
838 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
840 size_t actual
; /* not used */
842 * Use copyoutstr() to reduce the copy size; we let
843 * this catch a NULL uaddr because we shouldn't be
844 * asking in that case anyway.
846 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
849 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
853 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
854 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
855 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
856 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
859 KAUTH_RESOLVER_UNLOCK();
865 * kauth_resolver_getwork
867 * Description: Get a work item from the enqueued requests from the kernel and
868 * give it to the user space daemon.
870 * Parameters: message message to user space
873 * EINTR Interrupted (e.g. by signal)
874 * kauth_resolver_getwork2:EFAULT
876 * Notes: This function blocks in a continuation if there are no work
877 * items available for processing at the time the user space
878 * identity resolution daemon makes a request for work. This
879 * permits a large number of threads to be used by the daemon,
880 * without using a lot of wired kernel memory when there are no
881 * actual request outstanding.
884 kauth_resolver_getwork(user_addr_t message
)
886 struct kauth_resolver_work
*workp
;
889 KAUTH_RESOLVER_LOCK();
891 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
892 thread_t thread
= current_thread();
893 struct uthread
*ut
= get_bsdthread_info(thread
);
895 ut
->uu_kevent
.uu_kauth
.message
= message
;
896 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
897 KAUTH_RESOLVER_UNLOCK();
899 * If this is a wakeup from another thread in the resolver
900 * deregistering it, error out the request-for-work thread
902 if (!kauth_resolver_identity
)
906 return kauth_resolver_getwork2(message
);
911 * kauth_resolver_complete
913 * Description: Return a result from userspace.
915 * Parameters: message message from user space
918 * EIO The resolver is dead
919 * copyin:EFAULT Bad message from user space
922 kauth_resolver_complete(user_addr_t message
)
924 struct kauth_identity_extlookup extl
;
925 struct kauth_resolver_work
*workp
;
926 struct kauth_resolver_work
*killp
;
927 int error
, result
, request_flags
;
930 * Copy in the mesage, including the extension field, since we are
931 * copying into a local variable.
933 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
934 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
938 KAUTH_RESOLVER_LOCK();
942 switch (extl
.el_result
) {
943 case KAUTH_EXTLOOKUP_INPROG
:
947 /* XXX this should go away once memberd is updated */
949 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
955 case KAUTH_EXTLOOKUP_SUCCESS
:
958 case KAUTH_EXTLOOKUP_FATAL
:
959 /* fatal error means the resolver is dead */
960 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
962 * Terminate outstanding requests; without an authoritative
963 * resolver, we are now back on our own authority. Tag the
964 * resolver unregistered to prevent kauth_cred_ismember_gid()
965 * enqueueing more work until a new one is registered. This
966 * mitigates the damage a crashing resolver may inflict.
968 kauth_resolver_identity
= 0;
969 kauth_resolver_registered
= 0;
971 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
973 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
975 /* Cause all waiting-for-work threads to return EIO */
976 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
977 /* and return EIO to the caller */
981 case KAUTH_EXTLOOKUP_BADRQ
:
982 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
986 case KAUTH_EXTLOOKUP_FAILURE
:
987 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
992 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
998 * In the case of a fatal error, we assume that the resolver will
999 * restart quickly and re-collect all of the outstanding requests.
1000 * Thus, we don't complete the request which returned the fatal
1003 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1004 /* scan our list for this request */
1005 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1007 if (workp
->kr_seqno
== extl
.el_seqno
) {
1009 * Take a snapshot of the original request flags.
1011 request_flags
= workp
->kr_work
.el_flags
;
1014 * Get the request of the submitted queue so
1015 * that it is not cleaned up out from under
1018 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1019 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1020 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1021 workp
->kr_result
= result
;
1023 /* Copy the result message to the work item. */
1024 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1027 * Check if we have a result in the extension
1028 * field; if we do, then we need to separately
1029 * copy the data from the message el_extend
1030 * into the request buffer that's in the work
1031 * item. We have to do it here because we do
1032 * not want to wake up the waiter until the
1033 * data is in their buffer, and because the
1034 * actual request response may be destroyed
1035 * by the time the requester wakes up, and they
1036 * do not have access to the user space buffer
1039 * It is safe to drop and reacquire the lock
1040 * here because we've already removed the item
1041 * from the submission queue, but have not yet
1042 * moved it to the completion queue. Note that
1043 * near simultaneous requests may result in
1044 * duplication of requests for items in this
1045 * window. This should not be a performance
1046 * issue and is easily detectable by comparing
1047 * time to live on last response vs. time of
1048 * next request in the resolver logs.
1050 * A malicious/faulty resolver could overwrite
1051 * part of a user's address space if they return
1052 * flags that mismatch the original request's flags.
1054 if ((extl
.el_flags
& request_flags
) & (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1055 size_t actual
; /* notused */
1057 KAUTH_RESOLVER_UNLOCK();
1058 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1059 KAUTH_RESOLVER_LOCK();
1060 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1062 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1063 extl
.el_flags
, request_flags
);
1067 * Move the completed work item to the
1068 * completion queue and wake up requester(s)
1070 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1077 * Note that it's OK for us not to find anything; if the request has
1078 * timed out the work record will be gone.
1080 KAUTH_RESOLVER_UNLOCK();
1084 #endif /* CONFIG_EXT_RESOLVER */
1091 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1092 #define KI_VALID_GID (1<<1)
1093 #define KI_VALID_GUID (1<<2)
1094 #define KI_VALID_NTSID (1<<3)
1095 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1096 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1097 #define KI_VALID_GROUPS (1<<6)
1099 #if CONFIG_EXT_RESOLVER
1101 * kauth_identity_init
1103 * Description: Initialize the kernel side of the credential identity resolver
1105 * Parameters: (void)
1109 * Notes: Initialize the credential identity resolver for use; the
1110 * credential identity resolver is the KPI used to communicate
1111 * with a user space credential identity resolver daemon.
1113 * This function is called from kauth_init() in the file
1114 * kern_authorization.c.
1117 kauth_identity_init(void)
1119 TAILQ_INIT(&kauth_identities
);
1120 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1125 * kauth_identity_alloc
1127 * Description: Allocate and fill out a kauth_identity structure for
1128 * translation between {UID|GID}/GUID/NTSID
1132 * Returns: NULL Insufficient memory to satisfy
1133 * the request or bad parameters
1134 * !NULL A pointer to the allocated
1135 * structure, filled in
1137 * Notes: It is illegal to translate between UID and GID; any given UUID
1138 * or NTSID can only refer to an NTSID or UUID (respectively),
1139 * and *either* a UID *or* a GID, but not both.
1141 static struct kauth_identity
*
1142 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1143 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1144 const char *name
, int nametype
)
1146 struct kauth_identity
*kip
;
1148 /* get and fill in a new identity */
1149 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1151 if (gid
!= KAUTH_GID_NONE
) {
1153 kip
->ki_valid
= KI_VALID_GID
;
1155 if (uid
!= KAUTH_UID_NONE
) {
1156 if (kip
->ki_valid
& KI_VALID_GID
)
1157 panic("can't allocate kauth identity with both uid and gid");
1159 kip
->ki_valid
= KI_VALID_UID
;
1163 * A malicious/faulty resolver could return bad values
1165 assert(supgrpcnt
>= 0);
1166 assert(supgrpcnt
<= NGROUPS
);
1167 assert(supgrps
!= NULL
);
1169 if ((supgrpcnt
< 0) || (supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1172 if (kip
->ki_valid
& KI_VALID_GID
)
1173 panic("can't allocate kauth identity with both gid and supplementary groups");
1174 kip
->ki_supgrpcnt
= supgrpcnt
;
1175 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1176 kip
->ki_valid
|= KI_VALID_GROUPS
;
1178 kip
->ki_groups_expiry
= groups_expiry
;
1179 if (guidp
!= NULL
) {
1180 kip
->ki_guid
= *guidp
;
1181 kip
->ki_valid
|= KI_VALID_GUID
;
1183 kip
->ki_guid_expiry
= guid_expiry
;
1184 if (ntsidp
!= NULL
) {
1185 kip
->ki_ntsid
= *ntsidp
;
1186 kip
->ki_valid
|= KI_VALID_NTSID
;
1188 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1190 kip
->ki_name
= name
;
1191 kip
->ki_valid
|= nametype
;
1199 * kauth_identity_register_and_free
1201 * Description: Register an association between identity tokens. The passed
1202 * 'kip' is consumed by this function.
1204 * Parameters: kip Pointer to kauth_identity
1205 * structure to register
1209 * Notes: The memory pointer to by 'kip' is assumed to have been
1210 * previously allocated via kauth_identity_alloc().
1213 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1215 struct kauth_identity
*ip
;
1218 * We search the cache for the UID listed in the incoming association.
1219 * If we already have an entry, the new information is merged.
1222 KAUTH_IDENTITY_LOCK();
1223 if (kip
->ki_valid
& KI_VALID_UID
) {
1224 if (kip
->ki_valid
& KI_VALID_GID
)
1225 panic("kauth_identity: can't insert record with both UID and GID as key");
1226 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1227 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1229 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1230 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1231 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1234 panic("kauth_identity: can't insert record without UID or GID as key");
1238 /* we already have an entry, merge/overwrite */
1239 if (kip
->ki_valid
& KI_VALID_GUID
) {
1240 ip
->ki_guid
= kip
->ki_guid
;
1241 ip
->ki_valid
|= KI_VALID_GUID
;
1243 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1244 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1245 ip
->ki_ntsid
= kip
->ki_ntsid
;
1246 ip
->ki_valid
|= KI_VALID_NTSID
;
1248 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1249 /* a valid ki_name field overwrites the previous name field */
1250 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1251 /* if there's an old one, discard it */
1252 const char *oname
= NULL
;
1253 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1254 oname
= ip
->ki_name
;
1255 ip
->ki_name
= kip
->ki_name
;
1256 kip
->ki_name
= oname
;
1258 /* and discard the incoming entry */
1262 * if we don't have any information on this identity, add it;
1263 * if it pushes us over our limit, discard the oldest one.
1265 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1266 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1267 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1268 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1269 kauth_identity_count
--;
1272 KAUTH_IDENTITY_UNLOCK();
1273 /* have to drop lock before freeing expired entry (it may be in use) */
1275 /* if the ki_name field is used, clear it first */
1276 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1277 vfs_removename(ip
->ki_name
);
1278 /* free the expired entry */
1285 * kauth_identity_updatecache
1287 * Description: Given a lookup result, add any associations that we don't
1288 * currently have; replace ones which have changed.
1290 * Parameters: elp External lookup result from
1291 * user space daemon to kernel
1292 * rkip pointer to returned kauth
1294 * extend_data Extended data (can vary)
1299 * *rkip Modified (if non-NULL)
1301 * Notes: For extended information requests, this code relies on the fact
1302 * that elp->el_flags is never used as an rvalue, and is only
1303 * ever bit-tested for valid lookup information we are willing
1306 * XXX: We may have to do the same in the case that extended data was
1307 * passed out to user space to ensure that the request string
1308 * gets cached; we may also be able to use the rkip as an
1309 * input to avoid this. The jury is still out.
1311 * XXX: This codes performance could be improved for multiple valid
1312 * results by combining the loop iteration in a single loop.
1315 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1318 struct kauth_identity
*kip
;
1319 const char *speculative_name
= NULL
;
1324 * If there is extended data, and that data represents a name rather
1325 * than something else, speculatively create an entry for it in the
1326 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1327 * over the allocation later.
1329 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1330 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1331 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1334 /* user identity? */
1335 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1336 KAUTH_IDENTITY_LOCK();
1337 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1338 /* matching record */
1339 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1340 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1341 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1342 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1343 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1344 kip
->ki_valid
|= KI_VALID_GROUPS
;
1345 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1347 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1348 kip
->ki_guid
= elp
->el_uguid
;
1349 kip
->ki_valid
|= KI_VALID_GUID
;
1351 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1352 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1353 kip
->ki_ntsid
= elp
->el_usid
;
1354 kip
->ki_valid
|= KI_VALID_NTSID
;
1356 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1357 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1358 const char *oname
= kip
->ki_name
;
1359 kip
->ki_name
= speculative_name
;
1360 speculative_name
= NULL
;
1361 kip
->ki_valid
|= KI_VALID_PWNAM
;
1364 * free oname (if any) outside
1367 speculative_name
= oname
;
1370 kauth_identity_lru(kip
);
1373 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1377 KAUTH_IDENTITY_UNLOCK();
1378 /* not found in cache, add new record */
1380 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1381 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1382 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1383 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1384 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1385 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1386 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1387 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1388 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1393 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1394 speculative_name
= NULL
;
1395 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1396 kauth_identity_register_and_free(kip
);
1401 /* group identity? (ignore, if we already processed it as a user) */
1402 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1403 KAUTH_IDENTITY_LOCK();
1404 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1405 /* matching record */
1406 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1407 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1408 kip
->ki_guid
= elp
->el_gguid
;
1409 kip
->ki_valid
|= KI_VALID_GUID
;
1411 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1412 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1413 kip
->ki_ntsid
= elp
->el_gsid
;
1414 kip
->ki_valid
|= KI_VALID_NTSID
;
1416 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1417 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1418 const char *oname
= kip
->ki_name
;
1419 kip
->ki_name
= speculative_name
;
1420 speculative_name
= NULL
;
1421 kip
->ki_valid
|= KI_VALID_GRNAM
;
1424 * free oname (if any) outside
1427 speculative_name
= oname
;
1430 kauth_identity_lru(kip
);
1433 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1437 KAUTH_IDENTITY_UNLOCK();
1438 /* not found in cache, add new record */
1440 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1441 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1442 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1443 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1444 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1445 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1446 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1447 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1448 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1453 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1454 speculative_name
= NULL
;
1455 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1456 kauth_identity_register_and_free(kip
);
1461 /* If we have a name reference to drop, drop it here */
1462 if (speculative_name
!= NULL
) {
1463 vfs_removename(speculative_name
);
1469 * Trim older entries from the identity cache.
1471 * Must be called with the identity cache lock held.
1474 kauth_identity_trimcache(int newsize
) {
1475 struct kauth_identity
*kip
;
1477 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1479 while (kauth_identity_count
> newsize
) {
1480 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1481 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1482 kauth_identity_count
--;
1488 * kauth_identity_lru
1490 * Description: Promote the entry to the head of the LRU, assumes the cache
1493 * Parameters: kip kauth identity to move to the
1494 * head of the LRU list, if it's
1499 * Notes: This is called even if the entry has expired; typically an
1500 * expired entry that's been looked up is about to be revalidated,
1501 * and having it closer to the head of the LRU means finding it
1502 * quickly again when the revalidation comes through.
1505 kauth_identity_lru(struct kauth_identity
*kip
)
1507 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1508 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1509 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1515 * kauth_identity_guid_expired
1517 * Description: Handle lazy expiration of GUID translations.
1519 * Parameters: kip kauth identity to check for
1522 * Returns: 1 Expired
1526 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1531 * Expiration time of 0 means this entry is persistent.
1533 if (kip
->ki_guid_expiry
== 0)
1537 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1539 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1544 * kauth_identity_ntsid_expired
1546 * Description: Handle lazy expiration of NTSID translations.
1548 * Parameters: kip kauth identity to check for
1551 * Returns: 1 Expired
1555 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1560 * Expiration time of 0 means this entry is persistent.
1562 if (kip
->ki_ntsid_expiry
== 0)
1566 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1568 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1572 * kauth_identity_groups_expired
1574 * Description: Handle lazy expiration of supplemental group translations.
1576 * Parameters: kip kauth identity to check for
1579 * Returns: 1 Expired
1583 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1588 * Expiration time of 0 means this entry is persistent.
1590 if (kip
->ki_groups_expiry
== 0)
1594 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1596 return((kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0);
1600 * kauth_identity_find_uid
1602 * Description: Search for an entry by UID
1604 * Parameters: uid UID to find
1605 * kir Pointer to return area
1606 * getname Name buffer, if ki_name wanted
1612 * *klr Modified, if found
1615 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1617 struct kauth_identity
*kip
;
1619 KAUTH_IDENTITY_LOCK();
1620 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1621 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1622 kauth_identity_lru(kip
);
1623 /* Copy via structure assignment */
1625 /* If a name is wanted and one exists, copy it out */
1626 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1627 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1631 KAUTH_IDENTITY_UNLOCK();
1632 return((kip
== NULL
) ? ENOENT
: 0);
1637 * kauth_identity_find_gid
1639 * Description: Search for an entry by GID
1641 * Parameters: gid GID to find
1642 * kir Pointer to return area
1643 * getname Name buffer, if ki_name wanted
1649 * *klr Modified, if found
1652 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1654 struct kauth_identity
*kip
;
1656 KAUTH_IDENTITY_LOCK();
1657 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1658 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1659 kauth_identity_lru(kip
);
1660 /* Copy via structure assignment */
1662 /* If a name is wanted and one exists, copy it out */
1663 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1664 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1668 KAUTH_IDENTITY_UNLOCK();
1669 return((kip
== NULL
) ? ENOENT
: 0);
1674 * kauth_identity_find_guid
1676 * Description: Search for an entry by GUID
1678 * Parameters: guidp Pointer to GUID to find
1679 * kir Pointer to return area
1680 * getname Name buffer, if ki_name wanted
1686 * *klr Modified, if found
1688 * Note: The association may be expired, in which case the caller
1689 * may elect to call out to userland to revalidate.
1692 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1694 struct kauth_identity
*kip
;
1696 KAUTH_IDENTITY_LOCK();
1697 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1698 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1699 kauth_identity_lru(kip
);
1700 /* Copy via structure assignment */
1702 /* If a name is wanted and one exists, copy it out */
1703 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1704 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1708 KAUTH_IDENTITY_UNLOCK();
1709 return((kip
== NULL
) ? ENOENT
: 0);
1713 * kauth_identity_find_nam
1715 * Description: Search for an entry by name
1717 * Parameters: name Pointer to name to find
1718 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1719 * kir Pointer to return area
1725 * *klr Modified, if found
1728 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1730 struct kauth_identity
*kip
;
1732 KAUTH_IDENTITY_LOCK();
1733 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1734 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1735 kauth_identity_lru(kip
);
1736 /* Copy via structure assignment */
1741 KAUTH_IDENTITY_UNLOCK();
1742 return((kip
== NULL
) ? ENOENT
: 0);
1747 * kauth_identity_find_ntsid
1749 * Description: Search for an entry by NTSID
1751 * Parameters: ntsid Pointer to NTSID to find
1752 * kir Pointer to return area
1753 * getname Name buffer, if ki_name wanted
1759 * *klr Modified, if found
1761 * Note: The association may be expired, in which case the caller
1762 * may elect to call out to userland to revalidate.
1765 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1767 struct kauth_identity
*kip
;
1769 KAUTH_IDENTITY_LOCK();
1770 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1771 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1772 kauth_identity_lru(kip
);
1773 /* Copy via structure assignment */
1775 /* If a name is wanted and one exists, copy it out */
1776 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1777 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1781 KAUTH_IDENTITY_UNLOCK();
1782 return((kip
== NULL
) ? ENOENT
: 0);
1784 #endif /* CONFIG_EXT_RESOLVER */
1790 guid_t kauth_null_guid
;
1796 * Description: Determine the equality of two GUIDs
1798 * Parameters: guid1 Pointer to first GUID
1799 * guid2 Pointer to second GUID
1801 * Returns: 0 If GUIDs are unequal
1802 * !0 If GUIDs are equal
1805 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1807 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1812 * kauth_wellknown_guid
1814 * Description: Determine if a GUID is a well-known GUID
1816 * Parameters: guid Pointer to GUID to check
1818 * Returns: KAUTH_WKG_NOT Not a well known GUID
1819 * KAUTH_WKG_EVERYBODY "Everybody"
1820 * KAUTH_WKG_NOBODY "Nobody"
1821 * KAUTH_WKG_OWNER "Other"
1822 * KAUTH_WKG_GROUP "Group"
1825 kauth_wellknown_guid(guid_t
*guid
)
1827 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1830 * All WKGs begin with the same 12 bytes.
1832 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1834 * The final 4 bytes are our code (in network byte order).
1836 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1839 return(KAUTH_WKG_EVERYBODY
);
1841 return(KAUTH_WKG_NOBODY
);
1843 return(KAUTH_WKG_OWNER
);
1845 return(KAUTH_WKG_GROUP
);
1848 return(KAUTH_WKG_NOT
);
1855 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1857 * Parameters: sid1 Pointer to first NTSID
1858 * sid2 Pointer to second NTSID
1860 * Returns: 0 If GUIDs are unequal
1861 * !0 If GUIDs are equal
1864 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1866 /* check sizes for equality, also sanity-check size while we're at it */
1867 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1868 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1869 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1878 * We support four tokens representing identity:
1879 * - Credential reference
1882 * - NT security identifier
1884 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1891 * kauth_cred_change_egid
1893 * Description: Set EGID by changing the first element of cr_groups for the
1894 * passed credential; if the new EGID exists in the list of
1895 * groups already, then rotate the old EGID into its position,
1896 * otherwise replace it
1898 * Parameters: cred Pointer to the credential to modify
1899 * new_egid The new EGID to set
1901 * Returns: 0 The egid did not displace a member of
1902 * the supplementary group list
1903 * 1 The egid being set displaced a member
1904 * of the supplementary groups list
1906 * Note: Utility function; internal use only because of locking.
1908 * This function operates on the credential passed; the caller
1909 * must operate either on a newly allocated credential (one for
1910 * which there is no hash cache reference and no externally
1911 * visible pointer reference), or a template credential.
1914 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1920 #endif /* radar_4600026 */
1921 gid_t old_egid
= kauth_cred_getgid(cred
);
1922 posix_cred_t pcred
= posix_cred_get(cred
);
1924 /* Ignoring the first entry, scan for a match for the new egid */
1925 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1927 * If we find a match, swap them so we don't lose overall
1930 if (pcred
->cr_groups
[i
] == new_egid
) {
1931 pcred
->cr_groups
[i
] = old_egid
;
1932 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1939 #error Fix radar 4600026 first!!!
1942 This is correct for memberd behaviour, but incorrect for POSIX; to address
1943 this, we would need to automatically opt-out any SUID/SGID binary, and force
1944 it to use initgroups to opt back in. We take the approach of considering it
1945 opt'ed out in any group of 16 displacement instead, since it's a much more
1946 conservative approach (i.e. less likely to cause things to break).
1950 * If we displaced a member of the supplementary groups list of the
1951 * credential, and we have not opted out of memberd, then if memberd
1952 * says that the credential is a member of the group, then it has not
1953 * actually been displaced.
1955 * NB: This is typically a cold code path.
1957 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1958 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1961 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1963 #endif /* radar_4600026 */
1965 /* set the new EGID into the old spot */
1966 pcred
->cr_groups
[0] = new_egid
;
1975 * Description: Fetch UID from credential
1977 * Parameters: cred Credential to examine
1979 * Returns: (uid_t) UID associated with credential
1982 kauth_cred_getuid(kauth_cred_t cred
)
1984 NULLCRED_CHECK(cred
);
1985 return(posix_cred_get(cred
)->cr_uid
);
1990 * kauth_cred_getruid
1992 * Description: Fetch RUID from credential
1994 * Parameters: cred Credential to examine
1996 * Returns: (uid_t) RUID associated with credential
1999 kauth_cred_getruid(kauth_cred_t cred
)
2001 NULLCRED_CHECK(cred
);
2002 return(posix_cred_get(cred
)->cr_ruid
);
2007 * kauth_cred_getsvuid
2009 * Description: Fetch SVUID from credential
2011 * Parameters: cred Credential to examine
2013 * Returns: (uid_t) SVUID associated with credential
2016 kauth_cred_getsvuid(kauth_cred_t cred
)
2018 NULLCRED_CHECK(cred
);
2019 return(posix_cred_get(cred
)->cr_svuid
);
2026 * Description: Fetch GID from credential
2028 * Parameters: cred Credential to examine
2030 * Returns: (gid_t) GID associated with credential
2033 kauth_cred_getgid(kauth_cred_t cred
)
2035 NULLCRED_CHECK(cred
);
2036 return(posix_cred_get(cred
)->cr_gid
);
2041 * kauth_cred_getrgid
2043 * Description: Fetch RGID from credential
2045 * Parameters: cred Credential to examine
2047 * Returns: (gid_t) RGID associated with credential
2050 kauth_cred_getrgid(kauth_cred_t cred
)
2052 NULLCRED_CHECK(cred
);
2053 return(posix_cred_get(cred
)->cr_rgid
);
2058 * kauth_cred_getsvgid
2060 * Description: Fetch SVGID from credential
2062 * Parameters: cred Credential to examine
2064 * Returns: (gid_t) SVGID associated with credential
2067 kauth_cred_getsvgid(kauth_cred_t cred
)
2069 NULLCRED_CHECK(cred
);
2070 return(posix_cred_get(cred
)->cr_svgid
);
2074 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2076 #if CONFIG_EXT_RESOLVER == 0
2078 * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
2081 kauth_cred_cache_lookup(__unused
int from
, __unused
int to
,
2082 __unused
void *src
, __unused
void *dst
)
2084 return (EWOULDBLOCK
);
2089 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2091 * Structure to hold supplemental groups. Used for impedance matching with
2092 * kauth_cred_cache_lookup below.
2100 * kauth_cred_uid2groups
2102 * Description: Fetch supplemental GROUPS from UID
2104 * Parameters: uid UID to examine
2105 * groups pointer to an array of gid_ts
2106 * gcount pointer to the number of groups wanted/returned
2108 * Returns: 0 Success
2109 * kauth_cred_cache_lookup:EINVAL
2112 * *groups Modified, if successful
2113 * *gcount Modified, if successful
2117 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, int *gcount
)
2121 struct supgroups supgroups
;
2122 supgroups
.count
= gcount
;
2123 supgroups
.groups
= groups
;
2125 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2132 * kauth_cred_guid2pwnam
2134 * Description: Fetch PWNAM from GUID
2136 * Parameters: guidp Pointer to GUID to examine
2137 * pwnam Pointer to user@domain buffer
2139 * Returns: 0 Success
2140 * kauth_cred_cache_lookup:EINVAL
2143 * *pwnam Modified, if successful
2145 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2148 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2150 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
2155 * kauth_cred_guid2grnam
2157 * Description: Fetch GRNAM from GUID
2159 * Parameters: guidp Pointer to GUID to examine
2160 * grnam Pointer to group@domain buffer
2162 * Returns: 0 Success
2163 * kauth_cred_cache_lookup:EINVAL
2166 * *grnam Modified, if successful
2168 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2171 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2173 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
2178 * kauth_cred_pwnam2guid
2180 * Description: Fetch PWNAM from GUID
2182 * Parameters: pwnam String containing user@domain
2183 * guidp Pointer to buffer for GUID
2185 * Returns: 0 Success
2186 * kauth_cred_cache_lookup:EINVAL
2189 * *guidp Modified, if successful
2191 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2192 * bytes in size, including the NUL termination of the string.
2195 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2197 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
2202 * kauth_cred_grnam2guid
2204 * Description: Fetch GRNAM from GUID
2206 * Parameters: grnam String containing group@domain
2207 * guidp Pointer to buffer for GUID
2209 * Returns: 0 Success
2210 * kauth_cred_cache_lookup:EINVAL
2213 * *guidp Modified, if successful
2215 * Notes: grnam should not point to a request larger than MAXPATHLEN
2216 * bytes in size, including the NUL termination of the string.
2219 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2221 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
2226 * kauth_cred_guid2uid
2228 * Description: Fetch UID from GUID
2230 * Parameters: guidp Pointer to GUID to examine
2231 * uidp Pointer to buffer for UID
2233 * Returns: 0 Success
2234 * kauth_cred_cache_lookup:EINVAL
2237 * *uidp Modified, if successful
2240 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2242 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
2247 * kauth_cred_guid2gid
2249 * Description: Fetch GID from GUID
2251 * Parameters: guidp Pointer to GUID to examine
2252 * gidp Pointer to buffer for GID
2254 * Returns: 0 Success
2255 * kauth_cred_cache_lookup:EINVAL
2258 * *gidp Modified, if successful
2261 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2263 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2268 * kauth_cred_ntsid2uid
2270 * Description: Fetch UID from NTSID
2272 * Parameters: sidp Pointer to NTSID to examine
2273 * uidp Pointer to buffer for UID
2275 * Returns: 0 Success
2276 * kauth_cred_cache_lookup:EINVAL
2279 * *uidp Modified, if successful
2282 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2284 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2289 * kauth_cred_ntsid2gid
2291 * Description: Fetch GID from NTSID
2293 * Parameters: sidp Pointer to NTSID to examine
2294 * gidp Pointer to buffer for GID
2296 * Returns: 0 Success
2297 * kauth_cred_cache_lookup:EINVAL
2300 * *gidp Modified, if successful
2303 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2305 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2310 * kauth_cred_ntsid2guid
2312 * Description: Fetch GUID from NTSID
2314 * Parameters: sidp Pointer to NTSID 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_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2326 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2331 * kauth_cred_uid2guid
2333 * Description: Fetch GUID from UID
2335 * Parameters: uid UID 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_uid2guid(uid_t uid
, guid_t
*guidp
)
2347 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2352 * kauth_cred_getguid
2354 * Description: Fetch GUID from credential
2356 * Parameters: cred Credential to examine
2357 * guidp Pointer to buffer for GUID
2359 * Returns: 0 Success
2360 * kauth_cred_cache_lookup:EINVAL
2363 * *guidp Modified, if successful
2366 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2368 NULLCRED_CHECK(cred
);
2369 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2374 * kauth_cred_getguid
2376 * Description: Fetch GUID from GID
2378 * Parameters: gid GID to examine
2379 * guidp Pointer to buffer for GUID
2381 * Returns: 0 Success
2382 * kauth_cred_cache_lookup:EINVAL
2385 * *guidp Modified, if successful
2388 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2390 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2395 * kauth_cred_uid2ntsid
2397 * Description: Fetch NTSID from UID
2399 * Parameters: uid UID 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_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2411 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2416 * kauth_cred_getntsid
2418 * Description: Fetch NTSID from credential
2420 * Parameters: cred Credential to examine
2421 * sidp Pointer to buffer for NTSID
2423 * Returns: 0 Success
2424 * kauth_cred_cache_lookup:EINVAL
2427 * *sidp Modified, if successful
2430 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2432 NULLCRED_CHECK(cred
);
2433 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2438 * kauth_cred_gid2ntsid
2440 * Description: Fetch NTSID from GID
2442 * Parameters: gid GID 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_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2454 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2459 * kauth_cred_guid2ntsid
2461 * Description: Fetch NTSID from GUID
2463 * Parameters: guidp Pointer to GUID to examine
2464 * sidp Pointer to buffer for NTSID
2466 * Returns: 0 Success
2467 * kauth_cred_cache_lookup:EINVAL
2470 * *sidp Modified, if successful
2473 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2475 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2480 * kauth_cred_cache_lookup
2482 * Description: Lookup a translation in the cache; if one is not found, and
2483 * the attempt was not fatal, submit the request to the resolver
2484 * instead, and wait for it to complete or be aborted.
2486 * Parameters: from Identity information we have
2487 * to Identity information we want
2488 * src Pointer to buffer containing
2489 * the source identity
2490 * dst Pointer to buffer to receive
2491 * the target identity
2493 * Returns: 0 Success
2494 * EINVAL Unknown source identity type
2496 #if CONFIG_EXT_RESOLVER
2498 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2500 struct kauth_identity ki
;
2501 struct kauth_identity_extlookup el
;
2503 uint64_t extend_data
= 0ULL;
2504 int (* expired
)(struct kauth_identity
*kip
);
2505 char *namebuf
= NULL
;
2507 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2510 * Look for an existing cache entry for this association.
2511 * If the entry has not expired, return the cached information.
2512 * We do not cache user@domain translations here; they use too
2513 * much memory to hold onto forever, and can not be updated
2516 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2522 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2525 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2528 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2530 case KI_VALID_NTSID
:
2531 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2533 case KI_VALID_PWNAM
:
2534 case KI_VALID_GRNAM
:
2535 /* Names are unique in their 'from' space */
2536 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2541 /* lookup failure or error */
2543 /* any other error is fatal */
2544 if (error
!= ENOENT
) {
2545 /* XXX bogus check - this is not possible */
2546 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2550 /* found a valid cached entry, check expiry */
2553 expired
= kauth_identity_guid_expired
;
2555 case KI_VALID_NTSID
:
2556 expired
= kauth_identity_ntsid_expired
;
2558 case KI_VALID_GROUPS
:
2559 expired
= kauth_identity_groups_expired
;
2564 expired
= kauth_identity_guid_expired
;
2566 case KI_VALID_NTSID
:
2567 expired
= kauth_identity_ntsid_expired
;
2575 * If no expiry function, or not expired, we have found
2579 if (!expired(&ki
)) {
2580 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2581 expired
= NULL
; /* must clear it is used as a flag */
2584 * We leave ki_valid set here; it contains a
2585 * translation but the TTL has expired. If we can't
2586 * get a result from the resolver, we will use it as
2587 * a better-than nothing alternative.
2590 KAUTH_DEBUG("CACHE - expired entry found");
2593 KAUTH_DEBUG("CACHE - no expiry function");
2597 /* do we have a translation? */
2598 if (ki
.ki_valid
& to
) {
2599 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2600 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2604 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2605 * If we went looking for a translation from GUID or NTSID and
2606 * found a translation that wasn't for our desired type, then
2607 * don't bother calling the resolver. We know that this
2608 * GUID/NTSID can't translate to our desired type.
2612 case KI_VALID_NTSID
:
2615 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2616 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2621 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2622 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2634 * We failed to find a cache entry; call the resolver.
2636 * Note: We ask for as much non-extended data as we can get,
2637 * and only provide (or ask for) extended information if
2638 * we have a 'from' (or 'to') which requires it. This
2639 * way we don't pay for the extra transfer overhead for
2640 * data we don't need.
2642 bzero(&el
, sizeof(el
));
2643 el
.el_info_pid
= current_proc()->p_pid
;
2646 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2647 el
.el_uid
= *(uid_t
*)src
;
2650 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2651 el
.el_gid
= *(gid_t
*)src
;
2654 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2655 el
.el_uguid
= *(guid_t
*)src
;
2656 el
.el_gguid
= *(guid_t
*)src
;
2658 case KI_VALID_NTSID
:
2659 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2660 el
.el_usid
= *(ntsid_t
*)src
;
2661 el
.el_gsid
= *(ntsid_t
*)src
;
2663 case KI_VALID_PWNAM
:
2664 /* extra overhead */
2665 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2666 extend_data
= CAST_USER_ADDR_T(src
);
2668 case KI_VALID_GRNAM
:
2669 /* extra overhead */
2670 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2671 extend_data
= CAST_USER_ADDR_T(src
);
2677 * Here we ask for everything all at once, to avoid having to work
2678 * out what we really want now, or might want soon.
2680 * Asking for SID translations when we don't know we need them right
2681 * now is going to cause excess work to be done if we're connected
2682 * to a network that thinks it can translate them. This list needs
2683 * to get smaller/smarter.
2685 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2686 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2687 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2688 if (to
== KI_VALID_PWNAM
) {
2689 /* extra overhead */
2690 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2691 extend_data
= CAST_USER_ADDR_T(dst
);
2693 if (to
== KI_VALID_GRNAM
) {
2694 /* extra overhead */
2695 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2696 extend_data
= CAST_USER_ADDR_T(dst
);
2698 if (to
== KI_VALID_GROUPS
) {
2699 /* Expensive and only useful for an NFS client not using kerberos */
2700 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2701 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2703 * Copy the current supplemental groups for the resolver.
2704 * The resolver should check these groups first and if
2705 * the user (uid) is still a member it should endeavor to
2706 * keep them in the list. Otherwise NFS clients could get
2707 * changing access to server file system objects on each
2710 el
.el_sup_grp_cnt
= ki
.ki_supgrpcnt
;
2712 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof (el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2713 /* Let the resolver know these were the previous valid groups */
2714 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2715 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2717 KAUTH_DEBUG("GROUPS: no valid groups to send");
2721 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2723 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2725 error
= kauth_resolver_submit(&el
, extend_data
);
2727 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2729 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2731 /* was the external lookup successful? */
2734 * Save the results from the lookup - we may have other
2735 * information, even if we didn't get a guid or the
2738 * If we came from a name, we know the extend_data is valid.
2740 if (from
== KI_VALID_PWNAM
)
2741 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2742 else if (from
== KI_VALID_GRNAM
)
2743 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2745 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2748 * Check to see if we have a valid cache entry
2749 * originating from the result.
2751 if (!(ki
.ki_valid
& to
)) {
2759 * Copy from the appropriate struct kauth_identity cache entry
2760 * structure into the destination buffer area.
2764 *(uid_t
*)dst
= ki
.ki_uid
;
2767 *(gid_t
*)dst
= ki
.ki_gid
;
2770 *(guid_t
*)dst
= ki
.ki_guid
;
2772 case KI_VALID_NTSID
:
2773 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2775 case KI_VALID_GROUPS
: {
2776 struct supgroups
*gp
= (struct supgroups
*)dst
;
2777 u_int32_t limit
= ki
.ki_supgrpcnt
;
2780 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2784 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2787 case KI_VALID_PWNAM
:
2788 case KI_VALID_GRNAM
:
2789 /* handled in kauth_resolver_complete() */
2794 KAUTH_DEBUG("CACHE - returned successfully");
2800 * Group membership cache.
2802 * XXX the linked-list implementation here needs to be optimized.
2808 * Description: Initialize the groups cache
2810 * Parameters: (void)
2814 * Notes: Initialize the groups cache for use; the group cache is used
2815 * to avoid unnecessary calls out to user space.
2817 * This function is called from kauth_init() in the file
2818 * kern_authorization.c.
2821 kauth_groups_init(void)
2823 TAILQ_INIT(&kauth_groups
);
2824 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2829 * kauth_groups_expired
2831 * Description: Handle lazy expiration of group membership cache entries
2833 * Parameters: gm group membership entry to
2834 * check for expiration
2836 * Returns: 1 Expired
2840 kauth_groups_expired(struct kauth_group_membership
*gm
)
2845 * Expiration time of 0 means this entry is persistent.
2847 if (gm
->gm_expiry
== 0)
2852 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2859 * Description: Promote the entry to the head of the LRU, assumes the cache
2862 * Parameters: kip group membership entry to move
2863 * to the head of the LRU list,
2864 * if it's not already there
2868 * Notes: This is called even if the entry has expired; typically an
2869 * expired entry that's been looked up is about to be revalidated,
2870 * and having it closer to the head of the LRU means finding it
2871 * quickly again when the revalidation comes through.
2874 kauth_groups_lru(struct kauth_group_membership
*gm
)
2876 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2877 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2878 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2884 * kauth_groups_updatecache
2886 * Description: Given a lookup result, add any group cache associations that
2887 * we don't currently have.
2889 * Parameters: elp External lookup result from
2890 * user space daemon to kernel
2891 * rkip pointer to returned kauth
2897 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2899 struct kauth_group_membership
*gm
;
2902 /* need a valid response if we are to cache anything */
2904 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2905 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2911 * Search for an existing record for this association before inserting
2912 * a new one; if we find one, update it instead of creating a new one
2914 KAUTH_GROUPS_LOCK();
2915 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2916 if ((el
->el_uid
== gm
->gm_uid
) &&
2917 (el
->el_gid
== gm
->gm_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;
2924 kauth_groups_lru(gm
);
2928 KAUTH_GROUPS_UNLOCK();
2930 /* if we found an entry to update, stop here */
2934 /* allocate a new record */
2935 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2937 gm
->gm_uid
= el
->el_uid
;
2938 gm
->gm_gid
= el
->el_gid
;
2939 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2940 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2942 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2944 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2948 * Insert the new entry. Note that it's possible to race ourselves
2949 * here and end up with duplicate entries in the list. Wasteful, but
2950 * harmless since the first into the list will never be looked up,
2951 * and thus will eventually just fall off the end.
2953 KAUTH_GROUPS_LOCK();
2954 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2955 if (++kauth_groups_count
> kauth_groups_cachemax
) {
2956 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2957 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2958 kauth_groups_count
--;
2962 KAUTH_GROUPS_UNLOCK();
2964 /* free expired cache entry */
2970 * Trim older entries from the group membership cache.
2972 * Must be called with the group cache lock held.
2975 kauth_groups_trimcache(int new_size
) {
2976 struct kauth_group_membership
*gm
;
2978 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
2980 while (kauth_groups_count
> new_size
) {
2981 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2982 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2983 kauth_groups_count
--;
2987 #endif /* CONFIG_EXT_RESOLVER */
2990 * Group membership KPI
2994 * kauth_cred_ismember_gid
2996 * Description: Given a credential and a GID, determine if the GID is a member
2997 * of one of the supplementary groups associated with the given
3000 * Parameters: cred Credential to check in
3001 * gid GID to check for membership
3002 * resultp Pointer to int to contain the
3003 * result of the call
3005 * Returns: 0 Success
3006 * ENOENT Could not perform lookup
3007 * kauth_resolver_submit:EWOULDBLOCK
3008 * kauth_resolver_submit:EINTR
3009 * kauth_resolver_submit:ENOMEM
3010 * kauth_resolver_submit:ENOENT User space daemon did not vend
3012 * kauth_resolver_submit:??? Unlikely error from user space
3015 * *resultp (modified) 1 Is member
3018 * Notes: This function guarantees not to modify resultp when returning
3021 * This function effectively checks the EGID as well, since the
3022 * EGID is cr_groups[0] as an implementation detail.
3025 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3027 posix_cred_t pcred
= posix_cred_get(cred
);
3031 * Check the per-credential list of override groups.
3033 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3034 * the cache should be used for that case.
3036 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3037 if (gid
== pcred
->cr_groups
[i
]) {
3044 * If we don't have a UID for group membership checks, the in-cred list
3045 * was authoritative and we can stop here.
3047 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3052 #if CONFIG_EXT_RESOLVER
3053 struct kauth_group_membership
*gm
;
3054 struct kauth_identity_extlookup el
;
3058 * If the resolver hasn't checked in yet, we are early in the boot
3059 * phase and the local group list is complete and authoritative.
3061 if (!kauth_resolver_registered
) {
3067 /* XXX check supplementary groups */
3068 /* XXX check whiteout groups */
3069 /* XXX nesting of supplementary/whiteout groups? */
3072 * Check the group cache.
3074 KAUTH_GROUPS_LOCK();
3075 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3076 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3077 kauth_groups_lru(gm
);
3082 /* did we find a membership entry? */
3084 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3085 KAUTH_GROUPS_UNLOCK();
3087 /* if we did, we can return now */
3089 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3093 /* nothing in the cache, need to go to userland */
3094 bzero(&el
, sizeof(el
));
3095 el
.el_info_pid
= current_proc()->p_pid
;
3096 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3097 el
.el_uid
= pcred
->cr_gmuid
;
3099 el
.el_member_valid
= 0; /* XXX set by resolver? */
3101 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3103 error
= kauth_resolver_submit(&el
, 0ULL);
3105 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3109 /* save the results from the lookup */
3110 kauth_groups_updatecache(&el
);
3112 /* if we successfully ascertained membership, report */
3113 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3114 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3126 * kauth_cred_ismember_guid
3128 * Description: Determine whether the supplied credential is a member of the
3129 * group nominated by GUID.
3131 * Parameters: cred Credential to check in
3132 * guidp Pointer to GUID whose group
3133 * we are testing for membership
3134 * resultp Pointer to int to contain the
3135 * result of the call
3137 * Returns: 0 Success
3138 * kauth_cred_guid2gid:EINVAL
3139 * kauth_cred_ismember_gid:ENOENT
3140 * kauth_resolver_submit:ENOENT User space daemon did not vend
3142 * kauth_cred_ismember_gid:EWOULDBLOCK
3143 * kauth_cred_ismember_gid:EINTR
3144 * kauth_cred_ismember_gid:ENOMEM
3145 * kauth_cred_ismember_gid:??? Unlikely error from user space
3148 * *resultp (modified) 1 Is member
3152 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3156 switch (kauth_wellknown_guid(guidp
)) {
3157 case KAUTH_WKG_NOBODY
:
3160 case KAUTH_WKG_EVERYBODY
:
3164 #if CONFIG_EXT_RESOLVER
3166 struct kauth_identity ki
;
3170 * Grovel the identity cache looking for this GUID.
3171 * If we find it, and it is for a user record, return
3172 * false because it's not a group.
3174 * This is necessary because we don't have -ve caching
3175 * of group memberships, and we really want to avoid
3176 * calling out to the resolver if at all possible.
3178 * Because we're called by the ACL evaluator, and the
3179 * ACL evaluator is likely to encounter ACEs for users,
3180 * this is expected to be a common case.
3183 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3184 !kauth_identity_guid_expired(&ki
)) {
3185 if (ki
.ki_valid
& KI_VALID_GID
) {
3186 /* It's a group after all... */
3190 if (ki
.ki_valid
& KI_VALID_UID
) {
3195 #endif /* 6603280 */
3197 * Attempt to translate the GUID to a GID. Even if
3198 * this fails, we will have primed the cache if it is
3199 * a user record and we'll see it above the next time
3202 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3204 * If we have no guid -> gid translation, it's not a group and
3205 * thus the cred can't be a member.
3207 if (error
== ENOENT
) {
3213 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3216 #else /* CONFIG_EXT_RESOLVER */
3218 #endif /* CONFIG_EXT_RESOLVER */
3225 * kauth_cred_gid_subset
3227 * Description: Given two credentials, determine if all GIDs associated with
3228 * the first are also associated with the second
3230 * Parameters: cred1 Credential to check for
3231 * cred2 Credential to check in
3232 * resultp Pointer to int to contain the
3233 * result of the call
3235 * Returns: 0 Success
3236 * non-zero See kauth_cred_ismember_gid for
3240 * *resultp (modified) 1 Is subset
3243 * Notes: This function guarantees not to modify resultp when returning
3247 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3249 int i
, err
, res
= 1;
3251 posix_cred_t pcred1
= posix_cred_get(cred1
);
3252 posix_cred_t pcred2
= posix_cred_get(cred2
);
3254 /* First, check the local list of groups */
3255 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3256 gid
= pcred1
->cr_groups
[i
];
3257 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3261 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3267 /* Check real gid */
3268 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3272 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3273 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3278 /* Finally, check saved gid */
3279 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
3283 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3284 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3295 * kauth_cred_issuser
3297 * Description: Fast replacement for issuser()
3299 * Parameters: cred Credential to check for super
3302 * Returns: 0 Not super user
3305 * Notes: This function uses a magic number which is not a manifest
3306 * constant; this is bad practice.
3309 kauth_cred_issuser(kauth_cred_t cred
)
3311 return(kauth_cred_getuid(cred
) == 0);
3319 /* lock protecting credential hash table */
3320 static lck_mtx_t
*kauth_cred_hash_mtx
;
3321 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3322 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
3323 #if KAUTH_CRED_HASH_DEBUG
3324 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3325 #else /* !KAUTH_CRED_HASH_DEBUG */
3326 #define KAUTH_CRED_HASH_LOCK_ASSERT()
3327 #endif /* !KAUTH_CRED_HASH_DEBUG */
3333 * Description: Initialize the credential hash cache
3335 * Parameters: (void)
3339 * Notes: Intialize the credential hash cache for use; the credential
3340 * hash cache is used convert duplicate credentials into a
3341 * single reference counted credential in order to save wired
3342 * kernel memory. In practice, this generally means a desktop
3343 * system runs with a few tens of credentials, instead of one
3344 * per process, one per thread, one per vnode cache entry, and
3345 * so on. This generally results in savings of 200K or more
3346 * (potentially much more on server systems).
3348 * The hash cache internally has a reference on the credential
3349 * for itself as a means of avoiding a reclaim race for a
3350 * credential in the process of having it's last non-hash
3351 * reference released. This would otherwise result in the
3352 * possibility of a freed credential that was still in uses due
3353 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3355 * On final release, the hash reference is droped, and the
3356 * credential is freed back to the system.
3358 * This function is called from kauth_init() in the file
3359 * kern_authorization.c.
3362 kauth_cred_init(void)
3366 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3367 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
3369 /*allocate credential hash table */
3370 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3371 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
3372 M_KAUTH
, M_WAITOK
| M_ZERO
);
3373 if (kauth_cred_table_anchor
== NULL
)
3374 panic("startup: kauth_cred_init");
3375 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
3376 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3384 * Description: Get the current thread's effective UID.
3386 * Parameters: (void)
3388 * Returns: (uid_t) The effective UID of the
3394 return(kauth_cred_getuid(kauth_cred_get()));
3401 * Description: Get the current thread's real UID.
3403 * Parameters: (void)
3405 * Returns: (uid_t) The real UID of the current
3411 return(kauth_cred_getruid(kauth_cred_get()));
3418 * Description: Get the current thread's effective GID.
3420 * Parameters: (void)
3422 * Returns: (gid_t) The effective GID of the
3428 return(kauth_cred_getgid(kauth_cred_get()));
3435 * Description: Get the current thread's real GID.
3437 * Parameters: (void)
3439 * Returns: (gid_t) The real GID of the current
3445 return(kauth_cred_getrgid(kauth_cred_get()));
3452 * Description: Returns a pointer to the current thread's credential
3454 * Parameters: (void)
3456 * Returns: (kauth_cred_t) Pointer to the current thread's
3459 * Notes: This function does not take a reference; because of this, the
3460 * caller MUST NOT do anything that would let the thread's
3461 * credential change while using the returned value, without
3462 * first explicitly taking their own reference.
3464 * If a caller intends to take a reference on the resulting
3465 * credential pointer from calling this function, it is strongly
3466 * recommended that the caller use kauth_cred_get_with_ref()
3467 * instead, to protect against any future changes to the cred
3468 * locking protocols; such changes could otherwise potentially
3469 * introduce race windows in the callers code.
3472 kauth_cred_get(void)
3475 struct uthread
*uthread
;
3477 uthread
= get_bsdthread_info(current_thread());
3479 if (uthread
== NULL
)
3480 panic("thread wants credential but has no BSD thread info");
3482 * We can lazy-bind credentials to threads, as long as their processes
3485 * XXX If we later inline this function, the code in this block
3486 * XXX should probably be called out in a function.
3488 if (uthread
->uu_ucred
== NOCRED
) {
3489 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3490 panic("thread wants credential but has no BSD process");
3491 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3493 return(uthread
->uu_ucred
);
3497 mach_kauth_cred_uthread_update(void)
3502 uthread
= get_bsdthread_info(current_thread());
3503 proc
= current_proc();
3505 kauth_cred_uthread_update(uthread
, proc
);
3509 * kauth_cred_uthread_update
3511 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3512 * late-bind the uthread cred to the proc cred.
3514 * Parameters: uthread_t The uthread to update
3515 * proc_t The process to update to
3519 * Notes: This code is common code called from system call or trap entry
3520 * in the case that the process thread may have been changed
3521 * since the last time the thread entered the kernel. It is
3522 * generally only called with the current uthread and process as
3526 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3528 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3529 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3530 kauth_cred_t old
= uthread
->uu_ucred
;
3531 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3532 if (IS_VALID_CRED(old
))
3533 kauth_cred_unref(&old
);
3539 * kauth_cred_get_with_ref
3541 * Description: Takes a reference on the current thread's credential, and then
3542 * returns a pointer to it to the caller.
3544 * Parameters: (void)
3546 * Returns: (kauth_cred_t) Pointer to the current thread's
3547 * newly referenced credential
3549 * Notes: This function takes a reference on the credential before
3550 * returning it to the caller.
3552 * It is the responsibility of the calling code to release this
3553 * reference when the credential is no longer in use.
3555 * Since the returned reference may be a persistent reference
3556 * (e.g. one cached in another data structure with a lifetime
3557 * longer than the calling function), this release may be delayed
3558 * until such time as the persistent reference is to be destroyed.
3559 * An example of this would be the per vnode credential cache used
3560 * to accelerate lookup operations.
3563 kauth_cred_get_with_ref(void)
3566 struct uthread
*uthread
;
3568 uthread
= get_bsdthread_info(current_thread());
3570 if (uthread
== NULL
)
3571 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3572 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3573 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3576 * We can lazy-bind credentials to threads, as long as their processes
3579 * XXX If we later inline this function, the code in this block
3580 * XXX should probably be called out in a function.
3582 if (uthread
->uu_ucred
== NOCRED
) {
3583 /* take reference for new cred in thread */
3584 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3586 /* take a reference for our caller */
3587 kauth_cred_ref(uthread
->uu_ucred
);
3588 return(uthread
->uu_ucred
);
3593 * kauth_cred_proc_ref
3595 * Description: Takes a reference on the current process's credential, and
3596 * then returns a pointer to it to the caller.
3598 * Parameters: procp Process whose credential we
3599 * intend to take a reference on
3601 * Returns: (kauth_cred_t) Pointer to the process's
3602 * newly referenced credential
3604 * Locks: PROC_LOCK is held before taking the reference and released
3605 * after the refeence is taken to protect the p_ucred field of
3606 * the process referred to by procp.
3608 * Notes: This function takes a reference on the credential before
3609 * returning it to the caller.
3611 * It is the responsibility of the calling code to release this
3612 * reference when the credential is no longer in use.
3614 * Since the returned reference may be a persistent reference
3615 * (e.g. one cached in another data structure with a lifetime
3616 * longer than the calling function), this release may be delayed
3617 * until such time as the persistent reference is to be destroyed.
3618 * An example of this would be the per vnode credential cache used
3619 * to accelerate lookup operations.
3622 kauth_cred_proc_ref(proc_t procp
)
3627 cred
= proc_ucred(procp
);
3628 kauth_cred_ref(cred
);
3637 * Description: Allocate a new credential
3639 * Parameters: (void)
3641 * Returns: !NULL Newly allocated credential
3642 * NULL Insufficient memory
3644 * Notes: The newly allocated credential is zero'ed as part of the
3645 * allocation process, with the exception of the reference
3646 * count, which is set to 1 to indicate a single reference
3647 * held by the caller.
3649 * Since newly allocated credentials have no external pointers
3650 * referencing them, prior to making them visible in an externally
3651 * visible pointer (e.g. by adding them to the credential hash
3652 * cache) is the only legal time in which an existing credential
3653 * can be safely iinitialized or modified directly.
3655 * After initialization, the caller is expected to call the
3656 * function kauth_cred_add() to add the credential to the hash
3657 * cache, after which time it's frozen and becomes publically
3660 * The release protocol depends on kauth_hash_add() being called
3661 * before kauth_cred_rele() (there is a diagnostic panic which
3662 * will trigger if this protocol is not observed).
3664 * XXX: This function really ought to be static, rather than being
3665 * exported as KPI, since a failure of kauth_cred_add() can only
3666 * be handled by an explicit free of the credential; such frees
3667 * depend on knowlegdge of the allocation method used, which is
3668 * permitted to change between kernel revisions.
3670 * XXX: In the insufficient resource case, this code panic's rather
3671 * than returning a NULL pointer; the code that calls this
3672 * function needs to be audited before this can be changed.
3675 kauth_cred_alloc(void)
3677 kauth_cred_t newcred
;
3679 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3681 posix_cred_t newpcred
= posix_cred_get(newcred
);
3682 bzero(newcred
, sizeof(*newcred
));
3683 newcred
->cr_ref
= 1;
3684 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3685 /* must do this, or cred has same group membership as uid 0 */
3686 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3689 panic("kauth_cred_alloc: couldn't allocate credential");
3693 #if KAUTH_CRED_HASH_DEBUG
3698 mac_cred_label_init(newcred
);
3708 * Description: Look to see if we already have a known credential in the hash
3709 * cache; if one is found, bump the reference count and return
3710 * it. If there are no credentials that match the given
3711 * credential, then allocate a new credential.
3713 * Parameters: cred Template for credential to
3716 * Returns: (kauth_cred_t) The credential that was found
3717 * in the hash or created
3718 * NULL kauth_cred_add() failed, or
3719 * there was not an egid specified
3721 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3722 * maintain this field, we can't expect callers to know how it
3723 * needs to be set. Callers should be prepared for this field
3724 * to be overwritten.
3726 * XXX: This code will tight-loop if memory for a new credential is
3727 * persistently unavailable; this is perhaps not the wisest way
3728 * to handle this condition, but current callers do not expect
3732 kauth_cred_create(kauth_cred_t cred
)
3734 kauth_cred_t found_cred
, new_cred
= NULL
;
3735 posix_cred_t pcred
= posix_cred_get(cred
);
3738 KAUTH_CRED_HASH_LOCK_ASSERT();
3740 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3741 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3744 * If the template credential is not opting out of external
3745 * group membership resolution, then we need to check that
3746 * the UID we will be using is resolvable by the external
3747 * resolver. If it's not, then we opt it out anyway, since
3748 * all future external resolution requests will be failing
3749 * anyway, and potentially taking a long time to do it. We
3750 * use gid 0 because we always know it will exist and not
3751 * trigger additional lookups. This is OK, because we end up
3752 * precatching the information here as a result.
3754 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3756 * It's a recognized value; we don't really care about
3757 * the answer, so long as it's something the external
3758 * resolver could have vended.
3760 pcred
->cr_gmuid
= pcred
->cr_uid
;
3763 * It's not something the external resolver could
3764 * have vended, so we don't want to ask it more
3765 * questions about the credential in the future. This
3766 * speeds up future lookups, as long as the caller
3767 * caches results; otherwise, it the same recurring
3768 * cost. Since most credentials are used multiple
3769 * times, we still get some performance win from this.
3771 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3772 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3776 /* Caller *must* specify at least the egid in cr_groups[0] */
3777 if (pcred
->cr_ngroups
< 1)
3781 KAUTH_CRED_HASH_LOCK();
3782 found_cred
= kauth_cred_find(cred
);
3783 if (found_cred
!= NULL
) {
3785 * Found an existing credential so we'll bump
3786 * reference count and return
3788 kauth_cred_ref(found_cred
);
3789 KAUTH_CRED_HASH_UNLOCK();
3792 KAUTH_CRED_HASH_UNLOCK();
3795 * No existing credential found. Create one and add it to
3798 new_cred
= kauth_cred_alloc();
3799 if (new_cred
!= NULL
) {
3801 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3802 new_pcred
->cr_uid
= pcred
->cr_uid
;
3803 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3804 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3805 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3806 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3807 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3808 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3809 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3811 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3812 sizeof(new_cred
->cr_audit
));
3814 new_pcred
->cr_flags
= pcred
->cr_flags
;
3816 KAUTH_CRED_HASH_LOCK();
3817 err
= kauth_cred_add(new_cred
);
3818 KAUTH_CRED_HASH_UNLOCK();
3820 /* Retry if kauth_cred_add returns non zero value */
3824 mac_cred_label_destroy(new_cred
);
3826 AUDIT_SESSION_UNREF(new_cred
);
3828 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3838 * kauth_cred_setresuid
3840 * Description: Update the given credential using the UID arguments. The given
3841 * UIDs are used to set the effective UID, real UID, saved UID,
3842 * and GMUID (used for group membership checking).
3844 * Parameters: cred The original credential
3845 * ruid The new real UID
3846 * euid The new effective UID
3847 * svuid The new saved UID
3848 * gmuid KAUTH_UID_NONE -or- the new
3849 * group membership UID
3851 * Returns: (kauth_cred_t) The updated credential
3853 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3854 * setting, so if you don't want it to change, pass it the
3855 * previous value, explicitly.
3857 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3858 * if it returns a credential other than the one it is passed,
3859 * will have dropped the reference on the passed credential. All
3860 * callers should be aware of this, and treat this function as an
3861 * unref + ref, potentially on different credentials.
3863 * Because of this, the caller is expected to take its own
3864 * reference on the credential passed as the first parameter,
3865 * and be prepared to release the reference on the credential
3866 * that is returned to them, if it is not intended to be a
3867 * persistent reference.
3870 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3872 struct ucred temp_cred
;
3873 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3874 posix_cred_t pcred
= posix_cred_get(cred
);
3876 NULLCRED_CHECK(cred
);
3879 * We don't need to do anything if the UIDs we are changing are
3880 * already the same as the UIDs passed in
3882 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3883 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3884 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3885 (pcred
->cr_gmuid
== gmuid
)) {
3886 /* no change needed */
3891 * Look up in cred hash table to see if we have a matching credential
3892 * with the new values; this is done by calling kauth_cred_update().
3894 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3895 if (euid
!= KAUTH_UID_NONE
) {
3896 temp_pcred
->cr_uid
= euid
;
3898 if (ruid
!= KAUTH_UID_NONE
) {
3899 temp_pcred
->cr_ruid
= ruid
;
3901 if (svuid
!= KAUTH_UID_NONE
) {
3902 temp_pcred
->cr_svuid
= svuid
;
3906 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3907 * opt out of participation in external group resolution, unless we
3908 * unless we explicitly opt back in later.
3910 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3911 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3914 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3919 * kauth_cred_setresgid
3921 * Description: Update the given credential using the GID arguments. The given
3922 * GIDs are used to set the effective GID, real GID, and saved
3925 * Parameters: cred The original credential
3926 * rgid The new real GID
3927 * egid The new effective GID
3928 * svgid The new saved GID
3930 * Returns: (kauth_cred_t) The updated credential
3932 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3933 * if it returns a credential other than the one it is passed,
3934 * will have dropped the reference on the passed credential. All
3935 * callers should be aware of this, and treat this function as an
3936 * unref + ref, potentially on different credentials.
3938 * Because of this, the caller is expected to take its own
3939 * reference on the credential passed as the first parameter,
3940 * and be prepared to release the reference on the credential
3941 * that is returned to them, if it is not intended to be a
3942 * persistent reference.
3945 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3947 struct ucred temp_cred
;
3948 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3949 posix_cred_t pcred
= posix_cred_get(cred
);
3951 NULLCRED_CHECK(cred
);
3952 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3955 * We don't need to do anything if the given GID are already the
3956 * same as the GIDs in the credential.
3958 if (pcred
->cr_groups
[0] == egid
&&
3959 pcred
->cr_rgid
== rgid
&&
3960 pcred
->cr_svgid
== svgid
) {
3961 /* no change needed */
3966 * Look up in cred hash table to see if we have a matching credential
3967 * with the new values; this is done by calling kauth_cred_update().
3969 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3970 if (egid
!= KAUTH_GID_NONE
) {
3971 /* displacing a supplementary group opts us out of memberd */
3972 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3973 DEBUG_CRED_CHANGE("displaced!\n");
3974 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3975 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3977 DEBUG_CRED_CHANGE("not displaced\n");
3980 if (rgid
!= KAUTH_GID_NONE
) {
3981 temp_pcred
->cr_rgid
= rgid
;
3983 if (svgid
!= KAUTH_GID_NONE
) {
3984 temp_pcred
->cr_svgid
= svgid
;
3987 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3992 * Update the given credential with the given groups. We only allocate a new
3993 * credential when the given gid actually results in changes to the existing
3995 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3996 * which will be used for group membership checking.
3999 * kauth_cred_setgroups
4001 * Description: Update the given credential using the provide supplementary
4002 * group list and group membership UID
4004 * Parameters: cred The original credential
4005 * groups Pointer to gid_t array which
4006 * contains the new group list
4007 * groupcount The count of valid groups which
4008 * are contained in 'groups'
4009 * gmuid KAUTH_UID_NONE -or- the new
4010 * group membership UID
4012 * Returns: (kauth_cred_t) The updated credential
4014 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4015 * setting, so if you don't want it to change, pass it the
4016 * previous value, explicitly.
4018 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4019 * if it returns a credential other than the one it is passed,
4020 * will have dropped the reference on the passed credential. All
4021 * callers should be aware of this, and treat this function as an
4022 * unref + ref, potentially on different credentials.
4024 * Because of this, the caller is expected to take its own
4025 * reference on the credential passed as the first parameter,
4026 * and be prepared to release the reference on the credential
4027 * that is returned to them, if it is not intended to be a
4028 * persistent reference.
4030 * XXX: Changes are determined in ordinal order - if the caller passes
4031 * in the same groups list that is already present in the
4032 * credential, but the members are in a different order, even if
4033 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4034 * is considered a modification to the credential, and a new
4035 * credential is created.
4037 * This should perhaps be better optimized, but it is considered
4038 * to be the caller's problem.
4041 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
4044 struct ucred temp_cred
;
4045 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4048 NULLCRED_CHECK(cred
);
4050 pcred
= posix_cred_get(cred
);
4053 * We don't need to do anything if the given list of groups does not
4056 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4057 for (i
= 0; i
< groupcount
; i
++) {
4058 if (pcred
->cr_groups
[i
] != groups
[i
])
4061 if (i
== groupcount
) {
4062 /* no change needed */
4068 * Look up in cred hash table to see if we have a matching credential
4069 * with new values. If we are setting or clearing the gmuid, then
4070 * update the cr_flags, since clearing it is sticky. This permits an
4071 * opt-out of memberd processing using setgroups(), and an opt-in
4072 * using initgroups(). This is required for POSIX conformance.
4074 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4075 temp_pcred
->cr_ngroups
= groupcount
;
4076 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
4077 temp_pcred
->cr_gmuid
= gmuid
;
4078 if (gmuid
== KAUTH_UID_NONE
)
4079 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4081 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4083 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4087 * Notes: The return value exists to account for the possibility of a
4088 * kauth_cred_t without a POSIX label. This will be the case in
4089 * the future (see posix_cred_get() below, for more details).
4091 #if CONFIG_EXT_RESOLVER
4092 int kauth_external_supplementary_groups_supported
= 1;
4094 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4098 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
4100 int limit
= NGROUPS
;
4103 pcred
= posix_cred_get(cred
);
4105 #if CONFIG_EXT_RESOLVER
4107 * If we've not opted out of using the resolver, then convert the cred to a list
4108 * of supplemental groups. We do this only if there has been a resolver to talk to,
4109 * since we may be too early in boot, or in an environment that isn't using DS.
4111 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4112 uid_t uid
= kauth_cred_getuid(cred
);
4115 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4119 /* On error just fall through */
4120 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4122 #endif /* CONFIG_EXT_RESOLVER */
4125 * If they just want a copy of the groups list, they may not care
4126 * about the actual count. If they specify an input count, however,
4127 * treat it as an indicator of the buffer size available in grouplist,
4128 * and limit the returned list to that size.
4131 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4135 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4142 * kauth_cred_setuidgid
4144 * Description: Update the given credential using the UID and GID arguments.
4145 * The given UID is used to set the effective UID, real UID, and
4146 * saved UID. The given GID is used to set the effective GID,
4147 * real GID, and saved GID.
4149 * Parameters: cred The original credential
4150 * uid The new UID to use
4151 * gid The new GID to use
4153 * Returns: (kauth_cred_t) The updated credential
4155 * Notes: We set the gmuid to uid if the credential we are inheriting
4156 * from has not opted out of memberd participation; otherwise
4157 * we set it to KAUTH_UID_NONE
4159 * This code is only ever called from the per-thread credential
4160 * code path in the "set per thread credential" case; and in
4161 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4164 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4165 * if it returns a credential other than the one it is passed,
4166 * will have dropped the reference on the passed credential. All
4167 * callers should be aware of this, and treat this function as an
4168 * unref + ref, potentially on different credentials.
4170 * Because of this, the caller is expected to take its own
4171 * reference on the credential passed as the first parameter,
4172 * and be prepared to release the reference on the credential
4173 * that is returned to them, if it is not intended to be a
4174 * persistent reference.
4177 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4179 struct ucred temp_cred
;
4180 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4183 NULLCRED_CHECK(cred
);
4185 pcred
= posix_cred_get(cred
);
4188 * We don't need to do anything if the effective, real and saved
4189 * user IDs are already the same as the user ID passed into us.
4191 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4192 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4193 /* no change needed */
4198 * Look up in cred hash table to see if we have a matching credential
4199 * with the new values.
4201 bzero(&temp_cred
, sizeof(temp_cred
));
4202 temp_pcred
->cr_uid
= uid
;
4203 temp_pcred
->cr_ruid
= uid
;
4204 temp_pcred
->cr_svuid
= uid
;
4205 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4206 /* inherit the opt-out of memberd */
4207 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4208 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4209 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4211 temp_pcred
->cr_gmuid
= uid
;
4212 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4214 temp_pcred
->cr_ngroups
= 1;
4215 /* displacing a supplementary group opts us out of memberd */
4216 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4217 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4218 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4220 temp_pcred
->cr_rgid
= gid
;
4221 temp_pcred
->cr_svgid
= gid
;
4223 temp_cred
.cr_label
= cred
->cr_label
;
4226 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4231 * kauth_cred_setsvuidgid
4233 * Description: Function used by execve to set the saved uid and gid values
4234 * for suid/sgid programs
4236 * Parameters: cred The credential to update
4237 * uid The saved uid to set
4238 * gid The saved gid to set
4240 * Returns: (kauth_cred_t) The updated credential
4242 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4243 * if it returns a credential other than the one it is passed,
4244 * will have dropped the reference on the passed credential. All
4245 * callers should be aware of this, and treat this function as an
4246 * unref + ref, potentially on different credentials.
4248 * Because of this, the caller is expected to take its own
4249 * reference on the credential passed as the first parameter,
4250 * and be prepared to release the reference on the credential
4251 * that is returned to them, if it is not intended to be a
4252 * persistent reference.
4255 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4257 struct ucred temp_cred
;
4258 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4261 NULLCRED_CHECK(cred
);
4263 pcred
= posix_cred_get(cred
);
4265 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4268 * We don't need to do anything if the effective, real and saved
4269 * uids are already the same as the uid provided. This check is
4270 * likely insufficient.
4272 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4273 /* no change needed */
4276 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4278 /* look up in cred hash table to see if we have a matching credential
4281 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4282 temp_pcred
->cr_svuid
= uid
;
4283 temp_pcred
->cr_svgid
= gid
;
4285 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4290 * kauth_cred_setauditinfo
4292 * Description: Update the given credential using the given au_session_t.
4294 * Parameters: cred The original credential
4295 * auditinfo_p Pointer to ne audit information
4297 * Returns: (kauth_cred_t) The updated credential
4299 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4300 * if it returns a credential other than the one it is passed,
4301 * will have dropped the reference on the passed credential. All
4302 * callers should be aware of this, and treat this function as an
4303 * unref + ref, potentially on different credentials.
4305 * Because of this, the caller is expected to take its own
4306 * reference on the credential passed as the first parameter,
4307 * and be prepared to release the reference on the credential
4308 * that is returned to them, if it is not intended to be a
4309 * persistent reference.
4312 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4314 struct ucred temp_cred
;
4316 NULLCRED_CHECK(cred
);
4319 * We don't need to do anything if the audit info is already the
4320 * same as the audit info in the credential provided.
4322 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4323 /* no change needed */
4327 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4328 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4330 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
4335 * kauth_cred_label_update
4337 * Description: Update the MAC label associated with a credential
4339 * Parameters: cred The original credential
4340 * label The MAC label to set
4342 * Returns: (kauth_cred_t) The updated credential
4344 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4345 * if it returns a credential other than the one it is passed,
4346 * will have dropped the reference on the passed credential. All
4347 * callers should be aware of this, and treat this function as an
4348 * unref + ref, potentially on different credentials.
4350 * Because of this, the caller is expected to take its own
4351 * reference on the credential passed as the first parameter,
4352 * and be prepared to release the reference on the credential
4353 * that is returned to them, if it is not intended to be a
4354 * persistent reference.
4357 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4359 kauth_cred_t newcred
;
4360 struct ucred temp_cred
;
4362 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4364 mac_cred_label_init(&temp_cred
);
4365 mac_cred_label_associate(cred
, &temp_cred
);
4366 mac_cred_label_update(&temp_cred
, label
);
4368 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4369 mac_cred_label_destroy(&temp_cred
);
4374 * kauth_cred_label_update_execve
4376 * Description: Update the MAC label associated with a credential as
4379 * Parameters: cred The original credential
4381 * scriptl The script MAC label
4382 * execl The executable MAC label
4383 * disjointp Pointer to flag to set if old
4384 * and returned credentials are
4387 * Returns: (kauth_cred_t) The updated credential
4390 * *disjointp Set to 1 for disjoint creds
4392 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4393 * if it returns a credential other than the one it is passed,
4394 * will have dropped the reference on the passed credential. All
4395 * callers should be aware of this, and treat this function as an
4396 * unref + ref, potentially on different credentials.
4398 * Because of this, the caller is expected to take its own
4399 * reference on the credential passed as the first parameter,
4400 * and be prepared to release the reference on the credential
4401 * that is returned to them, if it is not intended to be a
4402 * persistent reference.
4407 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4408 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4409 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4411 kauth_cred_t newcred
;
4412 struct ucred temp_cred
;
4414 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4416 mac_cred_label_init(&temp_cred
);
4417 mac_cred_label_associate(cred
, &temp_cred
);
4418 mac_cred_label_update_execve(ctx
, &temp_cred
,
4419 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4420 macextensions
, disjointp
, labelupdateerror
);
4422 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4423 mac_cred_label_destroy(&temp_cred
);
4428 * kauth_proc_label_update
4430 * Description: Update the label inside the credential associated with the process.
4432 * Parameters: p The process to modify
4433 * label The label to place in the process credential
4435 * Notes: The credential associated with the process may change as a result
4436 * of this call. The caller should not assume the process reference to
4437 * the old credential still exists.
4439 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4441 kauth_cred_t my_cred
, my_new_cred
;
4443 my_cred
= kauth_cred_proc_ref(p
);
4445 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4447 /* get current credential and take a reference while we muck with it */
4451 * Set the credential with new info. If there is no change,
4452 * we get back the same credential we passed in; if there is
4453 * a change, we drop the reference on the credential we
4454 * passed in. The subsequent compare is safe, because it is
4455 * a pointer compare rather than a contents compare.
4457 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4458 if (my_cred
!= my_new_cred
) {
4460 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
);
4464 * We need to protect for a race where another thread
4465 * also changed the credential after we took our
4466 * reference. If p_ucred has changed then we should
4467 * restart this again with the new cred.
4469 if (p
->p_ucred
!= my_cred
) {
4471 kauth_cred_unref(&my_new_cred
);
4472 my_cred
= kauth_cred_proc_ref(p
);
4476 p
->p_ucred
= my_new_cred
;
4477 /* update cred on proc */
4478 PROC_UPDATE_CREDS_ONPROC(p
);
4480 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4485 /* Drop old proc reference or our extra reference */
4486 kauth_cred_unref(&my_cred
);
4492 * kauth_proc_label_update_execve
4494 * Description: Update the label inside the credential associated with the
4495 * process as part of a transitioning execve. The label will
4496 * be updated by the policies as part of this processing, not
4497 * provided up front.
4499 * Parameters: p The process to modify
4500 * ctx The context of the exec
4501 * vp The vnode being exec'ed
4502 * scriptl The script MAC label
4503 * execl The executable MAC label
4504 * lupdateerror The error place holder for MAC label authority
4505 * to update about possible termination
4507 * Returns: 0 Label update did not make credential
4509 * 1 Label update caused credential to be
4512 * Notes: The credential associated with the process WILL change as a
4513 * result of this call. The caller should not assume the process
4514 * reference to the old credential still exists.
4518 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4519 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4520 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4522 kauth_cred_t my_cred
, my_new_cred
;
4523 my_cred
= kauth_cred_proc_ref(p
);
4525 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4527 /* get current credential and take a reference while we muck with it */
4531 * Set the credential with new info. If there is no change,
4532 * we get back the same credential we passed in; if there is
4533 * a change, we drop the reference on the credential we
4534 * passed in. The subsequent compare is safe, because it is
4535 * a pointer compare rather than a contents compare.
4537 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4538 if (my_cred
!= my_new_cred
) {
4540 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
);
4544 * We need to protect for a race where another thread
4545 * also changed the credential after we took our
4546 * reference. If p_ucred has changed then we should
4547 * restart this again with the new cred.
4549 if (p
->p_ucred
!= my_cred
) {
4551 kauth_cred_unref(&my_new_cred
);
4552 my_cred
= kauth_cred_proc_ref(p
);
4556 p
->p_ucred
= my_new_cred
;
4557 /* update cred on proc */
4558 PROC_UPDATE_CREDS_ONPROC(p
);
4559 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4564 /* Drop old proc reference or our extra reference */
4565 kauth_cred_unref(&my_cred
);
4570 * for temporary binary compatibility
4572 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4574 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4576 return kauth_cred_label_update(cred
, label
);
4579 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4581 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4583 return kauth_proc_label_update(p
, label
);
4589 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4590 * Since we cannot build our export lists based on the kernel configuration we need
4594 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4600 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4607 * for temporary binary compatibility
4609 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4611 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4616 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4618 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4628 * Description: Add a reference to the passed credential
4630 * Parameters: cred The credential to reference
4634 * Notes: This function adds a reference to the provided credential;
4635 * the existing reference on the credential is assumed to be
4636 * held stable over this operation by taking the appropriate
4637 * lock to protect the pointer from which it is being referenced,
4638 * if necessary (e.g. the proc lock is held over the call if the
4639 * credential being referenced is from p_ucred, the vnode lock
4640 * if from the per vnode name cache cred cache, and so on).
4642 * This is safe from the kauth_cred_unref() path, since an atomic
4643 * add is used, and the unref path specifically checks to see that
4644 * the value has not been changed to add a reference between the
4645 * time the credential is unreferenced by another pointer and the
4646 * time it is unreferenced from the cred hash cache.
4649 kauth_cred_ref(kauth_cred_t cred
)
4653 NULLCRED_CHECK(cred
);
4655 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4658 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4660 #if 0 // use this to watch a specific credential
4661 if ( is_target_cred( cred
) != 0 ) {
4671 * kauth_cred_unref_hashlocked
4673 * Description: release a credential reference; when the last reference is
4674 * released, the credential will be freed.
4676 * Parameters: credp Pointer to address containing
4677 * credential to be freed
4679 * Returns: TRUE if the credential must be destroyed by the caller.
4683 * *credp Set to NOCRED
4685 * Notes: This function assumes the credential hash lock is held.
4687 * This function is internal use only, since the hash lock is
4688 * scoped to this compilation unit.
4690 * This function destroys the contents of the pointer passed by
4691 * the caller to prevent the caller accidentally attempting to
4692 * release a given reference twice in error.
4694 * The last reference is considered to be released when a release
4695 * of a credential of a reference count of 2 occurs; this is an
4696 * intended effect, to take into account the reference held by
4697 * the credential hash, which is released at the same time.
4700 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4703 boolean_t destroy_it
= FALSE
;
4705 KAUTH_CRED_HASH_LOCK_ASSERT();
4706 NULLCRED_CHECK(*credp
);
4708 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4712 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4714 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4717 #if 0 // use this to watch a specific credential
4718 if ( is_target_cred( *credp
) != 0 ) {
4724 * If the old_value is 2, then we have just released the last external
4725 * reference to this credential
4727 if (old_value
< 3) {
4728 /* The last absolute reference is our credential hash table */
4729 destroy_it
= kauth_cred_remove(*credp
);
4732 if (destroy_it
== FALSE
) {
4736 return (destroy_it
);
4743 * Description: Release a credential reference while holding the credential
4744 * hash lock; when the last reference is released, the credential
4747 * Parameters: credp Pointer to address containing
4748 * credential to be freed
4753 * *credp Set to NOCRED
4755 * Notes: See kauth_cred_unref_hashlocked() for more information.
4759 kauth_cred_unref(kauth_cred_t
*credp
)
4761 boolean_t destroy_it
;
4763 KAUTH_CRED_HASH_LOCK();
4764 destroy_it
= kauth_cred_unref_hashlocked(credp
);
4765 KAUTH_CRED_HASH_UNLOCK();
4767 if (destroy_it
== TRUE
) {
4768 assert(*credp
!= NOCRED
);
4770 mac_cred_label_destroy(*credp
);
4772 AUDIT_SESSION_UNREF(*credp
);
4774 (*credp
)->cr_ref
= 0;
4775 FREE_ZONE(*credp
, sizeof(*(*credp
)), M_CRED
);
4785 * Description: release a credential reference; when the last reference is
4786 * released, the credential will be freed
4788 * Parameters: cred Credential to release
4792 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4793 * clear the pointer in the caller to avoid multiple releases of
4794 * the same credential. The currently recommended interface is
4795 * kauth_cred_unref().
4798 kauth_cred_rele(kauth_cred_t cred
)
4800 kauth_cred_unref(&cred
);
4802 #endif /* !__LP64__ */
4808 * Description: Duplicate a credential via alloc and copy; the new credential
4811 * Parameters: cred The credential to duplicate
4813 * Returns: (kauth_cred_t) The duplicate credential
4815 * Notes: The typical value to calling this routine is if you are going
4816 * to modify an existing credential, and expect to need a new one
4817 * from the hash cache.
4819 * This should probably not be used in the majority of cases;
4820 * if you are using it instead of kauth_cred_create(), you are
4821 * likely making a mistake.
4823 * The newly allocated credential is copied as part of the
4824 * allocation process, with the exception of the reference
4825 * count, which is set to 1 to indicate a single reference
4826 * held by the caller.
4828 * Since newly allocated credentials have no external pointers
4829 * referencing them, prior to making them visible in an externally
4830 * visible pointer (e.g. by adding them to the credential hash
4831 * cache) is the only legal time in which an existing credential
4832 * can be safely initialized or modified directly.
4834 * After initialization, the caller is expected to call the
4835 * function kauth_cred_add() to add the credential to the hash
4836 * cache, after which time it's frozen and becomes publicly
4839 * The release protocol depends on kauth_hash_add() being called
4840 * before kauth_cred_rele() (there is a diagnostic panic which
4841 * will trigger if this protocol is not observed).
4845 kauth_cred_dup(kauth_cred_t cred
)
4847 kauth_cred_t newcred
;
4849 struct label
*temp_label
;
4853 if (cred
== NOCRED
|| cred
== FSCRED
)
4854 panic("kauth_cred_dup: bad credential");
4856 newcred
= kauth_cred_alloc();
4857 if (newcred
!= NULL
) {
4859 temp_label
= newcred
->cr_label
;
4861 bcopy(cred
, newcred
, sizeof(*newcred
));
4863 newcred
->cr_label
= temp_label
;
4864 mac_cred_label_associate(cred
, newcred
);
4866 AUDIT_SESSION_REF(cred
);
4867 newcred
->cr_ref
= 1;
4873 * kauth_cred_copy_real
4875 * Description: Returns a credential based on the passed credential but which
4876 * reflects the real rather than effective UID and GID.
4878 * Parameters: cred The credential from which to
4879 * derive the new credential
4881 * Returns: (kauth_cred_t) The copied credential
4883 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4884 * result, the caller is responsible for dropping BOTH the
4885 * additional reference on the passed cred (if any), and the
4886 * credential returned by this function. The drop should be
4887 * via the kauth_cred_unref() KPI.
4890 kauth_cred_copy_real(kauth_cred_t cred
)
4892 kauth_cred_t newcred
= NULL
, found_cred
;
4893 struct ucred temp_cred
;
4894 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4895 posix_cred_t pcred
= posix_cred_get(cred
);
4897 /* if the credential is already 'real', just take a reference */
4898 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4899 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4900 kauth_cred_ref(cred
);
4905 * Look up in cred hash table to see if we have a matching credential
4906 * with the new values.
4908 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4909 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4910 /* displacing a supplementary group opts us out of memberd */
4911 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4912 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4913 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4916 * If the cred is not opted out, make sure we are using the r/euid
4919 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4920 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4925 KAUTH_CRED_HASH_LOCK();
4926 found_cred
= kauth_cred_find(&temp_cred
);
4927 if (found_cred
== cred
) {
4928 /* same cred so just bail */
4929 KAUTH_CRED_HASH_UNLOCK();
4932 if (found_cred
!= NULL
) {
4934 * Found a match so we bump reference count on new
4935 * one. We leave the old one alone.
4937 kauth_cred_ref(found_cred
);
4938 KAUTH_CRED_HASH_UNLOCK();
4943 * Must allocate a new credential, copy in old credential
4944 * data and update the real user and group IDs.
4946 newcred
= kauth_cred_dup(&temp_cred
);
4947 err
= kauth_cred_add(newcred
);
4948 KAUTH_CRED_HASH_UNLOCK();
4950 /* Retry if kauth_cred_add() fails */
4954 mac_cred_label_destroy(newcred
);
4956 AUDIT_SESSION_UNREF(newcred
);
4958 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4969 * Description: Common code to update a credential
4971 * Parameters: old_cred Reference counted credential
4973 * model_cred Non-reference counted model
4974 * credential to apply to the
4975 * credential to be updated
4976 * retain_auditinfo Flag as to whether or not the
4977 * audit information should be
4978 * copied from the old_cred into
4981 * Returns: (kauth_cred_t) The updated credential
4983 * IMPORTANT: This function will potentially return a credential other than
4984 * the one it is passed, and if so, it will have dropped the
4985 * reference on the passed credential. All callers should be
4986 * aware of this, and treat this function as an unref + ref,
4987 * potentially on different credentials.
4989 * Because of this, the caller is expected to take its own
4990 * reference on the credential passed as the first parameter,
4991 * and be prepared to release the reference on the credential
4992 * that is returned to them, if it is not intended to be a
4993 * persistent reference.
4996 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4997 boolean_t retain_auditinfo
)
4999 kauth_cred_t found_cred
, new_cred
= NULL
;
5002 * Make sure we carry the auditinfo forward to the new credential
5003 * unless we are actually updating the auditinfo.
5005 if (retain_auditinfo
) {
5006 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
5007 sizeof(model_cred
->cr_audit
));
5013 KAUTH_CRED_HASH_LOCK();
5014 found_cred
= kauth_cred_find(model_cred
);
5015 if (found_cred
== old_cred
) {
5016 /* same cred so just bail */
5017 KAUTH_CRED_HASH_UNLOCK();
5020 if (found_cred
!= NULL
) {
5021 boolean_t destroy_it
;
5023 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
5025 * Found a match so we bump reference count on new
5026 * one and decrement reference count on the old one.
5028 kauth_cred_ref(found_cred
);
5029 destroy_it
= kauth_cred_unref_hashlocked(&old_cred
);
5030 KAUTH_CRED_HASH_UNLOCK();
5031 if (destroy_it
== TRUE
) {
5032 assert(old_cred
!= NOCRED
);
5034 mac_cred_label_destroy(old_cred
);
5036 AUDIT_SESSION_UNREF(old_cred
);
5038 old_cred
->cr_ref
= 0;
5039 FREE_ZONE(old_cred
, sizeof(*old_cred
), M_CRED
);
5047 * Must allocate a new credential using the model. also
5048 * adds the new credential to the credential hash table.
5050 new_cred
= kauth_cred_dup(model_cred
);
5051 err
= kauth_cred_add(new_cred
);
5052 KAUTH_CRED_HASH_UNLOCK();
5054 /* retry if kauth_cred_add returns non zero value */
5058 mac_cred_label_destroy(new_cred
);
5060 AUDIT_SESSION_UNREF(new_cred
);
5062 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
5066 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
5067 kauth_cred_unref(&old_cred
);
5075 * Description: Add the given credential to our credential hash table and
5076 * take an additional reference to account for our use of the
5077 * credential in the hash table
5079 * Parameters: new_cred Credential to insert into cred
5082 * Returns: 0 Success
5083 * -1 Hash insertion failed: caller
5086 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5088 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5091 kauth_cred_add(kauth_cred_t new_cred
)
5095 KAUTH_CRED_HASH_LOCK_ASSERT();
5097 hash_key
= kauth_cred_get_hashkey(new_cred
);
5098 hash_key
%= kauth_cred_table_size
;
5100 /* race fix - there is a window where another matching credential
5101 * could have been inserted between the time this one was created and we
5102 * got the hash lock. If we find a match return an error and have the
5105 if (kauth_cred_find(new_cred
) != NULL
) {
5109 /* take a reference for our use in credential hash table */
5110 kauth_cred_ref(new_cred
);
5112 /* insert the credential into the hash table */
5113 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
5122 * Description: Remove the given credential from our credential hash table
5124 * Parameters: cred Credential to remove from cred
5127 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not.
5129 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5131 * Notes: The check for the reference increment after entry is generally
5132 * agree to be safe, since we use atomic operations, and the
5133 * following code occurs with the hash lock held; in theory, this
5134 * protects us from the 2->1 reference that gets us here.
5137 kauth_cred_remove(kauth_cred_t cred
)
5140 kauth_cred_t found_cred
;
5142 hash_key
= kauth_cred_get_hashkey(cred
);
5143 hash_key
%= kauth_cred_table_size
;
5146 if (cred
->cr_ref
< 1)
5147 panic("cred reference underflow");
5148 if (cred
->cr_ref
> 1)
5149 return (FALSE
); /* someone else got a ref */
5151 /* Find cred in the credential hash table */
5152 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5153 if (found_cred
== cred
) {
5154 /* found a match, remove it from the hash table */
5155 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
5156 #if KAUTH_CRED_HASH_DEBUG
5163 /* Did not find a match... this should not happen! XXX Make panic? */
5164 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
5172 * Description: Using the given credential data, look for a match in our
5173 * credential hash table
5175 * Parameters: cred Credential to lookup in cred
5178 * Returns: NULL Not found
5179 * !NULL Matching credential already in
5182 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5185 kauth_cred_find(kauth_cred_t cred
)
5188 kauth_cred_t found_cred
;
5189 posix_cred_t pcred
= posix_cred_get(cred
);
5191 KAUTH_CRED_HASH_LOCK_ASSERT();
5193 #if KAUTH_CRED_HASH_DEBUG
5194 static int test_count
= 0;
5197 if ((test_count
% 200) == 0) {
5198 kauth_cred_hash_print();
5202 hash_key
= kauth_cred_get_hashkey(cred
);
5203 hash_key
%= kauth_cred_table_size
;
5205 /* Find cred in the credential hash table */
5206 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5208 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
5211 * don't worry about the label unless the flags in
5212 * either credential tell us to.
5214 match
= (bcmp(found_pcred
, pcred
, sizeof (*pcred
)) == 0) ? TRUE
: FALSE
;
5215 match
= match
&& ((bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
5216 sizeof(cred
->cr_audit
)) == 0) ? TRUE
: FALSE
);
5218 if (((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) ||
5219 ((pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0)) {
5220 match
= match
&& mac_cred_label_compare(found_cred
->cr_label
,
5229 /* No match found */
5238 * Description: Generates a hash key using data that makes up a credential;
5241 * Parameters: datap Pointer to data to hash
5242 * data_len Count of bytes to hash
5243 * start_key Start key value
5245 * Returns: (u_long) Returned hash key
5247 static inline u_long
5248 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5250 u_long hash_key
= start_key
;
5253 while (data_len
> 0) {
5254 hash_key
= (hash_key
<< 4) + *datap
++;
5255 temp
= hash_key
& 0xF0000000;
5257 hash_key
^= temp
>> 24;
5267 * kauth_cred_get_hashkey
5269 * Description: Generate a hash key using data that makes up a credential;
5270 * based on ElfHash. We hash on the entire credential data,
5271 * not including the ref count or the TAILQ, which are mutable;
5272 * everything else isn't.
5274 * Parameters: cred Credential for which hash is
5277 * Returns: (u_long) Returned hash key
5279 * Notes: When actually moving the POSIX credential into a real label,
5280 * remember to update this hash computation.
5283 kauth_cred_get_hashkey(kauth_cred_t cred
)
5286 posix_cred_t pcred
= posix_cred_get(cred
);
5288 u_long hash_key
= 0;
5290 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5291 sizeof (struct posix_cred
),
5293 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5294 sizeof(struct au_session
),
5297 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5298 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5299 sizeof (struct label
),
5307 #if KAUTH_CRED_HASH_DEBUG
5309 * kauth_cred_hash_print
5311 * Description: Print out cred hash cache table information for debugging
5312 * purposes, including the credential contents
5314 * Parameters: (void)
5318 * Implicit returns: Results in console output
5321 kauth_cred_hash_print(void)
5324 kauth_cred_t found_cred
;
5326 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
5327 /* count slot hits, misses, collisions, and max depth */
5328 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5329 printf("[%02d] ", i
);
5331 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5336 kauth_cred_print(found_cred
);
5340 printf("NOCRED \n");
5344 #endif /* KAUTH_CRED_HASH_DEBUG */
5347 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5351 * Description: Print out an individual credential's contents for debugging
5354 * Parameters: cred The credential to print out
5358 * Implicit returns: Results in console output
5361 kauth_cred_print(kauth_cred_t cred
)
5365 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
);
5366 printf("group count %d gids ", cred
->cr_ngroups
);
5367 for (i
= 0; i
< NGROUPS
; i
++) {
5370 printf("%d ", cred
->cr_groups
[i
]);
5372 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5373 printf("auditinfo_addr %d %d %d %d %d %d\n",
5374 cred
->cr_audit
.s_aia_p
->ai_auid
,
5375 cred
->cr_audit
.as_mask
.am_success
,
5376 cred
->cr_audit
.as_mask
.am_failure
,
5377 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5378 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5379 cred
->cr_audit
.as_aia_p
->ai_asid
);
5382 int is_target_cred( kauth_cred_t the_cred
)
5384 if ( the_cred
->cr_uid
!= 0 )
5386 if ( the_cred
->cr_ruid
!= 0 )
5388 if ( the_cred
->cr_svuid
!= 0 )
5390 if ( the_cred
->cr_ngroups
!= 11 )
5392 if ( the_cred
->cr_groups
[0] != 11 )
5394 if ( the_cred
->cr_groups
[1] != 81 )
5396 if ( the_cred
->cr_groups
[2] != 63947 )
5398 if ( the_cred
->cr_groups
[3] != 80288 )
5400 if ( the_cred
->cr_groups
[4] != 89006 )
5402 if ( the_cred
->cr_groups
[5] != 52173 )
5404 if ( the_cred
->cr_groups
[6] != 84524 )
5406 if ( the_cred
->cr_groups
[7] != 79 )
5408 if ( the_cred
->cr_groups
[8] != 80292 )
5410 if ( the_cred
->cr_groups
[9] != 80 )
5412 if ( the_cred
->cr_groups
[10] != 90824 )
5414 if ( the_cred
->cr_rgid
!= 11 )
5416 if ( the_cred
->cr_svgid
!= 11 )
5418 if ( the_cred
->cr_gmuid
!= 3475 )
5420 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5423 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5425 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5427 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5429 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5431 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5433 if ( the_cred->cr_flags != 0 )
5436 return( -1 ); // found target cred
5439 void get_backtrace( void )
5442 void * my_stack
[ MAX_STACK_DEPTH
];
5445 if ( cred_debug_buf_p
== NULL
) {
5446 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5447 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5450 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5451 /* buffer is full */
5455 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5456 if ( my_depth
== 0 ) {
5457 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5461 /* fill new backtrace */
5462 my_slot
= cred_debug_buf_p
->next_slot
;
5463 cred_debug_buf_p
->next_slot
++;
5464 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5465 for ( i
= 0; i
< my_depth
; i
++ ) {
5466 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5473 /* subset of struct ucred for use in sysctl_dump_creds */
5474 struct debug_ucred
{
5476 u_long cr_ref
; /* reference count */
5477 uid_t cr_uid
; /* effective user id */
5478 uid_t cr_ruid
; /* real user id */
5479 uid_t cr_svuid
; /* saved user id */
5480 short cr_ngroups
; /* number of groups in advisory list */
5481 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5482 gid_t cr_rgid
; /* real group id */
5483 gid_t cr_svgid
; /* saved group id */
5484 uid_t cr_gmuid
; /* UID for group membership purposes */
5485 struct auditinfo_addr cr_audit
; /* user auditing data. */
5486 void *cr_label
; /* MACF label */
5487 int cr_flags
; /* flags on credential */
5489 typedef struct debug_ucred debug_ucred
;
5491 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5492 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5495 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5499 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5501 int i
, j
, counter
= 0;
5504 kauth_cred_t found_cred
;
5505 debug_ucred
* cred_listp
;
5506 debug_ucred
* nextp
;
5508 /* This is a readonly node. */
5509 if (req
->newptr
!= USER_ADDR_NULL
)
5512 /* calculate space needed */
5513 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5514 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5519 /* they are querying us so just return the space required. */
5520 if (req
->oldptr
== USER_ADDR_NULL
) {
5521 counter
+= 10; // add in some padding;
5522 req
->oldidx
= counter
* sizeof(debug_ucred
);
5526 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5527 if ( cred_listp
== NULL
) {
5531 /* fill in creds to send back */
5534 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5535 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5536 nextp
->credp
= found_cred
;
5537 nextp
->cr_ref
= found_cred
->cr_ref
;
5538 nextp
->cr_uid
= found_cred
->cr_uid
;
5539 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5540 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5541 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5542 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5543 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5545 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5546 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5547 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5548 nextp
->cr_audit
.ai_auid
=
5549 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5550 nextp
->cr_audit
.ai_mask
.am_success
=
5551 found_cred
->cr_audit
.as_mask
.am_success
;
5552 nextp
->cr_audit
.ai_mask
.am_failure
=
5553 found_cred
->cr_audit
.as_mask
.am_failure
;
5554 nextp
->cr_audit
.ai_termid
.at_port
=
5555 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5556 nextp
->cr_audit
.ai_termid
.at_type
=
5557 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5558 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5559 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5560 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5561 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5562 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5563 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5564 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5565 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5566 nextp
->cr_audit
.ai_asid
=
5567 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5568 nextp
->cr_audit
.ai_flags
=
5569 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5570 nextp
->cr_label
= found_cred
->cr_label
;
5571 nextp
->cr_flags
= found_cred
->cr_flags
;
5573 space
+= sizeof(debug_ucred
);
5574 if ( space
> req
->oldlen
) {
5575 FREE(cred_listp
, M_TEMP
);
5580 req
->oldlen
= space
;
5581 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5582 FREE(cred_listp
, M_TEMP
);
5587 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5588 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5591 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5595 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5600 cred_debug_buffer
* bt_bufp
;
5601 cred_backtrace
* nextp
;
5603 /* This is a readonly node. */
5604 if (req
->newptr
!= USER_ADDR_NULL
)
5607 if ( cred_debug_buf_p
== NULL
) {
5611 /* calculate space needed */
5612 space
= sizeof( cred_debug_buf_p
->next_slot
);
5613 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5615 /* they are querying us so just return the space required. */
5616 if (req
->oldptr
== USER_ADDR_NULL
) {
5617 req
->oldidx
= space
;
5621 if ( space
> req
->oldlen
) {
5625 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5626 if ( bt_bufp
== NULL
) {
5630 /* fill in backtrace info to send back */
5631 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5632 space
= sizeof(bt_bufp
->next_slot
);
5634 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5635 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5636 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5637 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5638 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5640 space
+= sizeof(*nextp
);
5643 req
->oldlen
= space
;
5644 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5645 FREE(bt_bufp
, M_TEMP
);
5649 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5653 **********************************************************************
5654 * The following routines will be moved to a policy_posix.c module at
5655 * some future point.
5656 **********************************************************************
5662 * Description: Helper function to create a kauth_cred_t credential that is
5663 * initally labelled with a specific POSIX credential label
5665 * Parameters: pcred The posix_cred_t to use as the initial
5668 * Returns: (kauth_cred_t) The credential that was found in the
5670 * NULL kauth_cred_add() failed, or there was
5671 * no egid specified, or we failed to
5672 * attach a label to the new credential
5674 * Notes: This function currently wraps kauth_cred_create(), and is the
5675 * only consumer of that ill-fated function, apart from bsd_init().
5676 * It exists solely to support the NFS server code creation of
5677 * credentials based on the over-the-wire RPC calls containing
5678 * traditional POSIX credential information being tunneled to
5679 * the server host from the client machine.
5681 * In the future, we hope this function goes away.
5683 * In the short term, it creates a temporary credential, puts
5684 * the POSIX information from NFS into it, and then calls
5685 * kauth_cred_create(), as an internal implementation detail.
5687 * If we have to keep it around in the medium term, it will
5688 * create a new kauth_cred_t, then label it with a POSIX label
5689 * corresponding to the contents of the kauth_cred_t. If the
5690 * policy_posix MACF module is not loaded, it will instead
5691 * substitute a posix_cred_t which GRANTS all access (effectively
5692 * a "root" credential) in order to not prevent NFS from working
5693 * in the case that we are not supporting POSIX credentials.
5696 posix_cred_create(posix_cred_t pcred
)
5698 struct ucred temp_cred
;
5700 bzero(&temp_cred
, sizeof(temp_cred
));
5701 temp_cred
.cr_posix
= *pcred
;
5703 return kauth_cred_create(&temp_cred
);
5710 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5711 * any, which is associated with it.
5713 * Parameters: cred The credential to obtain the label from
5715 * Returns: posix_cred_t The POSIX credential label
5717 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5718 * this function will return a pointer to a posix_cred_t which
5719 * GRANTS all access (effectively, a "root" credential). This is
5720 * necessary to support legacy code which insists on tightly
5721 * integrating POSIX credentials into its APIs, including, but
5722 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5723 * NFSv3, signals, dtrace, and a large number of kauth routines
5724 * used to implement POSIX permissions related system calls.
5726 * In the event that the policy_posix MACF module IS loaded, and
5727 * there is no POSIX label on the kauth_cred_t credential, this
5728 * function will return a pointer to a posix_cred_t which DENIES
5729 * all access (effectively, a "deny rights granted by POSIX"
5730 * credential). This is necessary to support the concept of a
5731 * transiently loaded POSIX policy, or kauth_cred_t credentials
5732 * which can not be used in conjunctions with POSIX permissions
5735 * This function currently returns the address of the cr_posix
5736 * field of the supplied kauth_cred_t credential, and as such
5737 * currently can not fail. In the future, this will not be the
5741 posix_cred_get(kauth_cred_t cred
)
5743 return(&cred
->cr_posix
);
5750 * Description: Label a kauth_cred_t with a POSIX credential label
5752 * Parameters: cred The credential to label
5753 * pcred The POSIX credential t label it with
5757 * Notes: This function is currently void in order to permit it to fit
5758 * in with the current MACF framework label methods which allow
5759 * labeling to fail silently. This is like acceptable for
5760 * mandatory access controls, but not for POSIX, since those
5761 * access controls are advisory. We will need to consider a
5762 * return value in a future version of the MACF API.
5764 * This operation currently cannot fail, as currently the POSIX
5765 * credential is a subfield of the kauth_cred_t (ucred), which
5766 * MUST be valid. In the future, this will not be the case.
5769 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5771 cred
->cr_posix
= *pcred
; /* structure assign for now */
5778 * Description: Perform a POSIX access check for a protected object
5780 * Parameters: cred The credential to check
5781 * object_uid The POSIX UID of the protected object
5782 * object_gid The POSIX GID of the protected object
5783 * object_mode The POSIX mode of the protected object
5784 * mode_req The requested POSIX access rights
5786 * Returns 0 Access is granted
5787 * EACCES Access is denied
5789 * Notes: This code optimizes the case where the world and group rights
5790 * would both grant the requested rights to avoid making a group
5791 * membership query. This is a big performance win in the case
5792 * where this is true.
5795 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5798 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5799 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5800 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5803 * Check first for owner rights
5805 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5809 * Combined group and world rights check, if we don't have owner rights
5811 * OPTIMIZED: If group and world rights would grant the same bits, and
5812 * they set of requested bits is in both, then we can simply check the
5813 * world rights, avoiding a group membership check, which is expensive.
5815 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5819 * NON-OPTIMIZED: requires group membership check.
5821 if ((mode_req
& mode_group
) != mode_req
) {
5823 * exclusion group : treat errors as "is a member"
5825 * NON-OPTIMIZED: +group would deny; must check group
5827 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5829 * DENY: +group denies
5833 if ((mode_req
& mode_world
) != mode_req
) {
5835 * DENY: both -group & world would deny
5840 * ALLOW: allowed by -group and +world
5847 * inclusion group; treat errors as "not a member"
5849 * NON-OPTIMIZED: +group allows, world denies; must
5852 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5854 * ALLOW: allowed by +group
5858 if ((mode_req
& mode_world
) != mode_req
) {
5860 * DENY: both -group & world would deny
5865 * ALLOW: allowed by -group and +world