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 #define KAUTH_CRED_TABLE_SIZE 97
269 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
270 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
272 #define KAUTH_CRED_HASH_DEBUG 0
274 static int kauth_cred_add(kauth_cred_t new_cred
);
275 static boolean_t
kauth_cred_remove(kauth_cred_t cred
);
276 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
277 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
278 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
279 static boolean_t
kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
281 #if KAUTH_CRED_HASH_DEBUG
282 static int kauth_cred_count
= 0;
283 static void kauth_cred_hash_print(void);
284 static void kauth_cred_print(kauth_cred_t cred
);
287 #if CONFIG_EXT_RESOLVER
290 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
292 * Description: Waits for the user space daemon to respond to the request
293 * we made. Function declared non inline to be visible in
294 * stackshots and spindumps as well as debugging.
296 * Parameters: workp Work queue entry.
298 * Returns: 0 on Success.
299 * EIO if Resolver is dead.
300 * EINTR thread interrupted in msleep
301 * EWOULDBLOCK thread timed out in msleep
302 * ERESTART returned by msleep.
305 static __attribute__((noinline
)) int
306 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
307 struct kauth_resolver_work
*workp
)
312 /* we could compute a better timeout here */
313 ts
.tv_sec
= kauth_resolver_timeout
;
315 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
316 /* request has been completed? */
317 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
319 /* woken because the resolver has died? */
320 if (kauth_resolver_identity
== 0) {
333 * kauth_resolver_init
335 * Description: Initialize the daemon side of the credential identity resolver
341 * Notes: Initialize the credential identity resolver for use; the
342 * credential identity resolver is the KPI used by the user
343 * space credential identity resolver daemon to communicate
344 * with the kernel via the identitysvc() system call..
346 * This is how membership in more than 16 groups (1 effective
347 * and 15 supplementary) is supported, and also how UID's,
348 * UUID's, and so on, are translated to/from POSIX credential
351 * The credential identity resolver operates by attempting to
352 * determine identity first from the credential, then from
353 * the kernel credential identity cache, and finally by
354 * enqueueing a request to a user space daemon.
356 * This function is called from kauth_init() in the file
357 * kern_authorization.c.
360 kauth_resolver_init(void)
362 TAILQ_INIT(&kauth_resolver_unsubmitted
);
363 TAILQ_INIT(&kauth_resolver_submitted
);
364 TAILQ_INIT(&kauth_resolver_done
);
365 kauth_resolver_sequence
= 31337;
366 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
371 * kauth_resolver_submit
373 * Description: Submit an external credential identity resolution request to
374 * the user space daemon.
376 * Parameters: lkp A pointer to an external
378 * extend_data extended data for kr_extend
381 * EWOULDBLOCK No resolver registered
382 * EINTR Operation interrupted (e.g. by
384 * ENOMEM Could not allocate work item
385 * copyinstr:EFAULT Bad message from user space
386 * workp->kr_result:??? An error from the user space
387 * daemon (includes ENOENT!)
392 * Notes: Allocate a work queue entry, submit the work and wait for
393 * the operation to either complete or time out. Outstanding
394 * operations may also be cancelled.
396 * Submission is by means of placing the item on a work queue
397 * which is serviced by an external resolver thread calling
398 * into the kernel. The caller then sleeps until timeout,
399 * cancellation, or an external resolver thread calls in with
400 * a result message to kauth_resolver_complete(). All of these
401 * events wake the caller back up.
403 * This code is called from either kauth_cred_ismember_gid()
404 * for a group membership request, or it is called from
405 * kauth_cred_cache_lookup() when we get a cache miss.
408 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
410 struct kauth_resolver_work
*workp
, *killp
;
412 int error
, shouldfree
;
414 /* no point actually blocking if the resolver isn't up yet */
415 if (kauth_resolver_identity
== 0) {
417 * We've already waited an initial <kauth_resolver_timeout>
418 * seconds with no result.
420 * Sleep on a stack address so no one wakes us before timeout;
421 * we sleep a half a second in case we are a high priority
422 * process, so that memberd doesn't starve while we are in a
423 * tight loop between user and kernel, eating all the CPU.
425 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
426 if (kauth_resolver_identity
== 0) {
428 * if things haven't changed while we were asleep,
429 * tell the caller we couldn't get an authoritative
436 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
440 workp
->kr_work
= *lkp
;
441 workp
->kr_extend
= extend_data
;
443 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
444 workp
->kr_result
= 0;
447 * We insert the request onto the unsubmitted queue, the call in from
448 * the resolver will it to the submitted thread when appropriate.
450 KAUTH_RESOLVER_LOCK();
451 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
452 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
455 * XXX We *MUST NOT* attempt to coalesce identical work items due to
456 * XXX the inability to ensure order of update of the request item
457 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
458 * XXX for each item repeat the update when they wake up.
460 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
463 * Wake up an external resolver thread to deal with the new work; one
464 * may not be available, and if not, then the request will be grabbed
465 * when a resolver thread comes back into the kernel to request new
468 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
469 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
471 /* if the request was processed, copy the result */
473 *lkp
= workp
->kr_work
;
475 if (error
== EWOULDBLOCK
) {
476 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
477 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
478 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
481 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
483 * If the request timed out and was never collected, the resolver
484 * is dead and probably not coming back anytime soon. In this
485 * case we revert to no-resolver behaviour, and punt all the other
486 * sleeping requests to clear the backlog.
488 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
491 * Make the current resolver non-authoritative, and mark it as
492 * no longer registered to prevent kauth_cred_ismember_gid()
493 * enqueueing more work until a new one is registered. This
494 * mitigates the damage a crashing resolver may inflict.
496 kauth_resolver_identity
= 0;
497 kauth_resolver_registered
= 0;
499 /* kill all the other requestes that are waiting as well */
500 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
502 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
504 /* Cause all waiting-for-work threads to return EIO */
505 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
510 * drop our reference on the work item, and note whether we should
513 if (--workp
->kr_refs
<= 0) {
514 /* work out which list we have to remove it from */
515 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
516 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
517 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
518 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
519 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
520 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
522 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
526 /* someone else still has a reference on this request */
530 /* collect request result */
532 error
= workp
->kr_result
;
534 KAUTH_RESOLVER_UNLOCK();
537 * If we dropped the last reference, free the request.
540 FREE(workp
, M_KAUTH
);
543 KAUTH_DEBUG("RESOLVER - returning %d", error
);
551 * Description: System call interface for the external identity resolver.
553 * Parameters: uap->message Message from daemon to kernel
555 * Returns: 0 Successfully became resolver
556 * EPERM Not the resolver process
557 * kauth_authorize_generic:EPERM Not root user
558 * kauth_resolver_complete:EIO
559 * kauth_resolver_complete:EFAULT
560 * kauth_resolver_getwork:EINTR
561 * kauth_resolver_getwork:EFAULT
563 * Notes: This system call blocks until there is work enqueued, at
564 * which time the kernel wakes it up, and a message from the
565 * kernel is copied out to the identity resolution daemon, which
566 * proceed to attempt to resolve it. When the resolution has
567 * completed (successfully or not), the daemon called back into
568 * this system call to give the result to the kernel, and wait
569 * for the next request.
572 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
574 int opcode
= uap
->opcode
;
575 user_addr_t message
= uap
->message
;
576 struct kauth_resolver_work
*workp
;
577 struct kauth_cache_sizes sz_arg
;
582 * New server registering itself.
584 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
585 new_id
= current_proc()->p_pid
;
586 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
587 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
590 KAUTH_RESOLVER_LOCK();
591 if (kauth_resolver_identity
!= new_id
) {
592 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
594 * We have a new server, so assume that all the old requests have been lost.
596 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
597 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
598 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
599 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
600 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
603 * Allow user space resolver to override the
604 * external resolution timeout
606 if (message
> 30 && message
< 10000) {
607 kauth_resolver_timeout
= message
;
608 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
610 kauth_resolver_identity
= new_id
;
611 kauth_resolver_registered
= 1;
612 kauth_identitysvc_has_registered
= 1;
613 wakeup(&kauth_resolver_unsubmitted
);
615 KAUTH_RESOLVER_UNLOCK();
620 * Beyond this point, we must be the resolver process. We verify this
621 * by confirming the resolver credential and pid.
623 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
624 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
628 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
629 KAUTH_IDENTITY_LOCK();
630 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
631 KAUTH_IDENTITY_UNLOCK();
634 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
635 KAUTH_GROUPS_UNLOCK();
637 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof (sz_arg
))) != 0) {
642 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
643 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof (sz_arg
))) != 0) {
647 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
648 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
652 KAUTH_IDENTITY_LOCK();
653 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
654 kauth_identity_trimcache(kauth_identity_cachemax
);
655 KAUTH_IDENTITY_UNLOCK();
658 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
659 kauth_groups_trimcache(kauth_groups_cachemax
);
660 KAUTH_GROUPS_UNLOCK();
663 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
664 KAUTH_IDENTITY_LOCK();
665 kauth_identity_trimcache(0);
666 KAUTH_IDENTITY_UNLOCK();
669 kauth_groups_trimcache(0);
670 KAUTH_GROUPS_UNLOCK();
671 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
673 * Terminate outstanding requests; without an authoritative
674 * resolver, we are now back on our own authority.
676 struct kauth_resolver_work
*killp
;
678 KAUTH_RESOLVER_LOCK();
681 * Clear the identity, but also mark it as unregistered so
682 * there is no explicit future expectation of us getting a
683 * new resolver any time soon.
685 kauth_resolver_identity
= 0;
686 kauth_resolver_registered
= 0;
688 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
690 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
692 /* Cause all waiting-for-work threads to return EIO */
693 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
694 KAUTH_RESOLVER_UNLOCK();
698 * Got a result returning?
700 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
701 if ((error
= kauth_resolver_complete(message
)) != 0)
706 * Caller wants to take more work?
708 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
709 if ((error
= kauth_resolver_getwork(message
)) != 0)
718 * kauth_resolver_getwork_continue
720 * Description: Continuation for kauth_resolver_getwork
722 * Parameters: result Error code or 0 for the sleep
723 * that got us to this function
726 * EINTR Interrupted (e.g. by signal)
727 * kauth_resolver_getwork2:EFAULT
729 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
733 kauth_resolver_getwork_continue(int result
)
740 KAUTH_RESOLVER_UNLOCK();
745 * If we lost a race with another thread/memberd restarting, then we
746 * need to go back to sleep to look for more work. If it was memberd
747 * restarting, then the msleep0() will error out here, as our thread
748 * will already be "dead".
750 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
753 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
755 * If this is a wakeup from another thread in the resolver
756 * deregistering it, error out the request-for-work thread
758 if (!kauth_resolver_identity
)
760 KAUTH_RESOLVER_UNLOCK();
764 thread
= current_thread();
765 ut
= get_bsdthread_info(thread
);
766 message
= ut
->uu_kevent
.uu_kauth
.message
;
767 return(kauth_resolver_getwork2(message
));
772 * kauth_resolver_getwork2
774 * Decription: Common utility function to copy out a identity resolver work
775 * item from the kernel to user space as part of the user space
776 * identity resolver requesting work.
778 * Parameters: message message to user space
781 * EFAULT Bad user space message address
783 * Notes: This common function exists to permit the use of continuations
784 * in the identity resolution process. This frees up the stack
785 * while we are waiting for the user space resolver to complete
786 * a request. This is specifically used so that our per thread
787 * cost can be small, and we will therefore be willing to run a
788 * larger number of threads in the user space identity resolver.
791 kauth_resolver_getwork2(user_addr_t message
)
793 struct kauth_resolver_work
*workp
;
797 * Note: We depend on the caller protecting us from a NULL work item
798 * queue, since we must have the kauth resolver lock on entry to this
801 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
804 * Copy out the external lookup structure for the request, not
805 * including the el_extend field, which contains the address of the
806 * external buffer provided by the external resolver into which we
807 * copy the extension request information.
810 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
811 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
815 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
816 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
817 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
818 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
823 * Handle extended requests here; if we have a request of a type where
824 * the kernel wants a translation of extended information, then we need
825 * to copy it out into the extended buffer, assuming the buffer is
826 * valid; we only attempt to get the buffer address if we have request
827 * data to copy into it.
831 * translate a user@domain string into a uid/gid/whatever
833 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
836 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
838 size_t actual
; /* not used */
840 * Use copyoutstr() to reduce the copy size; we let
841 * this catch a NULL uaddr because we shouldn't be
842 * asking in that case anyway.
844 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
847 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
851 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
852 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
853 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
854 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
857 KAUTH_RESOLVER_UNLOCK();
863 * kauth_resolver_getwork
865 * Description: Get a work item from the enqueued requests from the kernel and
866 * give it to the user space daemon.
868 * Parameters: message message to user space
871 * EINTR Interrupted (e.g. by signal)
872 * kauth_resolver_getwork2:EFAULT
874 * Notes: This function blocks in a continuation if there are no work
875 * items available for processing at the time the user space
876 * identity resolution daemon makes a request for work. This
877 * permits a large number of threads to be used by the daemon,
878 * without using a lot of wired kernel memory when there are no
879 * actual request outstanding.
882 kauth_resolver_getwork(user_addr_t message
)
884 struct kauth_resolver_work
*workp
;
887 KAUTH_RESOLVER_LOCK();
889 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
890 thread_t thread
= current_thread();
891 struct uthread
*ut
= get_bsdthread_info(thread
);
893 ut
->uu_kevent
.uu_kauth
.message
= message
;
894 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
895 KAUTH_RESOLVER_UNLOCK();
897 * If this is a wakeup from another thread in the resolver
898 * deregistering it, error out the request-for-work thread
900 if (!kauth_resolver_identity
)
904 return kauth_resolver_getwork2(message
);
909 * kauth_resolver_complete
911 * Description: Return a result from userspace.
913 * Parameters: message message from user space
916 * EIO The resolver is dead
917 * copyin:EFAULT Bad message from user space
920 kauth_resolver_complete(user_addr_t message
)
922 struct kauth_identity_extlookup extl
;
923 struct kauth_resolver_work
*workp
;
924 struct kauth_resolver_work
*killp
;
925 int error
, result
, request_flags
;
928 * Copy in the mesage, including the extension field, since we are
929 * copying into a local variable.
931 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
932 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
936 KAUTH_RESOLVER_LOCK();
940 switch (extl
.el_result
) {
941 case KAUTH_EXTLOOKUP_INPROG
:
945 /* XXX this should go away once memberd is updated */
947 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
953 case KAUTH_EXTLOOKUP_SUCCESS
:
956 case KAUTH_EXTLOOKUP_FATAL
:
957 /* fatal error means the resolver is dead */
958 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
960 * Terminate outstanding requests; without an authoritative
961 * resolver, we are now back on our own authority. Tag the
962 * resolver unregistered to prevent kauth_cred_ismember_gid()
963 * enqueueing more work until a new one is registered. This
964 * mitigates the damage a crashing resolver may inflict.
966 kauth_resolver_identity
= 0;
967 kauth_resolver_registered
= 0;
969 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
971 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
973 /* Cause all waiting-for-work threads to return EIO */
974 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
975 /* and return EIO to the caller */
979 case KAUTH_EXTLOOKUP_BADRQ
:
980 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
984 case KAUTH_EXTLOOKUP_FAILURE
:
985 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
990 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
996 * In the case of a fatal error, we assume that the resolver will
997 * restart quickly and re-collect all of the outstanding requests.
998 * Thus, we don't complete the request which returned the fatal
1001 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1002 /* scan our list for this request */
1003 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1005 if (workp
->kr_seqno
== extl
.el_seqno
) {
1007 * Take a snapshot of the original request flags.
1009 request_flags
= workp
->kr_work
.el_flags
;
1012 * Get the request of the submitted queue so
1013 * that it is not cleaned up out from under
1016 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1017 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1018 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1019 workp
->kr_result
= result
;
1021 /* Copy the result message to the work item. */
1022 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1025 * Check if we have a result in the extension
1026 * field; if we do, then we need to separately
1027 * copy the data from the message el_extend
1028 * into the request buffer that's in the work
1029 * item. We have to do it here because we do
1030 * not want to wake up the waiter until the
1031 * data is in their buffer, and because the
1032 * actual request response may be destroyed
1033 * by the time the requester wakes up, and they
1034 * do not have access to the user space buffer
1037 * It is safe to drop and reacquire the lock
1038 * here because we've already removed the item
1039 * from the submission queue, but have not yet
1040 * moved it to the completion queue. Note that
1041 * near simultaneous requests may result in
1042 * duplication of requests for items in this
1043 * window. This should not be a performance
1044 * issue and is easily detectable by comparing
1045 * time to live on last response vs. time of
1046 * next request in the resolver logs.
1048 * A malicious/faulty resolver could overwrite
1049 * part of a user's address space if they return
1050 * flags that mismatch the original request's flags.
1052 if ((extl
.el_flags
& request_flags
) & (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1053 size_t actual
; /* notused */
1055 KAUTH_RESOLVER_UNLOCK();
1056 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1057 KAUTH_RESOLVER_LOCK();
1058 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1060 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1061 extl
.el_flags
, request_flags
);
1065 * Move the completed work item to the
1066 * completion queue and wake up requester(s)
1068 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1075 * Note that it's OK for us not to find anything; if the request has
1076 * timed out the work record will be gone.
1078 KAUTH_RESOLVER_UNLOCK();
1082 #endif /* CONFIG_EXT_RESOLVER */
1089 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1090 #define KI_VALID_GID (1<<1)
1091 #define KI_VALID_GUID (1<<2)
1092 #define KI_VALID_NTSID (1<<3)
1093 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1094 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1095 #define KI_VALID_GROUPS (1<<6)
1097 #if CONFIG_EXT_RESOLVER
1099 * kauth_identity_init
1101 * Description: Initialize the kernel side of the credential identity resolver
1103 * Parameters: (void)
1107 * Notes: Initialize the credential identity resolver for use; the
1108 * credential identity resolver is the KPI used to communicate
1109 * with a user space credential identity resolver daemon.
1111 * This function is called from kauth_init() in the file
1112 * kern_authorization.c.
1115 kauth_identity_init(void)
1117 TAILQ_INIT(&kauth_identities
);
1118 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1123 * kauth_identity_alloc
1125 * Description: Allocate and fill out a kauth_identity structure for
1126 * translation between {UID|GID}/GUID/NTSID
1130 * Returns: NULL Insufficient memory to satisfy
1131 * the request or bad parameters
1132 * !NULL A pointer to the allocated
1133 * structure, filled in
1135 * Notes: It is illegal to translate between UID and GID; any given UUID
1136 * or NTSID can only refer to an NTSID or UUID (respectively),
1137 * and *either* a UID *or* a GID, but not both.
1139 static struct kauth_identity
*
1140 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1141 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1142 const char *name
, int nametype
)
1144 struct kauth_identity
*kip
;
1146 /* get and fill in a new identity */
1147 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1149 if (gid
!= KAUTH_GID_NONE
) {
1151 kip
->ki_valid
= KI_VALID_GID
;
1153 if (uid
!= KAUTH_UID_NONE
) {
1154 if (kip
->ki_valid
& KI_VALID_GID
)
1155 panic("can't allocate kauth identity with both uid and gid");
1157 kip
->ki_valid
= KI_VALID_UID
;
1161 * A malicious/faulty resolver could return bad values
1163 assert(supgrpcnt
>= 0);
1164 assert(supgrpcnt
<= NGROUPS
);
1165 assert(supgrps
!= NULL
);
1167 if ((supgrpcnt
< 0) || (supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1170 if (kip
->ki_valid
& KI_VALID_GID
)
1171 panic("can't allocate kauth identity with both gid and supplementary groups");
1172 kip
->ki_supgrpcnt
= supgrpcnt
;
1173 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1174 kip
->ki_valid
|= KI_VALID_GROUPS
;
1176 kip
->ki_groups_expiry
= groups_expiry
;
1177 if (guidp
!= NULL
) {
1178 kip
->ki_guid
= *guidp
;
1179 kip
->ki_valid
|= KI_VALID_GUID
;
1181 kip
->ki_guid_expiry
= guid_expiry
;
1182 if (ntsidp
!= NULL
) {
1183 kip
->ki_ntsid
= *ntsidp
;
1184 kip
->ki_valid
|= KI_VALID_NTSID
;
1186 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1188 kip
->ki_name
= name
;
1189 kip
->ki_valid
|= nametype
;
1197 * kauth_identity_register_and_free
1199 * Description: Register an association between identity tokens. The passed
1200 * 'kip' is consumed by this function.
1202 * Parameters: kip Pointer to kauth_identity
1203 * structure to register
1207 * Notes: The memory pointer to by 'kip' is assumed to have been
1208 * previously allocated via kauth_identity_alloc().
1211 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1213 struct kauth_identity
*ip
;
1216 * We search the cache for the UID listed in the incoming association.
1217 * If we already have an entry, the new information is merged.
1220 KAUTH_IDENTITY_LOCK();
1221 if (kip
->ki_valid
& KI_VALID_UID
) {
1222 if (kip
->ki_valid
& KI_VALID_GID
)
1223 panic("kauth_identity: can't insert record with both UID and GID as key");
1224 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1225 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1227 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1228 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1229 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1232 panic("kauth_identity: can't insert record without UID or GID as key");
1236 /* we already have an entry, merge/overwrite */
1237 if (kip
->ki_valid
& KI_VALID_GUID
) {
1238 ip
->ki_guid
= kip
->ki_guid
;
1239 ip
->ki_valid
|= KI_VALID_GUID
;
1241 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1242 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1243 ip
->ki_ntsid
= kip
->ki_ntsid
;
1244 ip
->ki_valid
|= KI_VALID_NTSID
;
1246 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1247 /* a valid ki_name field overwrites the previous name field */
1248 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1249 /* if there's an old one, discard it */
1250 const char *oname
= NULL
;
1251 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1252 oname
= ip
->ki_name
;
1253 ip
->ki_name
= kip
->ki_name
;
1254 kip
->ki_name
= oname
;
1256 /* and discard the incoming entry */
1260 * if we don't have any information on this identity, add it;
1261 * if it pushes us over our limit, discard the oldest one.
1263 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1264 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1265 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1266 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1267 kauth_identity_count
--;
1270 KAUTH_IDENTITY_UNLOCK();
1271 /* have to drop lock before freeing expired entry (it may be in use) */
1273 /* if the ki_name field is used, clear it first */
1274 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1275 vfs_removename(ip
->ki_name
);
1276 /* free the expired entry */
1283 * kauth_identity_updatecache
1285 * Description: Given a lookup result, add any associations that we don't
1286 * currently have; replace ones which have changed.
1288 * Parameters: elp External lookup result from
1289 * user space daemon to kernel
1290 * rkip pointer to returned kauth
1292 * extend_data Extended data (can vary)
1297 * *rkip Modified (if non-NULL)
1299 * Notes: For extended information requests, this code relies on the fact
1300 * that elp->el_flags is never used as an rvalue, and is only
1301 * ever bit-tested for valid lookup information we are willing
1304 * XXX: We may have to do the same in the case that extended data was
1305 * passed out to user space to ensure that the request string
1306 * gets cached; we may also be able to use the rkip as an
1307 * input to avoid this. The jury is still out.
1309 * XXX: This codes performance could be improved for multiple valid
1310 * results by combining the loop iteration in a single loop.
1313 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1316 struct kauth_identity
*kip
;
1317 const char *speculative_name
= NULL
;
1322 * If there is extended data, and that data represents a name rather
1323 * than something else, speculatively create an entry for it in the
1324 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1325 * over the allocation later.
1327 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1328 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1329 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1332 /* user identity? */
1333 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1334 KAUTH_IDENTITY_LOCK();
1335 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1336 /* matching record */
1337 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1338 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1339 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1340 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1341 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1342 kip
->ki_valid
|= KI_VALID_GROUPS
;
1343 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1345 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1346 kip
->ki_guid
= elp
->el_uguid
;
1347 kip
->ki_valid
|= KI_VALID_GUID
;
1349 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1350 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1351 kip
->ki_ntsid
= elp
->el_usid
;
1352 kip
->ki_valid
|= KI_VALID_NTSID
;
1354 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1355 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1356 const char *oname
= kip
->ki_name
;
1357 kip
->ki_name
= speculative_name
;
1358 speculative_name
= NULL
;
1359 kip
->ki_valid
|= KI_VALID_PWNAM
;
1362 * free oname (if any) outside
1365 speculative_name
= oname
;
1368 kauth_identity_lru(kip
);
1371 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1375 KAUTH_IDENTITY_UNLOCK();
1376 /* not found in cache, add new record */
1378 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1379 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1380 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1381 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1382 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1383 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1384 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1385 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1386 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1391 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1392 speculative_name
= NULL
;
1393 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1394 kauth_identity_register_and_free(kip
);
1399 /* group identity? (ignore, if we already processed it as a user) */
1400 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1401 KAUTH_IDENTITY_LOCK();
1402 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1403 /* matching record */
1404 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1405 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1406 kip
->ki_guid
= elp
->el_gguid
;
1407 kip
->ki_valid
|= KI_VALID_GUID
;
1409 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1410 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1411 kip
->ki_ntsid
= elp
->el_gsid
;
1412 kip
->ki_valid
|= KI_VALID_NTSID
;
1414 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1415 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1416 const char *oname
= kip
->ki_name
;
1417 kip
->ki_name
= speculative_name
;
1418 speculative_name
= NULL
;
1419 kip
->ki_valid
|= KI_VALID_GRNAM
;
1422 * free oname (if any) outside
1425 speculative_name
= oname
;
1428 kauth_identity_lru(kip
);
1431 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1435 KAUTH_IDENTITY_UNLOCK();
1436 /* not found in cache, add new record */
1438 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1439 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1440 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1441 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1442 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1443 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1444 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1445 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1446 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1451 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1452 speculative_name
= NULL
;
1453 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1454 kauth_identity_register_and_free(kip
);
1459 /* If we have a name reference to drop, drop it here */
1460 if (speculative_name
!= NULL
) {
1461 vfs_removename(speculative_name
);
1467 * Trim older entries from the identity cache.
1469 * Must be called with the identity cache lock held.
1472 kauth_identity_trimcache(int newsize
) {
1473 struct kauth_identity
*kip
;
1475 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1477 while (kauth_identity_count
> newsize
) {
1478 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1479 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1480 kauth_identity_count
--;
1486 * kauth_identity_lru
1488 * Description: Promote the entry to the head of the LRU, assumes the cache
1491 * Parameters: kip kauth identity to move to the
1492 * head of the LRU list, if it's
1497 * Notes: This is called even if the entry has expired; typically an
1498 * expired entry that's been looked up is about to be revalidated,
1499 * and having it closer to the head of the LRU means finding it
1500 * quickly again when the revalidation comes through.
1503 kauth_identity_lru(struct kauth_identity
*kip
)
1505 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1506 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1507 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1513 * kauth_identity_guid_expired
1515 * Description: Handle lazy expiration of GUID translations.
1517 * Parameters: kip kauth identity to check for
1520 * Returns: 1 Expired
1524 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1529 * Expiration time of 0 means this entry is persistent.
1531 if (kip
->ki_guid_expiry
== 0)
1535 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1537 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1542 * kauth_identity_ntsid_expired
1544 * Description: Handle lazy expiration of NTSID translations.
1546 * Parameters: kip kauth identity to check for
1549 * Returns: 1 Expired
1553 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1558 * Expiration time of 0 means this entry is persistent.
1560 if (kip
->ki_ntsid_expiry
== 0)
1564 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1566 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1570 * kauth_identity_groups_expired
1572 * Description: Handle lazy expiration of supplemental group translations.
1574 * Parameters: kip kauth identity to check for
1577 * Returns: 1 Expired
1581 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1586 * Expiration time of 0 means this entry is persistent.
1588 if (kip
->ki_groups_expiry
== 0)
1592 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1594 return((kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0);
1598 * kauth_identity_find_uid
1600 * Description: Search for an entry by UID
1602 * Parameters: uid UID to find
1603 * kir Pointer to return area
1604 * getname Name buffer, if ki_name wanted
1610 * *klr Modified, if found
1613 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1615 struct kauth_identity
*kip
;
1617 KAUTH_IDENTITY_LOCK();
1618 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1619 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1620 kauth_identity_lru(kip
);
1621 /* Copy via structure assignment */
1623 /* If a name is wanted and one exists, copy it out */
1624 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1625 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1629 KAUTH_IDENTITY_UNLOCK();
1630 return((kip
== NULL
) ? ENOENT
: 0);
1635 * kauth_identity_find_gid
1637 * Description: Search for an entry by GID
1639 * Parameters: gid GID to find
1640 * kir Pointer to return area
1641 * getname Name buffer, if ki_name wanted
1647 * *klr Modified, if found
1650 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1652 struct kauth_identity
*kip
;
1654 KAUTH_IDENTITY_LOCK();
1655 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1656 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1657 kauth_identity_lru(kip
);
1658 /* Copy via structure assignment */
1660 /* If a name is wanted and one exists, copy it out */
1661 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1662 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1666 KAUTH_IDENTITY_UNLOCK();
1667 return((kip
== NULL
) ? ENOENT
: 0);
1672 * kauth_identity_find_guid
1674 * Description: Search for an entry by GUID
1676 * Parameters: guidp Pointer to GUID to find
1677 * kir Pointer to return area
1678 * getname Name buffer, if ki_name wanted
1684 * *klr Modified, if found
1686 * Note: The association may be expired, in which case the caller
1687 * may elect to call out to userland to revalidate.
1690 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1692 struct kauth_identity
*kip
;
1694 KAUTH_IDENTITY_LOCK();
1695 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1696 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1697 kauth_identity_lru(kip
);
1698 /* Copy via structure assignment */
1700 /* If a name is wanted and one exists, copy it out */
1701 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1702 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1706 KAUTH_IDENTITY_UNLOCK();
1707 return((kip
== NULL
) ? ENOENT
: 0);
1711 * kauth_identity_find_nam
1713 * Description: Search for an entry by name
1715 * Parameters: name Pointer to name to find
1716 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1717 * kir Pointer to return area
1723 * *klr Modified, if found
1726 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1728 struct kauth_identity
*kip
;
1730 KAUTH_IDENTITY_LOCK();
1731 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1732 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1733 kauth_identity_lru(kip
);
1734 /* Copy via structure assignment */
1739 KAUTH_IDENTITY_UNLOCK();
1740 return((kip
== NULL
) ? ENOENT
: 0);
1745 * kauth_identity_find_ntsid
1747 * Description: Search for an entry by NTSID
1749 * Parameters: ntsid Pointer to NTSID to find
1750 * kir Pointer to return area
1751 * getname Name buffer, if ki_name wanted
1757 * *klr Modified, if found
1759 * Note: The association may be expired, in which case the caller
1760 * may elect to call out to userland to revalidate.
1763 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1765 struct kauth_identity
*kip
;
1767 KAUTH_IDENTITY_LOCK();
1768 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1769 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1770 kauth_identity_lru(kip
);
1771 /* Copy via structure assignment */
1773 /* If a name is wanted and one exists, copy it out */
1774 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1775 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1779 KAUTH_IDENTITY_UNLOCK();
1780 return((kip
== NULL
) ? ENOENT
: 0);
1782 #endif /* CONFIG_EXT_RESOLVER */
1788 guid_t kauth_null_guid
;
1794 * Description: Determine the equality of two GUIDs
1796 * Parameters: guid1 Pointer to first GUID
1797 * guid2 Pointer to second GUID
1799 * Returns: 0 If GUIDs are unequal
1800 * !0 If GUIDs are equal
1803 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1805 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1810 * kauth_wellknown_guid
1812 * Description: Determine if a GUID is a well-known GUID
1814 * Parameters: guid Pointer to GUID to check
1816 * Returns: KAUTH_WKG_NOT Not a well known GUID
1817 * KAUTH_WKG_EVERYBODY "Everybody"
1818 * KAUTH_WKG_NOBODY "Nobody"
1819 * KAUTH_WKG_OWNER "Other"
1820 * KAUTH_WKG_GROUP "Group"
1823 kauth_wellknown_guid(guid_t
*guid
)
1825 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1828 * All WKGs begin with the same 12 bytes.
1830 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1832 * The final 4 bytes are our code (in network byte order).
1834 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1837 return(KAUTH_WKG_EVERYBODY
);
1839 return(KAUTH_WKG_NOBODY
);
1841 return(KAUTH_WKG_OWNER
);
1843 return(KAUTH_WKG_GROUP
);
1846 return(KAUTH_WKG_NOT
);
1853 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1855 * Parameters: sid1 Pointer to first NTSID
1856 * sid2 Pointer to second NTSID
1858 * Returns: 0 If GUIDs are unequal
1859 * !0 If GUIDs are equal
1862 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1864 /* check sizes for equality, also sanity-check size while we're at it */
1865 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1866 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1867 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1876 * We support four tokens representing identity:
1877 * - Credential reference
1880 * - NT security identifier
1882 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1889 * kauth_cred_change_egid
1891 * Description: Set EGID by changing the first element of cr_groups for the
1892 * passed credential; if the new EGID exists in the list of
1893 * groups already, then rotate the old EGID into its position,
1894 * otherwise replace it
1896 * Parameters: cred Pointer to the credential to modify
1897 * new_egid The new EGID to set
1899 * Returns: 0 The egid did not displace a member of
1900 * the supplementary group list
1901 * 1 The egid being set displaced a member
1902 * of the supplementary groups list
1904 * Note: Utility function; internal use only because of locking.
1906 * This function operates on the credential passed; the caller
1907 * must operate either on a newly allocated credential (one for
1908 * which there is no hash cache reference and no externally
1909 * visible pointer reference), or a template credential.
1912 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1918 #endif /* radar_4600026 */
1919 gid_t old_egid
= kauth_cred_getgid(cred
);
1920 posix_cred_t pcred
= posix_cred_get(cred
);
1922 /* Ignoring the first entry, scan for a match for the new egid */
1923 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1925 * If we find a match, swap them so we don't lose overall
1928 if (pcred
->cr_groups
[i
] == new_egid
) {
1929 pcred
->cr_groups
[i
] = old_egid
;
1930 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1937 #error Fix radar 4600026 first!!!
1940 This is correct for memberd behaviour, but incorrect for POSIX; to address
1941 this, we would need to automatically opt-out any SUID/SGID binary, and force
1942 it to use initgroups to opt back in. We take the approach of considering it
1943 opt'ed out in any group of 16 displacement instead, since it's a much more
1944 conservative approach (i.e. less likely to cause things to break).
1948 * If we displaced a member of the supplementary groups list of the
1949 * credential, and we have not opted out of memberd, then if memberd
1950 * says that the credential is a member of the group, then it has not
1951 * actually been displaced.
1953 * NB: This is typically a cold code path.
1955 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1956 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1959 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1961 #endif /* radar_4600026 */
1963 /* set the new EGID into the old spot */
1964 pcred
->cr_groups
[0] = new_egid
;
1973 * Description: Fetch UID from credential
1975 * Parameters: cred Credential to examine
1977 * Returns: (uid_t) UID associated with credential
1980 kauth_cred_getuid(kauth_cred_t cred
)
1982 NULLCRED_CHECK(cred
);
1983 return(posix_cred_get(cred
)->cr_uid
);
1988 * kauth_cred_getruid
1990 * Description: Fetch RUID from credential
1992 * Parameters: cred Credential to examine
1994 * Returns: (uid_t) RUID associated with credential
1997 kauth_cred_getruid(kauth_cred_t cred
)
1999 NULLCRED_CHECK(cred
);
2000 return(posix_cred_get(cred
)->cr_ruid
);
2005 * kauth_cred_getsvuid
2007 * Description: Fetch SVUID from credential
2009 * Parameters: cred Credential to examine
2011 * Returns: (uid_t) SVUID associated with credential
2014 kauth_cred_getsvuid(kauth_cred_t cred
)
2016 NULLCRED_CHECK(cred
);
2017 return(posix_cred_get(cred
)->cr_svuid
);
2024 * Description: Fetch GID from credential
2026 * Parameters: cred Credential to examine
2028 * Returns: (gid_t) GID associated with credential
2031 kauth_cred_getgid(kauth_cred_t cred
)
2033 NULLCRED_CHECK(cred
);
2034 return(posix_cred_get(cred
)->cr_gid
);
2039 * kauth_cred_getrgid
2041 * Description: Fetch RGID from credential
2043 * Parameters: cred Credential to examine
2045 * Returns: (gid_t) RGID associated with credential
2048 kauth_cred_getrgid(kauth_cred_t cred
)
2050 NULLCRED_CHECK(cred
);
2051 return(posix_cred_get(cred
)->cr_rgid
);
2056 * kauth_cred_getsvgid
2058 * Description: Fetch SVGID from credential
2060 * Parameters: cred Credential to examine
2062 * Returns: (gid_t) SVGID associated with credential
2065 kauth_cred_getsvgid(kauth_cred_t cred
)
2067 NULLCRED_CHECK(cred
);
2068 return(posix_cred_get(cred
)->cr_svgid
);
2072 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2074 #if CONFIG_EXT_RESOLVER == 0
2076 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
2079 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2081 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2082 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2083 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2084 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2089 case KI_VALID_UID
: {
2090 id_t uid
= htonl(*(id_t
*)src
);
2092 if (to
== KI_VALID_GUID
) {
2094 memcpy(uu
, _user_compat_prefix
, sizeof(_user_compat_prefix
));
2095 memcpy(&uu
[COMPAT_PREFIX_LEN
], &uid
, sizeof(uid
));
2100 case KI_VALID_GID
: {
2101 id_t gid
= htonl(*(id_t
*)src
);
2103 if (to
== KI_VALID_GUID
) {
2105 memcpy(uu
, _group_compat_prefix
, sizeof(_group_compat_prefix
));
2106 memcpy(&uu
[COMPAT_PREFIX_LEN
], &gid
, sizeof(gid
));
2111 case KI_VALID_GUID
: {
2112 const uint8_t *uu
= src
;
2114 if (to
== KI_VALID_UID
) {
2115 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2117 memcpy(&uid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(uid
));
2118 *(id_t
*)dst
= ntohl(uid
);
2121 } else if (to
== KI_VALID_GID
) {
2122 if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2124 memcpy(&gid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(gid
));
2125 *(id_t
*)dst
= ntohl(gid
);
2132 /* NOT IMPLEMENTED */
2139 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2141 * Structure to hold supplemental groups. Used for impedance matching with
2142 * kauth_cred_cache_lookup below.
2150 * kauth_cred_uid2groups
2152 * Description: Fetch supplemental GROUPS from UID
2154 * Parameters: uid UID to examine
2155 * groups pointer to an array of gid_ts
2156 * gcount pointer to the number of groups wanted/returned
2158 * Returns: 0 Success
2159 * kauth_cred_cache_lookup:EINVAL
2162 * *groups Modified, if successful
2163 * *gcount Modified, if successful
2167 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, int *gcount
)
2171 struct supgroups supgroups
;
2172 supgroups
.count
= gcount
;
2173 supgroups
.groups
= groups
;
2175 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2182 * kauth_cred_guid2pwnam
2184 * Description: Fetch PWNAM from GUID
2186 * Parameters: guidp Pointer to GUID to examine
2187 * pwnam Pointer to user@domain buffer
2189 * Returns: 0 Success
2190 * kauth_cred_cache_lookup:EINVAL
2193 * *pwnam Modified, if successful
2195 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2198 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2200 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
2205 * kauth_cred_guid2grnam
2207 * Description: Fetch GRNAM from GUID
2209 * Parameters: guidp Pointer to GUID to examine
2210 * grnam Pointer to group@domain buffer
2212 * Returns: 0 Success
2213 * kauth_cred_cache_lookup:EINVAL
2216 * *grnam Modified, if successful
2218 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2221 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2223 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
2228 * kauth_cred_pwnam2guid
2230 * Description: Fetch PWNAM from GUID
2232 * Parameters: pwnam String containing user@domain
2233 * guidp Pointer to buffer for GUID
2235 * Returns: 0 Success
2236 * kauth_cred_cache_lookup:EINVAL
2239 * *guidp Modified, if successful
2241 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2242 * bytes in size, including the NUL termination of the string.
2245 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2247 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
2252 * kauth_cred_grnam2guid
2254 * Description: Fetch GRNAM from GUID
2256 * Parameters: grnam String containing group@domain
2257 * guidp Pointer to buffer for GUID
2259 * Returns: 0 Success
2260 * kauth_cred_cache_lookup:EINVAL
2263 * *guidp Modified, if successful
2265 * Notes: grnam should not point to a request larger than MAXPATHLEN
2266 * bytes in size, including the NUL termination of the string.
2269 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2271 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
2276 * kauth_cred_guid2uid
2278 * Description: Fetch UID from GUID
2280 * Parameters: guidp Pointer to GUID to examine
2281 * uidp Pointer to buffer for UID
2283 * Returns: 0 Success
2284 * kauth_cred_cache_lookup:EINVAL
2287 * *uidp Modified, if successful
2290 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2292 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
2297 * kauth_cred_guid2gid
2299 * Description: Fetch GID from GUID
2301 * Parameters: guidp Pointer to GUID to examine
2302 * gidp Pointer to buffer for GID
2304 * Returns: 0 Success
2305 * kauth_cred_cache_lookup:EINVAL
2308 * *gidp Modified, if successful
2311 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2313 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2318 * kauth_cred_ntsid2uid
2320 * Description: Fetch UID from NTSID
2322 * Parameters: sidp Pointer to NTSID to examine
2323 * uidp Pointer to buffer for UID
2325 * Returns: 0 Success
2326 * kauth_cred_cache_lookup:EINVAL
2329 * *uidp Modified, if successful
2332 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2334 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2339 * kauth_cred_ntsid2gid
2341 * Description: Fetch GID from NTSID
2343 * Parameters: sidp Pointer to NTSID to examine
2344 * gidp Pointer to buffer for GID
2346 * Returns: 0 Success
2347 * kauth_cred_cache_lookup:EINVAL
2350 * *gidp Modified, if successful
2353 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2355 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2360 * kauth_cred_ntsid2guid
2362 * Description: Fetch GUID from NTSID
2364 * Parameters: sidp Pointer to NTSID to examine
2365 * guidp Pointer to buffer for GUID
2367 * Returns: 0 Success
2368 * kauth_cred_cache_lookup:EINVAL
2371 * *guidp Modified, if successful
2374 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2376 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2381 * kauth_cred_uid2guid
2383 * Description: Fetch GUID from UID
2385 * Parameters: uid UID to examine
2386 * guidp Pointer to buffer for GUID
2388 * Returns: 0 Success
2389 * kauth_cred_cache_lookup:EINVAL
2392 * *guidp Modified, if successful
2395 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2397 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2402 * kauth_cred_getguid
2404 * Description: Fetch GUID from credential
2406 * Parameters: cred Credential to examine
2407 * guidp Pointer to buffer for GUID
2409 * Returns: 0 Success
2410 * kauth_cred_cache_lookup:EINVAL
2413 * *guidp Modified, if successful
2416 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2418 NULLCRED_CHECK(cred
);
2419 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2424 * kauth_cred_getguid
2426 * Description: Fetch GUID from GID
2428 * Parameters: gid GID to examine
2429 * guidp Pointer to buffer for GUID
2431 * Returns: 0 Success
2432 * kauth_cred_cache_lookup:EINVAL
2435 * *guidp Modified, if successful
2438 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2440 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2445 * kauth_cred_uid2ntsid
2447 * Description: Fetch NTSID from UID
2449 * Parameters: uid UID to examine
2450 * sidp Pointer to buffer for NTSID
2452 * Returns: 0 Success
2453 * kauth_cred_cache_lookup:EINVAL
2456 * *sidp Modified, if successful
2459 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2461 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2466 * kauth_cred_getntsid
2468 * Description: Fetch NTSID from credential
2470 * Parameters: cred Credential to examine
2471 * sidp Pointer to buffer for NTSID
2473 * Returns: 0 Success
2474 * kauth_cred_cache_lookup:EINVAL
2477 * *sidp Modified, if successful
2480 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2482 NULLCRED_CHECK(cred
);
2483 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2488 * kauth_cred_gid2ntsid
2490 * Description: Fetch NTSID from GID
2492 * Parameters: gid GID to examine
2493 * sidp Pointer to buffer for NTSID
2495 * Returns: 0 Success
2496 * kauth_cred_cache_lookup:EINVAL
2499 * *sidp Modified, if successful
2502 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2504 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2509 * kauth_cred_guid2ntsid
2511 * Description: Fetch NTSID from GUID
2513 * Parameters: guidp Pointer to GUID to examine
2514 * sidp Pointer to buffer for NTSID
2516 * Returns: 0 Success
2517 * kauth_cred_cache_lookup:EINVAL
2520 * *sidp Modified, if successful
2523 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2525 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2530 * kauth_cred_cache_lookup
2532 * Description: Lookup a translation in the cache; if one is not found, and
2533 * the attempt was not fatal, submit the request to the resolver
2534 * instead, and wait for it to complete or be aborted.
2536 * Parameters: from Identity information we have
2537 * to Identity information we want
2538 * src Pointer to buffer containing
2539 * the source identity
2540 * dst Pointer to buffer to receive
2541 * the target identity
2543 * Returns: 0 Success
2544 * EINVAL Unknown source identity type
2546 #if CONFIG_EXT_RESOLVER
2548 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2550 struct kauth_identity ki
;
2551 struct kauth_identity_extlookup el
;
2553 uint64_t extend_data
= 0ULL;
2554 int (* expired
)(struct kauth_identity
*kip
);
2555 char *namebuf
= NULL
;
2557 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2560 * Look for an existing cache entry for this association.
2561 * If the entry has not expired, return the cached information.
2562 * We do not cache user@domain translations here; they use too
2563 * much memory to hold onto forever, and can not be updated
2566 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2572 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2575 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2578 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2580 case KI_VALID_NTSID
:
2581 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2583 case KI_VALID_PWNAM
:
2584 case KI_VALID_GRNAM
:
2585 /* Names are unique in their 'from' space */
2586 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2591 /* lookup failure or error */
2593 /* any other error is fatal */
2594 if (error
!= ENOENT
) {
2595 /* XXX bogus check - this is not possible */
2596 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2600 /* found a valid cached entry, check expiry */
2603 expired
= kauth_identity_guid_expired
;
2605 case KI_VALID_NTSID
:
2606 expired
= kauth_identity_ntsid_expired
;
2608 case KI_VALID_GROUPS
:
2609 expired
= kauth_identity_groups_expired
;
2614 expired
= kauth_identity_guid_expired
;
2616 case KI_VALID_NTSID
:
2617 expired
= kauth_identity_ntsid_expired
;
2625 * If no expiry function, or not expired, we have found
2629 if (!expired(&ki
)) {
2630 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2631 expired
= NULL
; /* must clear it is used as a flag */
2634 * We leave ki_valid set here; it contains a
2635 * translation but the TTL has expired. If we can't
2636 * get a result from the resolver, we will use it as
2637 * a better-than nothing alternative.
2640 KAUTH_DEBUG("CACHE - expired entry found");
2643 KAUTH_DEBUG("CACHE - no expiry function");
2647 /* do we have a translation? */
2648 if (ki
.ki_valid
& to
) {
2649 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2650 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2654 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2655 * If we went looking for a translation from GUID or NTSID and
2656 * found a translation that wasn't for our desired type, then
2657 * don't bother calling the resolver. We know that this
2658 * GUID/NTSID can't translate to our desired type.
2662 case KI_VALID_NTSID
:
2665 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2666 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2671 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2672 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2684 * We failed to find a cache entry; call the resolver.
2686 * Note: We ask for as much non-extended data as we can get,
2687 * and only provide (or ask for) extended information if
2688 * we have a 'from' (or 'to') which requires it. This
2689 * way we don't pay for the extra transfer overhead for
2690 * data we don't need.
2692 bzero(&el
, sizeof(el
));
2693 el
.el_info_pid
= current_proc()->p_pid
;
2696 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2697 el
.el_uid
= *(uid_t
*)src
;
2700 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2701 el
.el_gid
= *(gid_t
*)src
;
2704 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2705 el
.el_uguid
= *(guid_t
*)src
;
2706 el
.el_gguid
= *(guid_t
*)src
;
2708 case KI_VALID_NTSID
:
2709 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2710 el
.el_usid
= *(ntsid_t
*)src
;
2711 el
.el_gsid
= *(ntsid_t
*)src
;
2713 case KI_VALID_PWNAM
:
2714 /* extra overhead */
2715 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2716 extend_data
= CAST_USER_ADDR_T(src
);
2718 case KI_VALID_GRNAM
:
2719 /* extra overhead */
2720 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2721 extend_data
= CAST_USER_ADDR_T(src
);
2727 * Here we ask for everything all at once, to avoid having to work
2728 * out what we really want now, or might want soon.
2730 * Asking for SID translations when we don't know we need them right
2731 * now is going to cause excess work to be done if we're connected
2732 * to a network that thinks it can translate them. This list needs
2733 * to get smaller/smarter.
2735 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2736 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2737 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2738 if (to
== KI_VALID_PWNAM
) {
2739 /* extra overhead */
2740 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2741 extend_data
= CAST_USER_ADDR_T(dst
);
2743 if (to
== KI_VALID_GRNAM
) {
2744 /* extra overhead */
2745 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2746 extend_data
= CAST_USER_ADDR_T(dst
);
2748 if (to
== KI_VALID_GROUPS
) {
2749 /* Expensive and only useful for an NFS client not using kerberos */
2750 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2751 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2753 * Copy the current supplemental groups for the resolver.
2754 * The resolver should check these groups first and if
2755 * the user (uid) is still a member it should endeavor to
2756 * keep them in the list. Otherwise NFS clients could get
2757 * changing access to server file system objects on each
2760 el
.el_sup_grp_cnt
= ki
.ki_supgrpcnt
;
2762 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof (el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2763 /* Let the resolver know these were the previous valid groups */
2764 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2765 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2767 KAUTH_DEBUG("GROUPS: no valid groups to send");
2771 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2773 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2775 error
= kauth_resolver_submit(&el
, extend_data
);
2777 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2779 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2781 /* was the external lookup successful? */
2784 * Save the results from the lookup - we may have other
2785 * information, even if we didn't get a guid or the
2788 * If we came from a name, we know the extend_data is valid.
2790 if (from
== KI_VALID_PWNAM
)
2791 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2792 else if (from
== KI_VALID_GRNAM
)
2793 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2795 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2798 * Check to see if we have a valid cache entry
2799 * originating from the result.
2801 if (!(ki
.ki_valid
& to
)) {
2809 * Copy from the appropriate struct kauth_identity cache entry
2810 * structure into the destination buffer area.
2814 *(uid_t
*)dst
= ki
.ki_uid
;
2817 *(gid_t
*)dst
= ki
.ki_gid
;
2820 *(guid_t
*)dst
= ki
.ki_guid
;
2822 case KI_VALID_NTSID
:
2823 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2825 case KI_VALID_GROUPS
: {
2826 struct supgroups
*gp
= (struct supgroups
*)dst
;
2827 u_int32_t limit
= ki
.ki_supgrpcnt
;
2830 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2834 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2837 case KI_VALID_PWNAM
:
2838 case KI_VALID_GRNAM
:
2839 /* handled in kauth_resolver_complete() */
2844 KAUTH_DEBUG("CACHE - returned successfully");
2850 * Group membership cache.
2852 * XXX the linked-list implementation here needs to be optimized.
2858 * Description: Initialize the groups cache
2860 * Parameters: (void)
2864 * Notes: Initialize the groups cache for use; the group cache is used
2865 * to avoid unnecessary calls out to user space.
2867 * This function is called from kauth_init() in the file
2868 * kern_authorization.c.
2871 kauth_groups_init(void)
2873 TAILQ_INIT(&kauth_groups
);
2874 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2879 * kauth_groups_expired
2881 * Description: Handle lazy expiration of group membership cache entries
2883 * Parameters: gm group membership entry to
2884 * check for expiration
2886 * Returns: 1 Expired
2890 kauth_groups_expired(struct kauth_group_membership
*gm
)
2895 * Expiration time of 0 means this entry is persistent.
2897 if (gm
->gm_expiry
== 0)
2902 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2909 * Description: Promote the entry to the head of the LRU, assumes the cache
2912 * Parameters: kip group membership entry to move
2913 * to the head of the LRU list,
2914 * if it's not already there
2918 * Notes: This is called even if the entry has expired; typically an
2919 * expired entry that's been looked up is about to be revalidated,
2920 * and having it closer to the head of the LRU means finding it
2921 * quickly again when the revalidation comes through.
2924 kauth_groups_lru(struct kauth_group_membership
*gm
)
2926 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2927 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2928 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2934 * kauth_groups_updatecache
2936 * Description: Given a lookup result, add any group cache associations that
2937 * we don't currently have.
2939 * Parameters: elp External lookup result from
2940 * user space daemon to kernel
2941 * rkip pointer to returned kauth
2947 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2949 struct kauth_group_membership
*gm
;
2952 /* need a valid response if we are to cache anything */
2954 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2955 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2961 * Search for an existing record for this association before inserting
2962 * a new one; if we find one, update it instead of creating a new one
2964 KAUTH_GROUPS_LOCK();
2965 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2966 if ((el
->el_uid
== gm
->gm_uid
) &&
2967 (el
->el_gid
== gm
->gm_gid
)) {
2968 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2969 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2971 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2973 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2974 kauth_groups_lru(gm
);
2978 KAUTH_GROUPS_UNLOCK();
2980 /* if we found an entry to update, stop here */
2984 /* allocate a new record */
2985 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2987 gm
->gm_uid
= el
->el_uid
;
2988 gm
->gm_gid
= el
->el_gid
;
2989 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2990 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2992 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2994 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2998 * Insert the new entry. Note that it's possible to race ourselves
2999 * here and end up with duplicate entries in the list. Wasteful, but
3000 * harmless since the first into the list will never be looked up,
3001 * and thus will eventually just fall off the end.
3003 KAUTH_GROUPS_LOCK();
3004 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3005 if (++kauth_groups_count
> kauth_groups_cachemax
) {
3006 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3007 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3008 kauth_groups_count
--;
3012 KAUTH_GROUPS_UNLOCK();
3014 /* free expired cache entry */
3020 * Trim older entries from the group membership cache.
3022 * Must be called with the group cache lock held.
3025 kauth_groups_trimcache(int new_size
) {
3026 struct kauth_group_membership
*gm
;
3028 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
3030 while (kauth_groups_count
> new_size
) {
3031 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3032 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3033 kauth_groups_count
--;
3037 #endif /* CONFIG_EXT_RESOLVER */
3040 * Group membership KPI
3044 * kauth_cred_ismember_gid
3046 * Description: Given a credential and a GID, determine if the GID is a member
3047 * of one of the supplementary groups associated with the given
3050 * Parameters: cred Credential to check in
3051 * gid GID to check for membership
3052 * resultp Pointer to int to contain the
3053 * result of the call
3055 * Returns: 0 Success
3056 * ENOENT Could not perform lookup
3057 * kauth_resolver_submit:EWOULDBLOCK
3058 * kauth_resolver_submit:EINTR
3059 * kauth_resolver_submit:ENOMEM
3060 * kauth_resolver_submit:ENOENT User space daemon did not vend
3062 * kauth_resolver_submit:??? Unlikely error from user space
3065 * *resultp (modified) 1 Is member
3068 * Notes: This function guarantees not to modify resultp when returning
3071 * This function effectively checks the EGID as well, since the
3072 * EGID is cr_groups[0] as an implementation detail.
3075 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3077 posix_cred_t pcred
= posix_cred_get(cred
);
3081 * Check the per-credential list of override groups.
3083 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3084 * the cache should be used for that case.
3086 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3087 if (gid
== pcred
->cr_groups
[i
]) {
3094 * If we don't have a UID for group membership checks, the in-cred list
3095 * was authoritative and we can stop here.
3097 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3102 #if CONFIG_EXT_RESOLVER
3103 struct kauth_group_membership
*gm
;
3104 struct kauth_identity_extlookup el
;
3108 * If the resolver hasn't checked in yet, we are early in the boot
3109 * phase and the local group list is complete and authoritative.
3111 if (!kauth_resolver_registered
) {
3117 /* XXX check supplementary groups */
3118 /* XXX check whiteout groups */
3119 /* XXX nesting of supplementary/whiteout groups? */
3122 * Check the group cache.
3124 KAUTH_GROUPS_LOCK();
3125 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3126 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3127 kauth_groups_lru(gm
);
3132 /* did we find a membership entry? */
3134 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3135 KAUTH_GROUPS_UNLOCK();
3137 /* if we did, we can return now */
3139 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3143 /* nothing in the cache, need to go to userland */
3144 bzero(&el
, sizeof(el
));
3145 el
.el_info_pid
= current_proc()->p_pid
;
3146 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3147 el
.el_uid
= pcred
->cr_gmuid
;
3149 el
.el_member_valid
= 0; /* XXX set by resolver? */
3151 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3153 error
= kauth_resolver_submit(&el
, 0ULL);
3155 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3159 /* save the results from the lookup */
3160 kauth_groups_updatecache(&el
);
3162 /* if we successfully ascertained membership, report */
3163 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3164 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3176 * kauth_cred_ismember_guid
3178 * Description: Determine whether the supplied credential is a member of the
3179 * group nominated by GUID.
3181 * Parameters: cred Credential to check in
3182 * guidp Pointer to GUID whose group
3183 * we are testing for membership
3184 * resultp Pointer to int to contain the
3185 * result of the call
3187 * Returns: 0 Success
3188 * kauth_cred_guid2gid:EINVAL
3189 * kauth_cred_ismember_gid:ENOENT
3190 * kauth_resolver_submit:ENOENT User space daemon did not vend
3192 * kauth_cred_ismember_gid:EWOULDBLOCK
3193 * kauth_cred_ismember_gid:EINTR
3194 * kauth_cred_ismember_gid:ENOMEM
3195 * kauth_cred_ismember_gid:??? Unlikely error from user space
3198 * *resultp (modified) 1 Is member
3202 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3206 switch (kauth_wellknown_guid(guidp
)) {
3207 case KAUTH_WKG_NOBODY
:
3210 case KAUTH_WKG_EVERYBODY
:
3216 #if CONFIG_EXT_RESOLVER
3217 struct kauth_identity ki
;
3220 * Grovel the identity cache looking for this GUID.
3221 * If we find it, and it is for a user record, return
3222 * false because it's not a group.
3224 * This is necessary because we don't have -ve caching
3225 * of group memberships, and we really want to avoid
3226 * calling out to the resolver if at all possible.
3228 * Because we're called by the ACL evaluator, and the
3229 * ACL evaluator is likely to encounter ACEs for users,
3230 * this is expected to be a common case.
3233 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3234 !kauth_identity_guid_expired(&ki
)) {
3235 if (ki
.ki_valid
& KI_VALID_GID
) {
3236 /* It's a group after all... */
3240 if (ki
.ki_valid
& KI_VALID_UID
) {
3245 #endif /* CONFIG_EXT_RESOLVER */
3247 * Attempt to translate the GUID to a GID. Even if
3248 * this fails, we will have primed the cache if it is
3249 * a user record and we'll see it above the next time
3252 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3254 * If we have no guid -> gid translation, it's not a group and
3255 * thus the cred can't be a member.
3257 if (error
== ENOENT
) {
3262 #if CONFIG_EXT_RESOLVER
3264 #endif /* CONFIG_EXT_RESOLVER */
3265 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3274 * kauth_cred_gid_subset
3276 * Description: Given two credentials, determine if all GIDs associated with
3277 * the first are also associated with the second
3279 * Parameters: cred1 Credential to check for
3280 * cred2 Credential to check in
3281 * resultp Pointer to int to contain the
3282 * result of the call
3284 * Returns: 0 Success
3285 * non-zero See kauth_cred_ismember_gid for
3289 * *resultp (modified) 1 Is subset
3292 * Notes: This function guarantees not to modify resultp when returning
3296 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3298 int i
, err
, res
= 1;
3300 posix_cred_t pcred1
= posix_cred_get(cred1
);
3301 posix_cred_t pcred2
= posix_cred_get(cred2
);
3303 /* First, check the local list of groups */
3304 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3305 gid
= pcred1
->cr_groups
[i
];
3306 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3310 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3316 /* Check real gid */
3317 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3321 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3322 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3327 /* Finally, check saved gid */
3328 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
3332 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3333 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3344 * kauth_cred_issuser
3346 * Description: Fast replacement for issuser()
3348 * Parameters: cred Credential to check for super
3351 * Returns: 0 Not super user
3354 * Notes: This function uses a magic number which is not a manifest
3355 * constant; this is bad practice.
3358 kauth_cred_issuser(kauth_cred_t cred
)
3360 return(kauth_cred_getuid(cred
) == 0);
3368 /* lock protecting credential hash table */
3369 static lck_mtx_t
*kauth_cred_hash_mtx
;
3370 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3371 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
3372 #if KAUTH_CRED_HASH_DEBUG
3373 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3374 #else /* !KAUTH_CRED_HASH_DEBUG */
3375 #define KAUTH_CRED_HASH_LOCK_ASSERT()
3376 #endif /* !KAUTH_CRED_HASH_DEBUG */
3382 * Description: Initialize the credential hash cache
3384 * Parameters: (void)
3388 * Notes: Intialize the credential hash cache for use; the credential
3389 * hash cache is used convert duplicate credentials into a
3390 * single reference counted credential in order to save wired
3391 * kernel memory. In practice, this generally means a desktop
3392 * system runs with a few tens of credentials, instead of one
3393 * per process, one per thread, one per vnode cache entry, and
3394 * so on. This generally results in savings of 200K or more
3395 * (potentially much more on server systems).
3397 * The hash cache internally has a reference on the credential
3398 * for itself as a means of avoiding a reclaim race for a
3399 * credential in the process of having it's last non-hash
3400 * reference released. This would otherwise result in the
3401 * possibility of a freed credential that was still in uses due
3402 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3404 * On final release, the hash reference is droped, and the
3405 * credential is freed back to the system.
3407 * This function is called from kauth_init() in the file
3408 * kern_authorization.c.
3411 kauth_cred_init(void)
3415 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3417 /*allocate credential hash table */
3418 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3419 (sizeof(struct kauth_cred_entry_head
) * KAUTH_CRED_TABLE_SIZE
),
3420 M_KAUTH
, M_WAITOK
| M_ZERO
);
3421 if (kauth_cred_table_anchor
== NULL
)
3422 panic("startup: kauth_cred_init");
3423 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
3424 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3432 * Description: Get the current thread's effective UID.
3434 * Parameters: (void)
3436 * Returns: (uid_t) The effective UID of the
3442 return(kauth_cred_getuid(kauth_cred_get()));
3449 * Description: Get the current thread's real UID.
3451 * Parameters: (void)
3453 * Returns: (uid_t) The real UID of the current
3459 return(kauth_cred_getruid(kauth_cred_get()));
3466 * Description: Get the current thread's effective GID.
3468 * Parameters: (void)
3470 * Returns: (gid_t) The effective GID of the
3476 return(kauth_cred_getgid(kauth_cred_get()));
3483 * Description: Get the current thread's real GID.
3485 * Parameters: (void)
3487 * Returns: (gid_t) The real GID of the current
3493 return(kauth_cred_getrgid(kauth_cred_get()));
3500 * Description: Returns a pointer to the current thread's credential
3502 * Parameters: (void)
3504 * Returns: (kauth_cred_t) Pointer to the current thread's
3507 * Notes: This function does not take a reference; because of this, the
3508 * caller MUST NOT do anything that would let the thread's
3509 * credential change while using the returned value, without
3510 * first explicitly taking their own reference.
3512 * If a caller intends to take a reference on the resulting
3513 * credential pointer from calling this function, it is strongly
3514 * recommended that the caller use kauth_cred_get_with_ref()
3515 * instead, to protect against any future changes to the cred
3516 * locking protocols; such changes could otherwise potentially
3517 * introduce race windows in the callers code.
3520 kauth_cred_get(void)
3523 struct uthread
*uthread
;
3525 uthread
= get_bsdthread_info(current_thread());
3527 if (uthread
== NULL
)
3528 panic("thread wants credential but has no BSD thread info");
3530 * We can lazy-bind credentials to threads, as long as their processes
3533 * XXX If we later inline this function, the code in this block
3534 * XXX should probably be called out in a function.
3536 if (uthread
->uu_ucred
== NOCRED
) {
3537 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3538 panic("thread wants credential but has no BSD process");
3539 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3541 return(uthread
->uu_ucred
);
3545 mach_kauth_cred_uthread_update(void)
3550 uthread
= get_bsdthread_info(current_thread());
3551 proc
= current_proc();
3553 kauth_cred_uthread_update(uthread
, proc
);
3557 * kauth_cred_uthread_update
3559 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3560 * late-bind the uthread cred to the proc cred.
3562 * Parameters: uthread_t The uthread to update
3563 * proc_t The process to update to
3567 * Notes: This code is common code called from system call or trap entry
3568 * in the case that the process thread may have been changed
3569 * since the last time the thread entered the kernel. It is
3570 * generally only called with the current uthread and process as
3574 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3576 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3577 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3578 kauth_cred_t old
= uthread
->uu_ucred
;
3579 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3580 if (IS_VALID_CRED(old
))
3581 kauth_cred_unref(&old
);
3587 * kauth_cred_get_with_ref
3589 * Description: Takes a reference on the current thread's credential, and then
3590 * returns a pointer to it to the caller.
3592 * Parameters: (void)
3594 * Returns: (kauth_cred_t) Pointer to the current thread's
3595 * newly referenced credential
3597 * Notes: This function takes a reference on the credential before
3598 * returning it to the caller.
3600 * It is the responsibility of the calling code to release this
3601 * reference when the credential is no longer in use.
3603 * Since the returned reference may be a persistent reference
3604 * (e.g. one cached in another data structure with a lifetime
3605 * longer than the calling function), this release may be delayed
3606 * until such time as the persistent reference is to be destroyed.
3607 * An example of this would be the per vnode credential cache used
3608 * to accelerate lookup operations.
3611 kauth_cred_get_with_ref(void)
3614 struct uthread
*uthread
;
3616 uthread
= get_bsdthread_info(current_thread());
3618 if (uthread
== NULL
)
3619 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3620 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3621 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3624 * We can lazy-bind credentials to threads, as long as their processes
3627 * XXX If we later inline this function, the code in this block
3628 * XXX should probably be called out in a function.
3630 if (uthread
->uu_ucred
== NOCRED
) {
3631 /* take reference for new cred in thread */
3632 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3634 /* take a reference for our caller */
3635 kauth_cred_ref(uthread
->uu_ucred
);
3636 return(uthread
->uu_ucred
);
3641 * kauth_cred_proc_ref
3643 * Description: Takes a reference on the current process's credential, and
3644 * then returns a pointer to it to the caller.
3646 * Parameters: procp Process whose credential we
3647 * intend to take a reference on
3649 * Returns: (kauth_cred_t) Pointer to the process's
3650 * newly referenced credential
3652 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3653 * after the refeence is taken to protect the p_ucred field of
3654 * the process referred to by procp.
3656 * Notes: This function takes a reference on the credential before
3657 * returning it to the caller.
3659 * It is the responsibility of the calling code to release this
3660 * reference when the credential is no longer in use.
3662 * Since the returned reference may be a persistent reference
3663 * (e.g. one cached in another data structure with a lifetime
3664 * longer than the calling function), this release may be delayed
3665 * until such time as the persistent reference is to be destroyed.
3666 * An example of this would be the per vnode credential cache used
3667 * to accelerate lookup operations.
3670 kauth_cred_proc_ref(proc_t procp
)
3674 proc_ucred_lock(procp
);
3675 cred
= proc_ucred(procp
);
3676 kauth_cred_ref(cred
);
3677 proc_ucred_unlock(procp
);
3685 * Description: Allocate a new credential
3687 * Parameters: (void)
3689 * Returns: !NULL Newly allocated credential
3690 * NULL Insufficient memory
3692 * Notes: The newly allocated credential is zero'ed as part of the
3693 * allocation process, with the exception of the reference
3694 * count, which is set to 1 to indicate a single reference
3695 * held by the caller.
3697 * Since newly allocated credentials have no external pointers
3698 * referencing them, prior to making them visible in an externally
3699 * visible pointer (e.g. by adding them to the credential hash
3700 * cache) is the only legal time in which an existing credential
3701 * can be safely iinitialized or modified directly.
3703 * After initialization, the caller is expected to call the
3704 * function kauth_cred_add() to add the credential to the hash
3705 * cache, after which time it's frozen and becomes publically
3708 * The release protocol depends on kauth_hash_add() being called
3709 * before kauth_cred_rele() (there is a diagnostic panic which
3710 * will trigger if this protocol is not observed).
3712 * XXX: This function really ought to be static, rather than being
3713 * exported as KPI, since a failure of kauth_cred_add() can only
3714 * be handled by an explicit free of the credential; such frees
3715 * depend on knowlegdge of the allocation method used, which is
3716 * permitted to change between kernel revisions.
3718 * XXX: In the insufficient resource case, this code panic's rather
3719 * than returning a NULL pointer; the code that calls this
3720 * function needs to be audited before this can be changed.
3723 kauth_cred_alloc(void)
3725 kauth_cred_t newcred
;
3727 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3729 posix_cred_t newpcred
= posix_cred_get(newcred
);
3730 bzero(newcred
, sizeof(*newcred
));
3731 newcred
->cr_ref
= 1;
3732 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3733 /* must do this, or cred has same group membership as uid 0 */
3734 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3737 panic("kauth_cred_alloc: couldn't allocate credential");
3741 #if KAUTH_CRED_HASH_DEBUG
3746 mac_cred_label_init(newcred
);
3756 * Description: Look to see if we already have a known credential in the hash
3757 * cache; if one is found, bump the reference count and return
3758 * it. If there are no credentials that match the given
3759 * credential, then allocate a new credential.
3761 * Parameters: cred Template for credential to
3764 * Returns: (kauth_cred_t) The credential that was found
3765 * in the hash or created
3766 * NULL kauth_cred_add() failed, or
3767 * there was not an egid specified
3769 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3770 * maintain this field, we can't expect callers to know how it
3771 * needs to be set. Callers should be prepared for this field
3772 * to be overwritten.
3774 * XXX: This code will tight-loop if memory for a new credential is
3775 * persistently unavailable; this is perhaps not the wisest way
3776 * to handle this condition, but current callers do not expect
3780 kauth_cred_create(kauth_cred_t cred
)
3782 kauth_cred_t found_cred
, new_cred
= NULL
;
3783 posix_cred_t pcred
= posix_cred_get(cred
);
3786 KAUTH_CRED_HASH_LOCK_ASSERT();
3788 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3789 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3792 * If the template credential is not opting out of external
3793 * group membership resolution, then we need to check that
3794 * the UID we will be using is resolvable by the external
3795 * resolver. If it's not, then we opt it out anyway, since
3796 * all future external resolution requests will be failing
3797 * anyway, and potentially taking a long time to do it. We
3798 * use gid 0 because we always know it will exist and not
3799 * trigger additional lookups. This is OK, because we end up
3800 * precatching the information here as a result.
3802 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3804 * It's a recognized value; we don't really care about
3805 * the answer, so long as it's something the external
3806 * resolver could have vended.
3808 pcred
->cr_gmuid
= pcred
->cr_uid
;
3811 * It's not something the external resolver could
3812 * have vended, so we don't want to ask it more
3813 * questions about the credential in the future. This
3814 * speeds up future lookups, as long as the caller
3815 * caches results; otherwise, it the same recurring
3816 * cost. Since most credentials are used multiple
3817 * times, we still get some performance win from this.
3819 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3820 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3824 /* Caller *must* specify at least the egid in cr_groups[0] */
3825 if (pcred
->cr_ngroups
< 1)
3829 KAUTH_CRED_HASH_LOCK();
3830 found_cred
= kauth_cred_find(cred
);
3831 if (found_cred
!= NULL
) {
3833 * Found an existing credential so we'll bump
3834 * reference count and return
3836 kauth_cred_ref(found_cred
);
3837 KAUTH_CRED_HASH_UNLOCK();
3840 KAUTH_CRED_HASH_UNLOCK();
3843 * No existing credential found. Create one and add it to
3846 new_cred
= kauth_cred_alloc();
3847 if (new_cred
!= NULL
) {
3849 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3850 new_pcred
->cr_uid
= pcred
->cr_uid
;
3851 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3852 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3853 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3854 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3855 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3856 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3857 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3859 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3860 sizeof(new_cred
->cr_audit
));
3862 new_pcred
->cr_flags
= pcred
->cr_flags
;
3864 KAUTH_CRED_HASH_LOCK();
3865 err
= kauth_cred_add(new_cred
);
3866 KAUTH_CRED_HASH_UNLOCK();
3868 /* Retry if kauth_cred_add returns non zero value */
3872 mac_cred_label_destroy(new_cred
);
3874 AUDIT_SESSION_UNREF(new_cred
);
3876 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3886 * kauth_cred_setresuid
3888 * Description: Update the given credential using the UID arguments. The given
3889 * UIDs are used to set the effective UID, real UID, saved UID,
3890 * and GMUID (used for group membership checking).
3892 * Parameters: cred The original credential
3893 * ruid The new real UID
3894 * euid The new effective UID
3895 * svuid The new saved UID
3896 * gmuid KAUTH_UID_NONE -or- the new
3897 * group membership UID
3899 * Returns: (kauth_cred_t) The updated credential
3901 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3902 * setting, so if you don't want it to change, pass it the
3903 * previous value, explicitly.
3905 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3906 * if it returns a credential other than the one it is passed,
3907 * will have dropped the reference on the passed credential. All
3908 * callers should be aware of this, and treat this function as an
3909 * unref + ref, potentially on different credentials.
3911 * Because of this, the caller is expected to take its own
3912 * reference on the credential passed as the first parameter,
3913 * and be prepared to release the reference on the credential
3914 * that is returned to them, if it is not intended to be a
3915 * persistent reference.
3918 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3920 struct ucred temp_cred
;
3921 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3922 posix_cred_t pcred
= posix_cred_get(cred
);
3924 NULLCRED_CHECK(cred
);
3927 * We don't need to do anything if the UIDs we are changing are
3928 * already the same as the UIDs passed in
3930 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3931 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3932 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3933 (pcred
->cr_gmuid
== gmuid
)) {
3934 /* no change needed */
3939 * Look up in cred hash table to see if we have a matching credential
3940 * with the new values; this is done by calling kauth_cred_update().
3942 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3943 if (euid
!= KAUTH_UID_NONE
) {
3944 temp_pcred
->cr_uid
= euid
;
3946 if (ruid
!= KAUTH_UID_NONE
) {
3947 temp_pcred
->cr_ruid
= ruid
;
3949 if (svuid
!= KAUTH_UID_NONE
) {
3950 temp_pcred
->cr_svuid
= svuid
;
3954 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3955 * opt out of participation in external group resolution, unless we
3956 * unless we explicitly opt back in later.
3958 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3959 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3962 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3967 * kauth_cred_setresgid
3969 * Description: Update the given credential using the GID arguments. The given
3970 * GIDs are used to set the effective GID, real GID, and saved
3973 * Parameters: cred The original credential
3974 * rgid The new real GID
3975 * egid The new effective GID
3976 * svgid The new saved GID
3978 * Returns: (kauth_cred_t) The updated credential
3980 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3981 * if it returns a credential other than the one it is passed,
3982 * will have dropped the reference on the passed credential. All
3983 * callers should be aware of this, and treat this function as an
3984 * unref + ref, potentially on different credentials.
3986 * Because of this, the caller is expected to take its own
3987 * reference on the credential passed as the first parameter,
3988 * and be prepared to release the reference on the credential
3989 * that is returned to them, if it is not intended to be a
3990 * persistent reference.
3993 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3995 struct ucred temp_cred
;
3996 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3997 posix_cred_t pcred
= posix_cred_get(cred
);
3999 NULLCRED_CHECK(cred
);
4000 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
4003 * We don't need to do anything if the given GID are already the
4004 * same as the GIDs in the credential.
4006 if (pcred
->cr_groups
[0] == egid
&&
4007 pcred
->cr_rgid
== rgid
&&
4008 pcred
->cr_svgid
== svgid
) {
4009 /* no change needed */
4014 * Look up in cred hash table to see if we have a matching credential
4015 * with the new values; this is done by calling kauth_cred_update().
4017 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4018 if (egid
!= KAUTH_GID_NONE
) {
4019 /* displacing a supplementary group opts us out of memberd */
4020 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
4021 DEBUG_CRED_CHANGE("displaced!\n");
4022 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4023 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4025 DEBUG_CRED_CHANGE("not displaced\n");
4028 if (rgid
!= KAUTH_GID_NONE
) {
4029 temp_pcred
->cr_rgid
= rgid
;
4031 if (svgid
!= KAUTH_GID_NONE
) {
4032 temp_pcred
->cr_svgid
= svgid
;
4035 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4040 * Update the given credential with the given groups. We only allocate a new
4041 * credential when the given gid actually results in changes to the existing
4043 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4044 * which will be used for group membership checking.
4047 * kauth_cred_setgroups
4049 * Description: Update the given credential using the provide supplementary
4050 * group list and group membership UID
4052 * Parameters: cred The original credential
4053 * groups Pointer to gid_t array which
4054 * contains the new group list
4055 * groupcount The count of valid groups which
4056 * are contained in 'groups'
4057 * gmuid KAUTH_UID_NONE -or- the new
4058 * group membership UID
4060 * Returns: (kauth_cred_t) The updated credential
4062 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4063 * setting, so if you don't want it to change, pass it the
4064 * previous value, explicitly.
4066 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4067 * if it returns a credential other than the one it is passed,
4068 * will have dropped the reference on the passed credential. All
4069 * callers should be aware of this, and treat this function as an
4070 * unref + ref, potentially on different credentials.
4072 * Because of this, the caller is expected to take its own
4073 * reference on the credential passed as the first parameter,
4074 * and be prepared to release the reference on the credential
4075 * that is returned to them, if it is not intended to be a
4076 * persistent reference.
4078 * XXX: Changes are determined in ordinal order - if the caller passes
4079 * in the same groups list that is already present in the
4080 * credential, but the members are in a different order, even if
4081 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4082 * is considered a modification to the credential, and a new
4083 * credential is created.
4085 * This should perhaps be better optimized, but it is considered
4086 * to be the caller's problem.
4089 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
4092 struct ucred temp_cred
;
4093 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4096 NULLCRED_CHECK(cred
);
4098 pcred
= posix_cred_get(cred
);
4101 * We don't need to do anything if the given list of groups does not
4104 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4105 for (i
= 0; i
< groupcount
; i
++) {
4106 if (pcred
->cr_groups
[i
] != groups
[i
])
4109 if (i
== groupcount
) {
4110 /* no change needed */
4116 * Look up in cred hash table to see if we have a matching credential
4117 * with new values. If we are setting or clearing the gmuid, then
4118 * update the cr_flags, since clearing it is sticky. This permits an
4119 * opt-out of memberd processing using setgroups(), and an opt-in
4120 * using initgroups(). This is required for POSIX conformance.
4122 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4123 temp_pcred
->cr_ngroups
= groupcount
;
4124 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
4125 temp_pcred
->cr_gmuid
= gmuid
;
4126 if (gmuid
== KAUTH_UID_NONE
)
4127 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4129 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4131 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4135 * Notes: The return value exists to account for the possibility of a
4136 * kauth_cred_t without a POSIX label. This will be the case in
4137 * the future (see posix_cred_get() below, for more details).
4139 #if CONFIG_EXT_RESOLVER
4140 int kauth_external_supplementary_groups_supported
= 1;
4142 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4146 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
4148 int limit
= NGROUPS
;
4151 pcred
= posix_cred_get(cred
);
4153 #if CONFIG_EXT_RESOLVER
4155 * If we've not opted out of using the resolver, then convert the cred to a list
4156 * of supplemental groups. We do this only if there has been a resolver to talk to,
4157 * since we may be too early in boot, or in an environment that isn't using DS.
4159 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4160 uid_t uid
= kauth_cred_getuid(cred
);
4163 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4167 /* On error just fall through */
4168 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4170 #endif /* CONFIG_EXT_RESOLVER */
4173 * If they just want a copy of the groups list, they may not care
4174 * about the actual count. If they specify an input count, however,
4175 * treat it as an indicator of the buffer size available in grouplist,
4176 * and limit the returned list to that size.
4179 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4183 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4190 * kauth_cred_setuidgid
4192 * Description: Update the given credential using the UID and GID arguments.
4193 * The given UID is used to set the effective UID, real UID, and
4194 * saved UID. The given GID is used to set the effective GID,
4195 * real GID, and saved GID.
4197 * Parameters: cred The original credential
4198 * uid The new UID to use
4199 * gid The new GID to use
4201 * Returns: (kauth_cred_t) The updated credential
4203 * Notes: We set the gmuid to uid if the credential we are inheriting
4204 * from has not opted out of memberd participation; otherwise
4205 * we set it to KAUTH_UID_NONE
4207 * This code is only ever called from the per-thread credential
4208 * code path in the "set per thread credential" case; and in
4209 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4212 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4213 * if it returns a credential other than the one it is passed,
4214 * will have dropped the reference on the passed credential. All
4215 * callers should be aware of this, and treat this function as an
4216 * unref + ref, potentially on different credentials.
4218 * Because of this, the caller is expected to take its own
4219 * reference on the credential passed as the first parameter,
4220 * and be prepared to release the reference on the credential
4221 * that is returned to them, if it is not intended to be a
4222 * persistent reference.
4225 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4227 struct ucred temp_cred
;
4228 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4231 NULLCRED_CHECK(cred
);
4233 pcred
= posix_cred_get(cred
);
4236 * We don't need to do anything if the effective, real and saved
4237 * user IDs are already the same as the user ID passed into us.
4239 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4240 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4241 /* no change needed */
4246 * Look up in cred hash table to see if we have a matching credential
4247 * with the new values.
4249 bzero(&temp_cred
, sizeof(temp_cred
));
4250 temp_pcred
->cr_uid
= uid
;
4251 temp_pcred
->cr_ruid
= uid
;
4252 temp_pcred
->cr_svuid
= uid
;
4253 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4254 /* inherit the opt-out of memberd */
4255 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4256 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4257 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4259 temp_pcred
->cr_gmuid
= uid
;
4260 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4262 temp_pcred
->cr_ngroups
= 1;
4263 /* displacing a supplementary group opts us out of memberd */
4264 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4265 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4266 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4268 temp_pcred
->cr_rgid
= gid
;
4269 temp_pcred
->cr_svgid
= gid
;
4271 temp_cred
.cr_label
= cred
->cr_label
;
4274 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4279 * kauth_cred_setsvuidgid
4281 * Description: Function used by execve to set the saved uid and gid values
4282 * for suid/sgid programs
4284 * Parameters: cred The credential to update
4285 * uid The saved uid to set
4286 * gid The saved gid to set
4288 * Returns: (kauth_cred_t) The updated credential
4290 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4291 * if it returns a credential other than the one it is passed,
4292 * will have dropped the reference on the passed credential. All
4293 * callers should be aware of this, and treat this function as an
4294 * unref + ref, potentially on different credentials.
4296 * Because of this, the caller is expected to take its own
4297 * reference on the credential passed as the first parameter,
4298 * and be prepared to release the reference on the credential
4299 * that is returned to them, if it is not intended to be a
4300 * persistent reference.
4303 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4305 struct ucred temp_cred
;
4306 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4309 NULLCRED_CHECK(cred
);
4311 pcred
= posix_cred_get(cred
);
4313 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4316 * We don't need to do anything if the effective, real and saved
4317 * uids are already the same as the uid provided. This check is
4318 * likely insufficient.
4320 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4321 /* no change needed */
4324 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4326 /* look up in cred hash table to see if we have a matching credential
4329 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4330 temp_pcred
->cr_svuid
= uid
;
4331 temp_pcred
->cr_svgid
= gid
;
4333 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4338 * kauth_cred_setauditinfo
4340 * Description: Update the given credential using the given au_session_t.
4342 * Parameters: cred The original credential
4343 * auditinfo_p Pointer to ne audit information
4345 * Returns: (kauth_cred_t) The updated credential
4347 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4348 * if it returns a credential other than the one it is passed,
4349 * will have dropped the reference on the passed credential. All
4350 * callers should be aware of this, and treat this function as an
4351 * unref + ref, potentially on different credentials.
4353 * Because of this, the caller is expected to take its own
4354 * reference on the credential passed as the first parameter,
4355 * and be prepared to release the reference on the credential
4356 * that is returned to them, if it is not intended to be a
4357 * persistent reference.
4360 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4362 struct ucred temp_cred
;
4364 NULLCRED_CHECK(cred
);
4367 * We don't need to do anything if the audit info is already the
4368 * same as the audit info in the credential provided.
4370 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4371 /* no change needed */
4375 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4376 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4378 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
4383 * kauth_cred_label_update
4385 * Description: Update the MAC label associated with a credential
4387 * Parameters: cred The original credential
4388 * label The MAC label to set
4390 * Returns: (kauth_cred_t) The updated credential
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.
4405 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4407 kauth_cred_t newcred
;
4408 struct ucred temp_cred
;
4410 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4412 mac_cred_label_init(&temp_cred
);
4413 mac_cred_label_associate(cred
, &temp_cred
);
4414 mac_cred_label_update(&temp_cred
, label
);
4416 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4417 mac_cred_label_destroy(&temp_cred
);
4422 * kauth_cred_label_update_execve
4424 * Description: Update the MAC label associated with a credential as
4427 * Parameters: cred The original credential
4429 * scriptl The script MAC label
4430 * execl The executable MAC label
4431 * disjointp Pointer to flag to set if old
4432 * and returned credentials are
4435 * Returns: (kauth_cred_t) The updated credential
4438 * *disjointp Set to 1 for disjoint creds
4440 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4441 * if it returns a credential other than the one it is passed,
4442 * will have dropped the reference on the passed credential. All
4443 * callers should be aware of this, and treat this function as an
4444 * unref + ref, potentially on different credentials.
4446 * Because of this, the caller is expected to take its own
4447 * reference on the credential passed as the first parameter,
4448 * and be prepared to release the reference on the credential
4449 * that is returned to them, if it is not intended to be a
4450 * persistent reference.
4455 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4456 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4457 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4459 kauth_cred_t newcred
;
4460 struct ucred temp_cred
;
4462 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4464 mac_cred_label_init(&temp_cred
);
4465 mac_cred_label_associate(cred
, &temp_cred
);
4466 mac_cred_label_update_execve(ctx
, &temp_cred
,
4467 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4468 macextensions
, disjointp
, labelupdateerror
);
4470 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4471 mac_cred_label_destroy(&temp_cred
);
4476 * kauth_proc_label_update
4478 * Description: Update the label inside the credential associated with the process.
4480 * Parameters: p The process to modify
4481 * label The label to place in the process credential
4483 * Notes: The credential associated with the process may change as a result
4484 * of this call. The caller should not assume the process reference to
4485 * the old credential still exists.
4487 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4489 kauth_cred_t my_cred
, my_new_cred
;
4491 my_cred
= kauth_cred_proc_ref(p
);
4493 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4495 /* get current credential and take a reference while we muck with it */
4499 * Set the credential with new info. If there is no change,
4500 * we get back the same credential we passed in; if there is
4501 * a change, we drop the reference on the credential we
4502 * passed in. The subsequent compare is safe, because it is
4503 * a pointer compare rather than a contents compare.
4505 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4506 if (my_cred
!= my_new_cred
) {
4508 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
);
4512 * We need to protect for a race where another thread
4513 * also changed the credential after we took our
4514 * reference. If p_ucred has changed then we should
4515 * restart this again with the new cred.
4517 if (p
->p_ucred
!= my_cred
) {
4518 proc_ucred_unlock(p
);
4519 kauth_cred_unref(&my_new_cred
);
4520 my_cred
= kauth_cred_proc_ref(p
);
4524 p
->p_ucred
= my_new_cred
;
4525 /* update cred on proc */
4526 PROC_UPDATE_CREDS_ONPROC(p
);
4528 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4529 proc_ucred_unlock(p
);
4533 /* Drop old proc reference or our extra reference */
4534 kauth_cred_unref(&my_cred
);
4540 * kauth_proc_label_update_execve
4542 * Description: Update the label inside the credential associated with the
4543 * process as part of a transitioning execve. The label will
4544 * be updated by the policies as part of this processing, not
4545 * provided up front.
4547 * Parameters: p The process to modify
4548 * ctx The context of the exec
4549 * vp The vnode being exec'ed
4550 * scriptl The script MAC label
4551 * execl The executable MAC label
4552 * lupdateerror The error place holder for MAC label authority
4553 * to update about possible termination
4555 * Returns: 0 Label update did not make credential
4557 * 1 Label update caused credential to be
4560 * Notes: The credential associated with the process WILL change as a
4561 * result of this call. The caller should not assume the process
4562 * reference to the old credential still exists.
4566 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4567 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4568 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4570 kauth_cred_t my_cred
, my_new_cred
;
4571 my_cred
= kauth_cred_proc_ref(p
);
4573 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4575 /* get current credential and take a reference while we muck with it */
4579 * Set the credential with new info. If there is no change,
4580 * we get back the same credential we passed in; if there is
4581 * a change, we drop the reference on the credential we
4582 * passed in. The subsequent compare is safe, because it is
4583 * a pointer compare rather than a contents compare.
4585 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4586 if (my_cred
!= my_new_cred
) {
4588 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
);
4592 * We need to protect for a race where another thread
4593 * also changed the credential after we took our
4594 * reference. If p_ucred has changed then we should
4595 * restart this again with the new cred.
4597 if (p
->p_ucred
!= my_cred
) {
4598 proc_ucred_unlock(p
);
4599 kauth_cred_unref(&my_new_cred
);
4600 my_cred
= kauth_cred_proc_ref(p
);
4604 p
->p_ucred
= my_new_cred
;
4605 /* update cred on proc */
4606 PROC_UPDATE_CREDS_ONPROC(p
);
4607 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4608 proc_ucred_unlock(p
);
4612 /* Drop old proc reference or our extra reference */
4613 kauth_cred_unref(&my_cred
);
4618 * for temporary binary compatibility
4620 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4622 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4624 return kauth_cred_label_update(cred
, label
);
4627 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4629 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4631 return kauth_proc_label_update(p
, label
);
4637 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4638 * Since we cannot build our export lists based on the kernel configuration we need
4642 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4648 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4655 * for temporary binary compatibility
4657 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4659 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4664 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4666 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4676 * Description: Add a reference to the passed credential
4678 * Parameters: cred The credential to reference
4682 * Notes: This function adds a reference to the provided credential;
4683 * the existing reference on the credential is assumed to be
4684 * held stable over this operation by taking the appropriate
4685 * lock to protect the pointer from which it is being referenced,
4686 * if necessary (e.g. the proc lock is held over the call if the
4687 * credential being referenced is from p_ucred, the vnode lock
4688 * if from the per vnode name cache cred cache, and so on).
4690 * This is safe from the kauth_cred_unref() path, since an atomic
4691 * add is used, and the unref path specifically checks to see that
4692 * the value has not been changed to add a reference between the
4693 * time the credential is unreferenced by another pointer and the
4694 * time it is unreferenced from the cred hash cache.
4697 kauth_cred_ref(kauth_cred_t cred
)
4701 NULLCRED_CHECK(cred
);
4703 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4706 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4708 #if 0 // use this to watch a specific credential
4709 if ( is_target_cred( cred
) != 0 ) {
4719 * kauth_cred_unref_hashlocked
4721 * Description: release a credential reference; when the last reference is
4722 * released, the credential will be freed.
4724 * Parameters: credp Pointer to address containing
4725 * credential to be freed
4727 * Returns: TRUE if the credential must be destroyed by the caller.
4731 * *credp Set to NOCRED
4733 * Notes: This function assumes the credential hash lock is held.
4735 * This function is internal use only, since the hash lock is
4736 * scoped to this compilation unit.
4738 * This function destroys the contents of the pointer passed by
4739 * the caller to prevent the caller accidentally attempting to
4740 * release a given reference twice in error.
4742 * The last reference is considered to be released when a release
4743 * of a credential of a reference count of 2 occurs; this is an
4744 * intended effect, to take into account the reference held by
4745 * the credential hash, which is released at the same time.
4748 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4751 boolean_t destroy_it
= FALSE
;
4753 KAUTH_CRED_HASH_LOCK_ASSERT();
4754 NULLCRED_CHECK(*credp
);
4756 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4760 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4762 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4765 #if 0 // use this to watch a specific credential
4766 if ( is_target_cred( *credp
) != 0 ) {
4772 * If the old_value is 2, then we have just released the last external
4773 * reference to this credential
4775 if (old_value
< 3) {
4776 /* The last absolute reference is our credential hash table */
4777 destroy_it
= kauth_cred_remove(*credp
);
4780 if (destroy_it
== FALSE
) {
4784 return (destroy_it
);
4791 * Description: Release a credential reference while holding the credential
4792 * hash lock; when the last reference is released, the credential
4795 * Parameters: credp Pointer to address containing
4796 * credential to be freed
4801 * *credp Set to NOCRED
4803 * Notes: See kauth_cred_unref_hashlocked() for more information.
4807 kauth_cred_unref(kauth_cred_t
*credp
)
4809 boolean_t destroy_it
;
4811 KAUTH_CRED_HASH_LOCK();
4812 destroy_it
= kauth_cred_unref_hashlocked(credp
);
4813 KAUTH_CRED_HASH_UNLOCK();
4815 if (destroy_it
== TRUE
) {
4816 assert(*credp
!= NOCRED
);
4818 mac_cred_label_destroy(*credp
);
4820 AUDIT_SESSION_UNREF(*credp
);
4822 (*credp
)->cr_ref
= 0;
4823 FREE_ZONE(*credp
, sizeof(*(*credp
)), M_CRED
);
4833 * Description: release a credential reference; when the last reference is
4834 * released, the credential will be freed
4836 * Parameters: cred Credential to release
4840 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4841 * clear the pointer in the caller to avoid multiple releases of
4842 * the same credential. The currently recommended interface is
4843 * kauth_cred_unref().
4846 kauth_cred_rele(kauth_cred_t cred
)
4848 kauth_cred_unref(&cred
);
4850 #endif /* !__LP64__ */
4856 * Description: Duplicate a credential via alloc and copy; the new credential
4859 * Parameters: cred The credential to duplicate
4861 * Returns: (kauth_cred_t) The duplicate credential
4863 * Notes: The typical value to calling this routine is if you are going
4864 * to modify an existing credential, and expect to need a new one
4865 * from the hash cache.
4867 * This should probably not be used in the majority of cases;
4868 * if you are using it instead of kauth_cred_create(), you are
4869 * likely making a mistake.
4871 * The newly allocated credential is copied as part of the
4872 * allocation process, with the exception of the reference
4873 * count, which is set to 1 to indicate a single reference
4874 * held by the caller.
4876 * Since newly allocated credentials have no external pointers
4877 * referencing them, prior to making them visible in an externally
4878 * visible pointer (e.g. by adding them to the credential hash
4879 * cache) is the only legal time in which an existing credential
4880 * can be safely initialized or modified directly.
4882 * After initialization, the caller is expected to call the
4883 * function kauth_cred_add() to add the credential to the hash
4884 * cache, after which time it's frozen and becomes publicly
4887 * The release protocol depends on kauth_hash_add() being called
4888 * before kauth_cred_rele() (there is a diagnostic panic which
4889 * will trigger if this protocol is not observed).
4893 kauth_cred_dup(kauth_cred_t cred
)
4895 kauth_cred_t newcred
;
4897 struct label
*temp_label
;
4901 if (cred
== NOCRED
|| cred
== FSCRED
)
4902 panic("kauth_cred_dup: bad credential");
4904 newcred
= kauth_cred_alloc();
4905 if (newcred
!= NULL
) {
4907 temp_label
= newcred
->cr_label
;
4909 bcopy(cred
, newcred
, sizeof(*newcred
));
4911 newcred
->cr_label
= temp_label
;
4912 mac_cred_label_associate(cred
, newcred
);
4914 AUDIT_SESSION_REF(cred
);
4915 newcred
->cr_ref
= 1;
4921 * kauth_cred_copy_real
4923 * Description: Returns a credential based on the passed credential but which
4924 * reflects the real rather than effective UID and GID.
4926 * Parameters: cred The credential from which to
4927 * derive the new credential
4929 * Returns: (kauth_cred_t) The copied credential
4931 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4932 * result, the caller is responsible for dropping BOTH the
4933 * additional reference on the passed cred (if any), and the
4934 * credential returned by this function. The drop should be
4935 * via the kauth_cred_unref() KPI.
4938 kauth_cred_copy_real(kauth_cred_t cred
)
4940 kauth_cred_t newcred
= NULL
, found_cred
;
4941 struct ucred temp_cred
;
4942 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4943 posix_cred_t pcred
= posix_cred_get(cred
);
4945 /* if the credential is already 'real', just take a reference */
4946 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4947 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4948 kauth_cred_ref(cred
);
4953 * Look up in cred hash table to see if we have a matching credential
4954 * with the new values.
4956 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4957 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4958 /* displacing a supplementary group opts us out of memberd */
4959 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4960 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4961 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4964 * If the cred is not opted out, make sure we are using the r/euid
4967 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4968 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4973 KAUTH_CRED_HASH_LOCK();
4974 found_cred
= kauth_cred_find(&temp_cred
);
4975 if (found_cred
== cred
) {
4976 /* same cred so just bail */
4977 KAUTH_CRED_HASH_UNLOCK();
4980 if (found_cred
!= NULL
) {
4982 * Found a match so we bump reference count on new
4983 * one. We leave the old one alone.
4985 kauth_cred_ref(found_cred
);
4986 KAUTH_CRED_HASH_UNLOCK();
4991 * Must allocate a new credential, copy in old credential
4992 * data and update the real user and group IDs.
4994 newcred
= kauth_cred_dup(&temp_cred
);
4995 err
= kauth_cred_add(newcred
);
4996 KAUTH_CRED_HASH_UNLOCK();
4998 /* Retry if kauth_cred_add() fails */
5002 mac_cred_label_destroy(newcred
);
5004 AUDIT_SESSION_UNREF(newcred
);
5006 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
5017 * Description: Common code to update a credential
5019 * Parameters: old_cred Reference counted credential
5021 * model_cred Non-reference counted model
5022 * credential to apply to the
5023 * credential to be updated
5024 * retain_auditinfo Flag as to whether or not the
5025 * audit information should be
5026 * copied from the old_cred into
5029 * Returns: (kauth_cred_t) The updated credential
5031 * IMPORTANT: This function will potentially return a credential other than
5032 * the one it is passed, and if so, it will have dropped the
5033 * reference on the passed credential. All callers should be
5034 * aware of this, and treat this function as an unref + ref,
5035 * potentially on different credentials.
5037 * Because of this, the caller is expected to take its own
5038 * reference on the credential passed as the first parameter,
5039 * and be prepared to release the reference on the credential
5040 * that is returned to them, if it is not intended to be a
5041 * persistent reference.
5044 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
5045 boolean_t retain_auditinfo
)
5047 kauth_cred_t found_cred
, new_cred
= NULL
;
5050 * Make sure we carry the auditinfo forward to the new credential
5051 * unless we are actually updating the auditinfo.
5053 if (retain_auditinfo
) {
5054 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
5055 sizeof(model_cred
->cr_audit
));
5061 KAUTH_CRED_HASH_LOCK();
5062 found_cred
= kauth_cred_find(model_cred
);
5063 if (found_cred
== old_cred
) {
5064 /* same cred so just bail */
5065 KAUTH_CRED_HASH_UNLOCK();
5068 if (found_cred
!= NULL
) {
5069 boolean_t destroy_it
;
5071 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
5073 * Found a match so we bump reference count on new
5074 * one and decrement reference count on the old one.
5076 kauth_cred_ref(found_cred
);
5077 destroy_it
= kauth_cred_unref_hashlocked(&old_cred
);
5078 KAUTH_CRED_HASH_UNLOCK();
5079 if (destroy_it
== TRUE
) {
5080 assert(old_cred
!= NOCRED
);
5082 mac_cred_label_destroy(old_cred
);
5084 AUDIT_SESSION_UNREF(old_cred
);
5086 old_cred
->cr_ref
= 0;
5087 FREE_ZONE(old_cred
, sizeof(*old_cred
), M_CRED
);
5095 * Must allocate a new credential using the model. also
5096 * adds the new credential to the credential hash table.
5098 new_cred
= kauth_cred_dup(model_cred
);
5099 err
= kauth_cred_add(new_cred
);
5100 KAUTH_CRED_HASH_UNLOCK();
5102 /* retry if kauth_cred_add returns non zero value */
5106 mac_cred_label_destroy(new_cred
);
5108 AUDIT_SESSION_UNREF(new_cred
);
5110 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
5114 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
5115 kauth_cred_unref(&old_cred
);
5123 * Description: Add the given credential to our credential hash table and
5124 * take an additional reference to account for our use of the
5125 * credential in the hash table
5127 * Parameters: new_cred Credential to insert into cred
5130 * Returns: 0 Success
5131 * -1 Hash insertion failed: caller
5134 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5136 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5139 kauth_cred_add(kauth_cred_t new_cred
)
5143 KAUTH_CRED_HASH_LOCK_ASSERT();
5145 hash_key
= kauth_cred_get_hashkey(new_cred
);
5146 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5148 /* race fix - there is a window where another matching credential
5149 * could have been inserted between the time this one was created and we
5150 * got the hash lock. If we find a match return an error and have the
5153 if (kauth_cred_find(new_cred
) != NULL
) {
5157 /* take a reference for our use in credential hash table */
5158 kauth_cred_ref(new_cred
);
5160 /* insert the credential into the hash table */
5161 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
5170 * Description: Remove the given credential from our credential hash table
5172 * Parameters: cred Credential to remove from cred
5175 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not.
5177 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5179 * Notes: The check for the reference increment after entry is generally
5180 * agree to be safe, since we use atomic operations, and the
5181 * following code occurs with the hash lock held; in theory, this
5182 * protects us from the 2->1 reference that gets us here.
5185 kauth_cred_remove(kauth_cred_t cred
)
5188 kauth_cred_t found_cred
;
5190 hash_key
= kauth_cred_get_hashkey(cred
);
5191 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5194 if (cred
->cr_ref
< 1)
5195 panic("cred reference underflow");
5196 if (cred
->cr_ref
> 1)
5197 return (FALSE
); /* someone else got a ref */
5199 /* Find cred in the credential hash table */
5200 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5201 if (found_cred
== cred
) {
5202 /* found a match, remove it from the hash table */
5203 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
5204 #if KAUTH_CRED_HASH_DEBUG
5211 /* Did not find a match... this should not happen! XXX Make panic? */
5212 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
5220 * Description: Using the given credential data, look for a match in our
5221 * credential hash table
5223 * Parameters: cred Credential to lookup in cred
5226 * Returns: NULL Not found
5227 * !NULL Matching credential already in
5230 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5233 kauth_cred_find(kauth_cred_t cred
)
5236 kauth_cred_t found_cred
;
5237 posix_cred_t pcred
= posix_cred_get(cred
);
5239 KAUTH_CRED_HASH_LOCK_ASSERT();
5241 #if KAUTH_CRED_HASH_DEBUG
5242 static int test_count
= 0;
5245 if ((test_count
% 200) == 0) {
5246 kauth_cred_hash_print();
5250 hash_key
= kauth_cred_get_hashkey(cred
);
5251 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5253 /* Find cred in the credential hash table */
5254 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5256 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
5259 * don't worry about the label unless the flags in
5260 * either credential tell us to.
5262 match
= (bcmp(found_pcred
, pcred
, sizeof (*pcred
)) == 0) ? TRUE
: FALSE
;
5263 match
= match
&& ((bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
5264 sizeof(cred
->cr_audit
)) == 0) ? TRUE
: FALSE
);
5266 if (((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) ||
5267 ((pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0)) {
5268 match
= match
&& mac_cred_label_compare(found_cred
->cr_label
,
5277 /* No match found */
5286 * Description: Generates a hash key using data that makes up a credential;
5289 * Parameters: datap Pointer to data to hash
5290 * data_len Count of bytes to hash
5291 * start_key Start key value
5293 * Returns: (u_long) Returned hash key
5295 static inline u_long
5296 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5298 u_long hash_key
= start_key
;
5301 while (data_len
> 0) {
5302 hash_key
= (hash_key
<< 4) + *datap
++;
5303 temp
= hash_key
& 0xF0000000;
5305 hash_key
^= temp
>> 24;
5315 * kauth_cred_get_hashkey
5317 * Description: Generate a hash key using data that makes up a credential;
5318 * based on ElfHash. We hash on the entire credential data,
5319 * not including the ref count or the TAILQ, which are mutable;
5320 * everything else isn't.
5322 * Parameters: cred Credential for which hash is
5325 * Returns: (u_long) Returned hash key
5327 * Notes: When actually moving the POSIX credential into a real label,
5328 * remember to update this hash computation.
5331 kauth_cred_get_hashkey(kauth_cred_t cred
)
5334 posix_cred_t pcred
= posix_cred_get(cred
);
5336 u_long hash_key
= 0;
5338 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5339 sizeof (struct posix_cred
),
5341 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5342 sizeof(struct au_session
),
5345 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5346 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5347 sizeof (struct label
),
5355 #if KAUTH_CRED_HASH_DEBUG
5357 * kauth_cred_hash_print
5359 * Description: Print out cred hash cache table information for debugging
5360 * purposes, including the credential contents
5362 * Parameters: (void)
5366 * Implicit returns: Results in console output
5369 kauth_cred_hash_print(void)
5372 kauth_cred_t found_cred
;
5374 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
5375 /* count slot hits, misses, collisions, and max depth */
5376 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5377 printf("[%02d] ", i
);
5379 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5384 kauth_cred_print(found_cred
);
5388 printf("NOCRED \n");
5392 #endif /* KAUTH_CRED_HASH_DEBUG */
5395 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5399 * Description: Print out an individual credential's contents for debugging
5402 * Parameters: cred The credential to print out
5406 * Implicit returns: Results in console output
5409 kauth_cred_print(kauth_cred_t cred
)
5413 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
);
5414 printf("group count %d gids ", cred
->cr_ngroups
);
5415 for (i
= 0; i
< NGROUPS
; i
++) {
5418 printf("%d ", cred
->cr_groups
[i
]);
5420 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5421 printf("auditinfo_addr %d %d %d %d %d %d\n",
5422 cred
->cr_audit
.s_aia_p
->ai_auid
,
5423 cred
->cr_audit
.as_mask
.am_success
,
5424 cred
->cr_audit
.as_mask
.am_failure
,
5425 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5426 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5427 cred
->cr_audit
.as_aia_p
->ai_asid
);
5430 int is_target_cred( kauth_cred_t the_cred
)
5432 if ( the_cred
->cr_uid
!= 0 )
5434 if ( the_cred
->cr_ruid
!= 0 )
5436 if ( the_cred
->cr_svuid
!= 0 )
5438 if ( the_cred
->cr_ngroups
!= 11 )
5440 if ( the_cred
->cr_groups
[0] != 11 )
5442 if ( the_cred
->cr_groups
[1] != 81 )
5444 if ( the_cred
->cr_groups
[2] != 63947 )
5446 if ( the_cred
->cr_groups
[3] != 80288 )
5448 if ( the_cred
->cr_groups
[4] != 89006 )
5450 if ( the_cred
->cr_groups
[5] != 52173 )
5452 if ( the_cred
->cr_groups
[6] != 84524 )
5454 if ( the_cred
->cr_groups
[7] != 79 )
5456 if ( the_cred
->cr_groups
[8] != 80292 )
5458 if ( the_cred
->cr_groups
[9] != 80 )
5460 if ( the_cred
->cr_groups
[10] != 90824 )
5462 if ( the_cred
->cr_rgid
!= 11 )
5464 if ( the_cred
->cr_svgid
!= 11 )
5466 if ( the_cred
->cr_gmuid
!= 3475 )
5468 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5471 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5473 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5475 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5477 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5479 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5481 if ( the_cred->cr_flags != 0 )
5484 return( -1 ); // found target cred
5487 void get_backtrace( void )
5490 void * my_stack
[ MAX_STACK_DEPTH
];
5493 if ( cred_debug_buf_p
== NULL
) {
5494 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5495 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5498 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5499 /* buffer is full */
5503 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5504 if ( my_depth
== 0 ) {
5505 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5509 /* fill new backtrace */
5510 my_slot
= cred_debug_buf_p
->next_slot
;
5511 cred_debug_buf_p
->next_slot
++;
5512 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5513 for ( i
= 0; i
< my_depth
; i
++ ) {
5514 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5521 /* subset of struct ucred for use in sysctl_dump_creds */
5522 struct debug_ucred
{
5524 u_long cr_ref
; /* reference count */
5525 uid_t cr_uid
; /* effective user id */
5526 uid_t cr_ruid
; /* real user id */
5527 uid_t cr_svuid
; /* saved user id */
5528 short cr_ngroups
; /* number of groups in advisory list */
5529 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5530 gid_t cr_rgid
; /* real group id */
5531 gid_t cr_svgid
; /* saved group id */
5532 uid_t cr_gmuid
; /* UID for group membership purposes */
5533 struct auditinfo_addr cr_audit
; /* user auditing data. */
5534 void *cr_label
; /* MACF label */
5535 int cr_flags
; /* flags on credential */
5537 typedef struct debug_ucred debug_ucred
;
5539 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5540 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5543 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5547 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5549 int i
, j
, counter
= 0;
5552 kauth_cred_t found_cred
;
5553 debug_ucred
* cred_listp
;
5554 debug_ucred
* nextp
;
5556 /* This is a readonly node. */
5557 if (req
->newptr
!= USER_ADDR_NULL
)
5560 /* calculate space needed */
5561 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5562 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5567 /* they are querying us so just return the space required. */
5568 if (req
->oldptr
== USER_ADDR_NULL
) {
5569 counter
+= 10; // add in some padding;
5570 req
->oldidx
= counter
* sizeof(debug_ucred
);
5574 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5575 if ( cred_listp
== NULL
) {
5579 /* fill in creds to send back */
5582 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5583 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5584 nextp
->credp
= found_cred
;
5585 nextp
->cr_ref
= found_cred
->cr_ref
;
5586 nextp
->cr_uid
= found_cred
->cr_uid
;
5587 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5588 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5589 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5590 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5591 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5593 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5594 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5595 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5596 nextp
->cr_audit
.ai_auid
=
5597 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5598 nextp
->cr_audit
.ai_mask
.am_success
=
5599 found_cred
->cr_audit
.as_mask
.am_success
;
5600 nextp
->cr_audit
.ai_mask
.am_failure
=
5601 found_cred
->cr_audit
.as_mask
.am_failure
;
5602 nextp
->cr_audit
.ai_termid
.at_port
=
5603 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5604 nextp
->cr_audit
.ai_termid
.at_type
=
5605 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5606 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5607 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5608 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5609 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5610 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5611 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5612 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5613 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5614 nextp
->cr_audit
.ai_asid
=
5615 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5616 nextp
->cr_audit
.ai_flags
=
5617 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5618 nextp
->cr_label
= found_cred
->cr_label
;
5619 nextp
->cr_flags
= found_cred
->cr_flags
;
5621 space
+= sizeof(debug_ucred
);
5622 if ( space
> req
->oldlen
) {
5623 FREE(cred_listp
, M_TEMP
);
5628 req
->oldlen
= space
;
5629 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5630 FREE(cred_listp
, M_TEMP
);
5635 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5636 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5639 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5643 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5648 cred_debug_buffer
* bt_bufp
;
5649 cred_backtrace
* nextp
;
5651 /* This is a readonly node. */
5652 if (req
->newptr
!= USER_ADDR_NULL
)
5655 if ( cred_debug_buf_p
== NULL
) {
5659 /* calculate space needed */
5660 space
= sizeof( cred_debug_buf_p
->next_slot
);
5661 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5663 /* they are querying us so just return the space required. */
5664 if (req
->oldptr
== USER_ADDR_NULL
) {
5665 req
->oldidx
= space
;
5669 if ( space
> req
->oldlen
) {
5673 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5674 if ( bt_bufp
== NULL
) {
5678 /* fill in backtrace info to send back */
5679 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5680 space
= sizeof(bt_bufp
->next_slot
);
5682 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5683 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5684 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5685 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5686 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5688 space
+= sizeof(*nextp
);
5691 req
->oldlen
= space
;
5692 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5693 FREE(bt_bufp
, M_TEMP
);
5697 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5701 **********************************************************************
5702 * The following routines will be moved to a policy_posix.c module at
5703 * some future point.
5704 **********************************************************************
5710 * Description: Helper function to create a kauth_cred_t credential that is
5711 * initally labelled with a specific POSIX credential label
5713 * Parameters: pcred The posix_cred_t to use as the initial
5716 * Returns: (kauth_cred_t) The credential that was found in the
5718 * NULL kauth_cred_add() failed, or there was
5719 * no egid specified, or we failed to
5720 * attach a label to the new credential
5722 * Notes: This function currently wraps kauth_cred_create(), and is the
5723 * only consumer of that ill-fated function, apart from bsd_init().
5724 * It exists solely to support the NFS server code creation of
5725 * credentials based on the over-the-wire RPC calls containing
5726 * traditional POSIX credential information being tunneled to
5727 * the server host from the client machine.
5729 * In the future, we hope this function goes away.
5731 * In the short term, it creates a temporary credential, puts
5732 * the POSIX information from NFS into it, and then calls
5733 * kauth_cred_create(), as an internal implementation detail.
5735 * If we have to keep it around in the medium term, it will
5736 * create a new kauth_cred_t, then label it with a POSIX label
5737 * corresponding to the contents of the kauth_cred_t. If the
5738 * policy_posix MACF module is not loaded, it will instead
5739 * substitute a posix_cred_t which GRANTS all access (effectively
5740 * a "root" credential) in order to not prevent NFS from working
5741 * in the case that we are not supporting POSIX credentials.
5744 posix_cred_create(posix_cred_t pcred
)
5746 struct ucred temp_cred
;
5748 bzero(&temp_cred
, sizeof(temp_cred
));
5749 temp_cred
.cr_posix
= *pcred
;
5751 return kauth_cred_create(&temp_cred
);
5758 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5759 * any, which is associated with it.
5761 * Parameters: cred The credential to obtain the label from
5763 * Returns: posix_cred_t The POSIX credential label
5765 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5766 * this function will return a pointer to a posix_cred_t which
5767 * GRANTS all access (effectively, a "root" credential). This is
5768 * necessary to support legacy code which insists on tightly
5769 * integrating POSIX credentials into its APIs, including, but
5770 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5771 * NFSv3, signals, dtrace, and a large number of kauth routines
5772 * used to implement POSIX permissions related system calls.
5774 * In the event that the policy_posix MACF module IS loaded, and
5775 * there is no POSIX label on the kauth_cred_t credential, this
5776 * function will return a pointer to a posix_cred_t which DENIES
5777 * all access (effectively, a "deny rights granted by POSIX"
5778 * credential). This is necessary to support the concept of a
5779 * transiently loaded POSIX policy, or kauth_cred_t credentials
5780 * which can not be used in conjunctions with POSIX permissions
5783 * This function currently returns the address of the cr_posix
5784 * field of the supplied kauth_cred_t credential, and as such
5785 * currently can not fail. In the future, this will not be the
5789 posix_cred_get(kauth_cred_t cred
)
5791 return(&cred
->cr_posix
);
5798 * Description: Label a kauth_cred_t with a POSIX credential label
5800 * Parameters: cred The credential to label
5801 * pcred The POSIX credential t label it with
5805 * Notes: This function is currently void in order to permit it to fit
5806 * in with the current MACF framework label methods which allow
5807 * labeling to fail silently. This is like acceptable for
5808 * mandatory access controls, but not for POSIX, since those
5809 * access controls are advisory. We will need to consider a
5810 * return value in a future version of the MACF API.
5812 * This operation currently cannot fail, as currently the POSIX
5813 * credential is a subfield of the kauth_cred_t (ucred), which
5814 * MUST be valid. In the future, this will not be the case.
5817 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5819 cred
->cr_posix
= *pcred
; /* structure assign for now */
5826 * Description: Perform a POSIX access check for a protected object
5828 * Parameters: cred The credential to check
5829 * object_uid The POSIX UID of the protected object
5830 * object_gid The POSIX GID of the protected object
5831 * object_mode The POSIX mode of the protected object
5832 * mode_req The requested POSIX access rights
5834 * Returns 0 Access is granted
5835 * EACCES Access is denied
5837 * Notes: This code optimizes the case where the world and group rights
5838 * would both grant the requested rights to avoid making a group
5839 * membership query. This is a big performance win in the case
5840 * where this is true.
5843 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5846 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5847 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5848 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5851 * Check first for owner rights
5853 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5857 * Combined group and world rights check, if we don't have owner rights
5859 * OPTIMIZED: If group and world rights would grant the same bits, and
5860 * they set of requested bits is in both, then we can simply check the
5861 * world rights, avoiding a group membership check, which is expensive.
5863 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5867 * NON-OPTIMIZED: requires group membership check.
5869 if ((mode_req
& mode_group
) != mode_req
) {
5871 * exclusion group : treat errors as "is a member"
5873 * NON-OPTIMIZED: +group would deny; must check group
5875 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5877 * DENY: +group denies
5881 if ((mode_req
& mode_world
) != mode_req
) {
5883 * DENY: both -group & world would deny
5888 * ALLOW: allowed by -group and +world
5895 * inclusion group; treat errors as "not a member"
5897 * NON-OPTIMIZED: +group allows, world denies; must
5900 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5902 * ALLOW: allowed by +group
5906 if ((mode_req
& mode_world
) != mode_req
) {
5908 * DENY: both -group & world would deny
5913 * ALLOW: allowed by -group and +world