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 #include <IOKit/IOBSD.h>
79 void mach_kauth_cred_uthread_update( void );
81 #define CRED_DIAGNOSTIC 0
83 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
85 /* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */
99 # define K_UUID_FMT "%08x:%08x:%08x:%08x"
100 # 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]
101 # define KAUTH_DEBUG(fmt, args...) do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0)
105 * Credential debugging; we can track entry into a function that might
106 * change a credential, and we can track actual credential changes that
109 * Note: Does *NOT* currently include per-thread credential changes
113 #define DEBUG_CRED_ENTER printf
114 #define DEBUG_CRED_CHANGE printf
115 extern void kauth_cred_print(kauth_cred_t cred
);
117 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
119 int is_target_cred( kauth_cred_t the_cred
);
120 void get_backtrace( void );
122 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
123 __unused
int arg2
, struct sysctl_req
*req
);
125 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
126 __unused
int arg2
, struct sysctl_req
*req
);
128 #define MAX_STACK_DEPTH 8
129 struct cred_backtrace
{
131 void * stack
[ MAX_STACK_DEPTH
];
133 typedef struct cred_backtrace cred_backtrace
;
135 #define MAX_CRED_BUFFER_SLOTS 200
136 struct cred_debug_buffer
{
138 cred_backtrace stack_buffer
[ MAX_CRED_BUFFER_SLOTS
];
140 typedef struct cred_debug_buffer cred_debug_buffer
;
141 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
143 #else /* !DEBUG_CRED */
145 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
146 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
148 #endif /* !DEBUG_CRED */
150 #if CONFIG_EXT_RESOLVER
152 * Interface to external identity resolver.
154 * The architecture of the interface is simple; the external resolver calls
155 * in to get work, then calls back with completed work. It also calls us
156 * to let us know that it's (re)started, so that we can resubmit work if it
160 static lck_mtx_t
*kauth_resolver_mtx
;
161 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
162 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
164 static volatile pid_t kauth_resolver_identity
;
165 static int kauth_identitysvc_has_registered
;
166 static int kauth_resolver_registered
;
167 static uint32_t kauth_resolver_sequence
;
168 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
170 struct kauth_resolver_work
{
171 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
172 struct kauth_identity_extlookup kr_work
;
177 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
178 #define KAUTH_REQUEST_SUBMITTED (1<<1)
179 #define KAUTH_REQUEST_DONE (1<<2)
183 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
184 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
185 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
187 /* Number of resolver timeouts between logged complaints */
188 #define KAUTH_COMPLAINT_INTERVAL 1000
189 int kauth_resolver_timeout_cnt
= 0;
191 #if DEVELOPMENT || DEBUG
192 /* Internal builds get different (less ambiguous) breadcrumbs. */
193 #define KAUTH_RESOLVER_FAILED_ERRCODE EOWNERDEAD
195 /* But non-Internal builds get errors that are allowed by standards. */
196 #define KAUTH_RESOLVER_FAILED_ERRCODE EIO
197 #endif /* DEVELOPMENT || DEBUG */
199 int kauth_resolver_failed_cnt
= 0;
200 #define RESOLVER_FAILED_MESSAGE(fmt, args...) \
202 if (!(kauth_resolver_failed_cnt++ % 100)) { \
203 printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ##args); \
207 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
208 static int kauth_resolver_complete(user_addr_t message
);
209 static int kauth_resolver_getwork(user_addr_t message
);
210 static int kauth_resolver_getwork2(user_addr_t message
);
211 static __attribute__((noinline
)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
212 struct kauth_resolver_work
*);
214 #define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
216 struct kauth_identity
{
217 TAILQ_ENTRY(kauth_identity
) ki_link
;
222 gid_t ki_supgrps
[NGROUPS
];
225 const char *ki_name
; /* string name from string cache */
227 * Expiry times are the earliest time at which we will disregard the
228 * cached state and go to userland. Before then if the valid bit is
229 * set, we will return the cached value. If it's not set, we will
230 * not go to userland to resolve, just assume that there is no answer
233 time_t ki_groups_expiry
;
234 time_t ki_guid_expiry
;
235 time_t ki_ntsid_expiry
;
238 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
239 static lck_mtx_t
*kauth_identity_mtx
;
240 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
241 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
242 #define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
243 static int kauth_identity_cachemax
= KAUTH_IDENTITY_CACHEMAX_DEFAULT
;
244 static int kauth_identity_count
;
246 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
247 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
248 const char *name
, int nametype
);
249 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
250 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
251 static void kauth_identity_trimcache(int newsize
);
252 static void kauth_identity_lru(struct kauth_identity
*kip
);
253 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
254 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
255 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
256 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
257 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
258 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
259 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
261 struct kauth_group_membership
{
262 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
263 uid_t gm_uid
; /* the identity whose membership we're recording */
264 gid_t gm_gid
; /* group of which they are a member */
265 time_t gm_expiry
; /* TTL for the membership, or 0 for persistent entries */
267 #define KAUTH_GROUP_ISMEMBER (1<<0)
270 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
271 static lck_mtx_t
*kauth_groups_mtx
;
272 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
273 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
274 #define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
275 static int kauth_groups_cachemax
= KAUTH_GROUPS_CACHEMAX_DEFAULT
;
276 static int kauth_groups_count
;
278 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
279 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
280 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
281 static void kauth_groups_trimcache(int newsize
);
283 #endif /* CONFIG_EXT_RESOLVER */
285 #define KAUTH_CRED_TABLE_SIZE 97
287 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
288 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
290 #define KAUTH_CRED_HASH_DEBUG 0
292 static int kauth_cred_add(kauth_cred_t new_cred
);
293 static boolean_t
kauth_cred_remove(kauth_cred_t cred
);
294 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
295 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
296 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
297 static boolean_t
kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
299 #if KAUTH_CRED_HASH_DEBUG
300 static int kauth_cred_count
= 0;
301 static void kauth_cred_hash_print(void);
302 static void kauth_cred_print(kauth_cred_t cred
);
305 #if CONFIG_EXT_RESOLVER
308 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
310 * Description: Waits for the user space daemon to respond to the request
311 * we made. Function declared non inline to be visible in
312 * stackshots and spindumps as well as debugging.
314 * Parameters: workp Work queue entry.
316 * Returns: 0 on Success.
317 * EIO if Resolver is dead.
318 * EINTR thread interrupted in msleep
319 * EWOULDBLOCK thread timed out in msleep
320 * ERESTART returned by msleep.
323 static __attribute__((noinline
)) int
324 __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
325 struct kauth_resolver_work
*workp
)
330 /* we could compute a better timeout here */
331 ts
.tv_sec
= kauth_resolver_timeout
;
333 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
334 /* request has been completed? */
335 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
337 /* woken because the resolver has died? */
338 if (kauth_resolver_identity
== 0) {
339 RESOLVER_FAILED_MESSAGE("kauth external resolver died while while waiting for work to complete");
340 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
352 * kauth_resolver_init
354 * Description: Initialize the daemon side of the credential identity resolver
360 * Notes: Initialize the credential identity resolver for use; the
361 * credential identity resolver is the KPI used by the user
362 * space credential identity resolver daemon to communicate
363 * with the kernel via the identitysvc() system call..
365 * This is how membership in more than 16 groups (1 effective
366 * and 15 supplementary) is supported, and also how UID's,
367 * UUID's, and so on, are translated to/from POSIX credential
370 * The credential identity resolver operates by attempting to
371 * determine identity first from the credential, then from
372 * the kernel credential identity cache, and finally by
373 * enqueueing a request to a user space daemon.
375 * This function is called from kauth_init() in the file
376 * kern_authorization.c.
379 kauth_resolver_init(void)
381 TAILQ_INIT(&kauth_resolver_unsubmitted
);
382 TAILQ_INIT(&kauth_resolver_submitted
);
383 TAILQ_INIT(&kauth_resolver_done
);
384 kauth_resolver_sequence
= 31337;
385 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
390 * kauth_resolver_submit
392 * Description: Submit an external credential identity resolution request to
393 * the user space daemon.
395 * Parameters: lkp A pointer to an external
397 * extend_data extended data for kr_extend
400 * EWOULDBLOCK No resolver registered
401 * EINTR Operation interrupted (e.g. by
403 * ENOMEM Could not allocate work item
404 * copyinstr:EFAULT Bad message from user space
405 * workp->kr_result:??? An error from the user space
406 * daemon (includes ENOENT!)
411 * Notes: Allocate a work queue entry, submit the work and wait for
412 * the operation to either complete or time out. Outstanding
413 * operations may also be cancelled.
415 * Submission is by means of placing the item on a work queue
416 * which is serviced by an external resolver thread calling
417 * into the kernel. The caller then sleeps until timeout,
418 * cancellation, or an external resolver thread calls in with
419 * a result message to kauth_resolver_complete(). All of these
420 * events wake the caller back up.
422 * This code is called from either kauth_cred_ismember_gid()
423 * for a group membership request, or it is called from
424 * kauth_cred_cache_lookup() when we get a cache miss.
427 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
429 struct kauth_resolver_work
*workp
, *killp
;
431 int error
, shouldfree
;
433 /* no point actually blocking if the resolver isn't up yet */
434 if (kauth_resolver_identity
== 0) {
436 * We've already waited an initial <kauth_resolver_timeout>
437 * seconds with no result.
439 * Sleep on a stack address so no one wakes us before timeout;
440 * we sleep a half a second in case we are a high priority
441 * process, so that memberd doesn't starve while we are in a
442 * tight loop between user and kernel, eating all the CPU.
444 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
445 if (kauth_resolver_identity
== 0) {
447 * if things haven't changed while we were asleep,
448 * tell the caller we couldn't get an authoritative
455 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
459 workp
->kr_work
= *lkp
;
460 workp
->kr_extend
= extend_data
;
462 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
463 workp
->kr_result
= 0;
466 * We insert the request onto the unsubmitted queue, the call in from
467 * the resolver will it to the submitted thread when appropriate.
469 KAUTH_RESOLVER_LOCK();
470 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
471 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
474 * XXX We *MUST NOT* attempt to coalesce identical work items due to
475 * XXX the inability to ensure order of update of the request item
476 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
477 * XXX for each item repeat the update when they wake up.
479 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
482 * Wake up an external resolver thread to deal with the new work; one
483 * may not be available, and if not, then the request will be grabbed
484 * when a resolver thread comes back into the kernel to request new
487 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
488 error
= __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp
);
490 /* if the request was processed, copy the result */
492 *lkp
= workp
->kr_work
;
494 if (error
== EWOULDBLOCK
) {
495 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
496 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
497 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
500 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
502 * If the request timed out and was never collected, the resolver
503 * is dead and probably not coming back anytime soon. In this
504 * case we revert to no-resolver behaviour, and punt all the other
505 * sleeping requests to clear the backlog.
507 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
510 * Make the current resolver non-authoritative, and mark it as
511 * no longer registered to prevent kauth_cred_ismember_gid()
512 * enqueueing more work until a new one is registered. This
513 * mitigates the damage a crashing resolver may inflict.
515 kauth_resolver_identity
= 0;
516 kauth_resolver_registered
= 0;
518 /* kill all the other requestes that are waiting as well */
519 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
521 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
523 /* Cause all waiting-for-work threads to return EIO */
524 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
529 * drop our reference on the work item, and note whether we should
532 if (--workp
->kr_refs
<= 0) {
533 /* work out which list we have to remove it from */
534 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
535 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
536 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
537 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
538 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
539 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
541 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
545 /* someone else still has a reference on this request */
549 /* collect request result */
551 error
= workp
->kr_result
;
553 KAUTH_RESOLVER_UNLOCK();
556 * If we dropped the last reference, free the request.
559 FREE(workp
, M_KAUTH
);
562 KAUTH_DEBUG("RESOLVER - returning %d", error
);
570 * Description: System call interface for the external identity resolver.
572 * Parameters: uap->message Message from daemon to kernel
574 * Returns: 0 Successfully became resolver
575 * EPERM Not the resolver process
576 * kauth_authorize_generic:EPERM Not root user
577 * kauth_resolver_complete:EIO
578 * kauth_resolver_complete:EFAULT
579 * kauth_resolver_getwork:EINTR
580 * kauth_resolver_getwork:EFAULT
582 * Notes: This system call blocks until there is work enqueued, at
583 * which time the kernel wakes it up, and a message from the
584 * kernel is copied out to the identity resolution daemon, which
585 * proceed to attempt to resolve it. When the resolution has
586 * completed (successfully or not), the daemon called back into
587 * this system call to give the result to the kernel, and wait
588 * for the next request.
591 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
593 int opcode
= uap
->opcode
;
594 user_addr_t message
= uap
->message
;
595 struct kauth_resolver_work
*workp
;
596 struct kauth_cache_sizes sz_arg
= {};
600 if (!IOTaskHasEntitlement(current_task(), IDENTITYSVC_ENTITLEMENT
)) {
601 KAUTH_DEBUG("RESOLVER - pid %d not entitled to call identitysvc", current_proc()->p_pid
);
606 * New server registering itself.
608 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
609 new_id
= current_proc()->p_pid
;
610 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
611 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
614 KAUTH_RESOLVER_LOCK();
615 if (kauth_resolver_identity
!= new_id
) {
616 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
618 * We have a new server, so assume that all the old requests have been lost.
620 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
621 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
622 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
623 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
624 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
627 * Allow user space resolver to override the
628 * external resolution timeout
630 if (message
> 30 && message
< 10000) {
631 kauth_resolver_timeout
= message
;
632 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
634 kauth_resolver_identity
= new_id
;
635 kauth_resolver_registered
= 1;
636 kauth_identitysvc_has_registered
= 1;
637 wakeup(&kauth_resolver_unsubmitted
);
639 KAUTH_RESOLVER_UNLOCK();
644 * Beyond this point, we must be the resolver process. We verify this
645 * by confirming the resolver credential and pid.
647 if ((kauth_cred_getuid(kauth_cred_get()) != 0) || (current_proc()->p_pid
!= kauth_resolver_identity
)) {
648 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
652 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
653 KAUTH_IDENTITY_LOCK();
654 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
655 KAUTH_IDENTITY_UNLOCK();
658 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
659 KAUTH_GROUPS_UNLOCK();
661 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof (sz_arg
))) != 0) {
666 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
667 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof (sz_arg
))) != 0) {
671 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
672 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
676 KAUTH_IDENTITY_LOCK();
677 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
678 kauth_identity_trimcache(kauth_identity_cachemax
);
679 KAUTH_IDENTITY_UNLOCK();
682 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
683 kauth_groups_trimcache(kauth_groups_cachemax
);
684 KAUTH_GROUPS_UNLOCK();
687 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
688 KAUTH_IDENTITY_LOCK();
689 kauth_identity_trimcache(0);
690 KAUTH_IDENTITY_UNLOCK();
693 kauth_groups_trimcache(0);
694 KAUTH_GROUPS_UNLOCK();
695 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
697 * Terminate outstanding requests; without an authoritative
698 * resolver, we are now back on our own authority.
700 struct kauth_resolver_work
*killp
;
702 KAUTH_RESOLVER_LOCK();
705 * Clear the identity, but also mark it as unregistered so
706 * there is no explicit future expectation of us getting a
707 * new resolver any time soon.
709 kauth_resolver_identity
= 0;
710 kauth_resolver_registered
= 0;
712 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
714 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
716 /* Cause all waiting-for-work threads to return EIO */
717 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
718 KAUTH_RESOLVER_UNLOCK();
722 * Got a result returning?
724 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
725 if ((error
= kauth_resolver_complete(message
)) != 0)
730 * Caller wants to take more work?
732 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
733 if ((error
= kauth_resolver_getwork(message
)) != 0)
742 * kauth_resolver_getwork_continue
744 * Description: Continuation for kauth_resolver_getwork
746 * Parameters: result Error code or 0 for the sleep
747 * that got us to this function
750 * EINTR Interrupted (e.g. by signal)
751 * kauth_resolver_getwork2:EFAULT
753 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
757 kauth_resolver_getwork_continue(int result
)
764 KAUTH_RESOLVER_UNLOCK();
769 * If we lost a race with another thread/memberd restarting, then we
770 * need to go back to sleep to look for more work. If it was memberd
771 * restarting, then the msleep0() will error out here, as our thread
772 * will already be "dead".
774 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
777 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
779 * If this is a wakeup from another thread in the resolver
780 * deregistering it, error out the request-for-work thread
782 if (!kauth_resolver_identity
) {
783 RESOLVER_FAILED_MESSAGE("external resolver died");
784 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
786 KAUTH_RESOLVER_UNLOCK();
790 thread
= current_thread();
791 ut
= get_bsdthread_info(thread
);
792 message
= ut
->uu_save
.uus_kauth
.message
;
793 return(kauth_resolver_getwork2(message
));
798 * kauth_resolver_getwork2
800 * Decription: Common utility function to copy out a identity resolver work
801 * item from the kernel to user space as part of the user space
802 * identity resolver requesting work.
804 * Parameters: message message to user space
807 * EFAULT Bad user space message address
809 * Notes: This common function exists to permit the use of continuations
810 * in the identity resolution process. This frees up the stack
811 * while we are waiting for the user space resolver to complete
812 * a request. This is specifically used so that our per thread
813 * cost can be small, and we will therefore be willing to run a
814 * larger number of threads in the user space identity resolver.
817 kauth_resolver_getwork2(user_addr_t message
)
819 struct kauth_resolver_work
*workp
;
823 * Note: We depend on the caller protecting us from a NULL work item
824 * queue, since we must have the kauth resolver lock on entry to this
827 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
830 * Copy out the external lookup structure for the request, not
831 * including the el_extend field, which contains the address of the
832 * external buffer provided by the external resolver into which we
833 * copy the extension request information.
836 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
837 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
841 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
842 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
843 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
844 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
849 * Handle extended requests here; if we have a request of a type where
850 * the kernel wants a translation of extended information, then we need
851 * to copy it out into the extended buffer, assuming the buffer is
852 * valid; we only attempt to get the buffer address if we have request
853 * data to copy into it.
857 * translate a user@domain string into a uid/gid/whatever
859 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
862 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
864 size_t actual
; /* not used */
866 * Use copyoutstr() to reduce the copy size; we let
867 * this catch a NULL uaddr because we shouldn't be
868 * asking in that case anyway.
870 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
873 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
877 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
878 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
879 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
880 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
883 KAUTH_RESOLVER_UNLOCK();
889 * kauth_resolver_getwork
891 * Description: Get a work item from the enqueued requests from the kernel and
892 * give it to the user space daemon.
894 * Parameters: message message to user space
897 * EINTR Interrupted (e.g. by signal)
898 * kauth_resolver_getwork2:EFAULT
900 * Notes: This function blocks in a continuation if there are no work
901 * items available for processing at the time the user space
902 * identity resolution daemon makes a request for work. This
903 * permits a large number of threads to be used by the daemon,
904 * without using a lot of wired kernel memory when there are no
905 * actual request outstanding.
908 kauth_resolver_getwork(user_addr_t message
)
910 struct kauth_resolver_work
*workp
;
913 KAUTH_RESOLVER_LOCK();
915 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
916 thread_t thread
= current_thread();
917 struct uthread
*ut
= get_bsdthread_info(thread
);
919 ut
->uu_save
.uus_kauth
.message
= message
;
920 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
921 KAUTH_RESOLVER_UNLOCK();
923 * If this is a wakeup from another thread in the resolver
924 * deregistering it, error out the request-for-work thread
926 if (!kauth_resolver_identity
) {
927 printf("external resolver died");
928 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
932 return kauth_resolver_getwork2(message
);
937 * kauth_resolver_complete
939 * Description: Return a result from userspace.
941 * Parameters: message message from user space
944 * EIO The resolver is dead
945 * copyin:EFAULT Bad message from user space
948 kauth_resolver_complete(user_addr_t message
)
950 struct kauth_identity_extlookup extl
;
951 struct kauth_resolver_work
*workp
;
952 struct kauth_resolver_work
*killp
;
953 int error
, result
, want_extend_data
;
956 * Copy in the mesage, including the extension field, since we are
957 * copying into a local variable.
959 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
960 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
964 KAUTH_RESOLVER_LOCK();
968 switch (extl
.el_result
) {
969 case KAUTH_EXTLOOKUP_INPROG
:
973 /* XXX this should go away once memberd is updated */
975 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
981 case KAUTH_EXTLOOKUP_SUCCESS
:
984 case KAUTH_EXTLOOKUP_FATAL
:
985 /* fatal error means the resolver is dead */
986 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
987 RESOLVER_FAILED_MESSAGE("resolver %d died, waiting for a new one", kauth_resolver_identity
);
989 * Terminate outstanding requests; without an authoritative
990 * resolver, we are now back on our own authority. Tag the
991 * resolver unregistered to prevent kauth_cred_ismember_gid()
992 * enqueueing more work until a new one is registered. This
993 * mitigates the damage a crashing resolver may inflict.
995 kauth_resolver_identity
= 0;
996 kauth_resolver_registered
= 0;
998 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
1000 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
1002 /* Cause all waiting-for-work threads to return EIO */
1003 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
1004 /* and return EIO to the caller */
1005 error
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1008 case KAUTH_EXTLOOKUP_BADRQ
:
1009 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
1013 case KAUTH_EXTLOOKUP_FAILURE
:
1014 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
1015 RESOLVER_FAILED_MESSAGE("resolver reported transient failure for request %d", extl
.el_seqno
);
1016 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1020 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
1021 RESOLVER_FAILED_MESSAGE("resolver returned unexpected status %d", extl
.el_result
);
1022 result
= KAUTH_RESOLVER_FAILED_ERRCODE
;
1027 * In the case of a fatal error, we assume that the resolver will
1028 * restart quickly and re-collect all of the outstanding requests.
1029 * Thus, we don't complete the request which returned the fatal
1032 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
1033 /* scan our list for this request */
1034 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
1036 if (workp
->kr_seqno
== extl
.el_seqno
) {
1038 * Do we want extend_data?
1040 want_extend_data
= (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_WANT_PWNAM
|KAUTH_EXTLOOKUP_WANT_GRNAM
));
1043 * Get the request of the submitted queue so
1044 * that it is not cleaned up out from under
1047 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
1048 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
1049 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
1050 workp
->kr_result
= result
;
1052 /* Copy the result message to the work item. */
1053 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
1056 * Check if we have a result in the extension
1057 * field; if we do, then we need to separately
1058 * copy the data from the message el_extend
1059 * into the request buffer that's in the work
1060 * item. We have to do it here because we do
1061 * not want to wake up the waiter until the
1062 * data is in their buffer, and because the
1063 * actual request response may be destroyed
1064 * by the time the requester wakes up, and they
1065 * do not have access to the user space buffer
1068 * It is safe to drop and reacquire the lock
1069 * here because we've already removed the item
1070 * from the submission queue, but have not yet
1071 * moved it to the completion queue. Note that
1072 * near simultaneous requests may result in
1073 * duplication of requests for items in this
1074 * window. This should not be a performance
1075 * issue and is easily detectable by comparing
1076 * time to live on last response vs. time of
1077 * next request in the resolver logs.
1079 * A malicious/faulty resolver could overwrite
1080 * part of a user's address space if they return
1081 * flags that mismatch the original request's flags.
1083 if (want_extend_data
&& (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
))) {
1084 size_t actual
; /* notused */
1086 KAUTH_RESOLVER_UNLOCK();
1087 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
1088 KAUTH_DEBUG("RESOLVER - resolver got name :%*s: len = %d\n", (int)actual
,
1089 actual
? "null" : (char *)extl
.el_extend
, actual
);
1090 KAUTH_RESOLVER_LOCK();
1091 } else if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1093 KAUTH_DEBUG("RESOLVER - resolver returned mismatching extension flags (%d), request contained (%d)",
1094 extl
.el_flags
, request_flags
);
1098 * Move the completed work item to the
1099 * completion queue and wake up requester(s)
1101 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1108 * Note that it's OK for us not to find anything; if the request has
1109 * timed out the work record will be gone.
1111 KAUTH_RESOLVER_UNLOCK();
1115 #endif /* CONFIG_EXT_RESOLVER */
1122 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1123 #define KI_VALID_GID (1<<1)
1124 #define KI_VALID_GUID (1<<2)
1125 #define KI_VALID_NTSID (1<<3)
1126 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1127 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1128 #define KI_VALID_GROUPS (1<<6)
1130 #if CONFIG_EXT_RESOLVER
1132 * kauth_identity_init
1134 * Description: Initialize the kernel side of the credential identity resolver
1136 * Parameters: (void)
1140 * Notes: Initialize the credential identity resolver for use; the
1141 * credential identity resolver is the KPI used to communicate
1142 * with a user space credential identity resolver daemon.
1144 * This function is called from kauth_init() in the file
1145 * kern_authorization.c.
1148 kauth_identity_init(void)
1150 TAILQ_INIT(&kauth_identities
);
1151 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1156 * kauth_identity_alloc
1158 * Description: Allocate and fill out a kauth_identity structure for
1159 * translation between {UID|GID}/GUID/NTSID
1163 * Returns: NULL Insufficient memory to satisfy
1164 * the request or bad parameters
1165 * !NULL A pointer to the allocated
1166 * structure, filled in
1168 * Notes: It is illegal to translate between UID and GID; any given UUID
1169 * or NTSID can only refer to an NTSID or UUID (respectively),
1170 * and *either* a UID *or* a GID, but not both.
1172 static struct kauth_identity
*
1173 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
1174 ntsid_t
*ntsidp
, time_t ntsid_expiry
, int supgrpcnt
, gid_t
*supgrps
, time_t groups_expiry
,
1175 const char *name
, int nametype
)
1177 struct kauth_identity
*kip
;
1179 /* get and fill in a new identity */
1180 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1182 if (gid
!= KAUTH_GID_NONE
) {
1184 kip
->ki_valid
= KI_VALID_GID
;
1186 if (uid
!= KAUTH_UID_NONE
) {
1187 if (kip
->ki_valid
& KI_VALID_GID
)
1188 panic("can't allocate kauth identity with both uid and gid");
1190 kip
->ki_valid
= KI_VALID_UID
;
1194 * A malicious/faulty resolver could return bad values
1196 assert(supgrpcnt
>= 0);
1197 assert(supgrpcnt
<= NGROUPS
);
1198 assert(supgrps
!= NULL
);
1200 if ((supgrpcnt
< 0) || (supgrpcnt
> NGROUPS
) || (supgrps
== NULL
)) {
1203 if (kip
->ki_valid
& KI_VALID_GID
)
1204 panic("can't allocate kauth identity with both gid and supplementary groups");
1205 kip
->ki_supgrpcnt
= supgrpcnt
;
1206 memcpy(kip
->ki_supgrps
, supgrps
, sizeof(supgrps
[0]) * supgrpcnt
);
1207 kip
->ki_valid
|= KI_VALID_GROUPS
;
1209 kip
->ki_groups_expiry
= groups_expiry
;
1210 if (guidp
!= NULL
) {
1211 kip
->ki_guid
= *guidp
;
1212 kip
->ki_valid
|= KI_VALID_GUID
;
1214 kip
->ki_guid_expiry
= guid_expiry
;
1215 if (ntsidp
!= NULL
) {
1216 kip
->ki_ntsid
= *ntsidp
;
1217 kip
->ki_valid
|= KI_VALID_NTSID
;
1219 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1221 kip
->ki_name
= name
;
1222 kip
->ki_valid
|= nametype
;
1230 * kauth_identity_register_and_free
1232 * Description: Register an association between identity tokens. The passed
1233 * 'kip' is consumed by this function.
1235 * Parameters: kip Pointer to kauth_identity
1236 * structure to register
1240 * Notes: The memory pointer to by 'kip' is assumed to have been
1241 * previously allocated via kauth_identity_alloc().
1244 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1246 struct kauth_identity
*ip
;
1249 * We search the cache for the UID listed in the incoming association.
1250 * If we already have an entry, the new information is merged.
1253 KAUTH_IDENTITY_LOCK();
1254 if (kip
->ki_valid
& KI_VALID_UID
) {
1255 if (kip
->ki_valid
& KI_VALID_GID
)
1256 panic("kauth_identity: can't insert record with both UID and GID as key");
1257 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1258 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1260 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1261 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1262 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1265 panic("kauth_identity: can't insert record without UID or GID as key");
1269 /* we already have an entry, merge/overwrite */
1270 if (kip
->ki_valid
& KI_VALID_GUID
) {
1271 ip
->ki_guid
= kip
->ki_guid
;
1272 ip
->ki_valid
|= KI_VALID_GUID
;
1274 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1275 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1276 ip
->ki_ntsid
= kip
->ki_ntsid
;
1277 ip
->ki_valid
|= KI_VALID_NTSID
;
1279 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1280 /* a valid ki_name field overwrites the previous name field */
1281 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1282 /* if there's an old one, discard it */
1283 const char *oname
= NULL
;
1284 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1285 oname
= ip
->ki_name
;
1286 ip
->ki_name
= kip
->ki_name
;
1287 kip
->ki_name
= oname
;
1289 /* and discard the incoming entry */
1293 * if we don't have any information on this identity, add it;
1294 * if it pushes us over our limit, discard the oldest one.
1296 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1297 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1298 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1299 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1300 kauth_identity_count
--;
1303 KAUTH_IDENTITY_UNLOCK();
1304 /* have to drop lock before freeing expired entry (it may be in use) */
1306 /* if the ki_name field is used, clear it first */
1307 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1308 vfs_removename(ip
->ki_name
);
1309 /* free the expired entry */
1316 * kauth_identity_updatecache
1318 * Description: Given a lookup result, add any associations that we don't
1319 * currently have; replace ones which have changed.
1321 * Parameters: elp External lookup result from
1322 * user space daemon to kernel
1323 * rkip pointer to returned kauth
1325 * extend_data Extended data (can vary)
1330 * *rkip Modified (if non-NULL)
1332 * Notes: For extended information requests, this code relies on the fact
1333 * that elp->el_flags is never used as an rvalue, and is only
1334 * ever bit-tested for valid lookup information we are willing
1337 * XXX: We may have to do the same in the case that extended data was
1338 * passed out to user space to ensure that the request string
1339 * gets cached; we may also be able to use the rkip as an
1340 * input to avoid this. The jury is still out.
1342 * XXX: This codes performance could be improved for multiple valid
1343 * results by combining the loop iteration in a single loop.
1346 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1349 struct kauth_identity
*kip
;
1350 const char *speculative_name
= NULL
;
1355 * If there is extended data, and that data represents a name rather
1356 * than something else, speculatively create an entry for it in the
1357 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1358 * over the allocation later.
1360 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1361 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1362 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1365 /* user identity? */
1366 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1367 KAUTH_IDENTITY_LOCK();
1368 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1369 /* matching record */
1370 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1371 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) {
1372 assert(elp
->el_sup_grp_cnt
<= NGROUPS
);
1373 if (elp
->el_sup_grp_cnt
> NGROUPS
) {
1374 KAUTH_DEBUG("CACHE - invalid sup_grp_cnt provided (%d), truncating to %d",
1375 elp
->el_sup_grp_cnt
, NGROUPS
);
1376 elp
->el_sup_grp_cnt
= NGROUPS
;
1378 kip
->ki_supgrpcnt
= elp
->el_sup_grp_cnt
;
1379 memcpy(kip
->ki_supgrps
, elp
->el_sup_groups
, sizeof(elp
->el_sup_groups
[0]) * kip
->ki_supgrpcnt
);
1380 kip
->ki_valid
|= KI_VALID_GROUPS
;
1381 kip
->ki_groups_expiry
= (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0;
1383 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1384 kip
->ki_guid
= elp
->el_uguid
;
1385 kip
->ki_valid
|= KI_VALID_GUID
;
1387 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1388 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1389 kip
->ki_ntsid
= elp
->el_usid
;
1390 kip
->ki_valid
|= KI_VALID_NTSID
;
1392 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1393 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1394 const char *oname
= kip
->ki_name
;
1395 kip
->ki_name
= speculative_name
;
1396 speculative_name
= NULL
;
1397 kip
->ki_valid
|= KI_VALID_PWNAM
;
1400 * free oname (if any) outside
1403 speculative_name
= oname
;
1406 kauth_identity_lru(kip
);
1409 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1413 KAUTH_IDENTITY_UNLOCK();
1414 /* not found in cache, add new record */
1416 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1417 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1418 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1419 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1420 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1421 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1422 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1423 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1424 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1429 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1430 speculative_name
= NULL
;
1431 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1432 kauth_identity_register_and_free(kip
);
1437 /* group identity? (ignore, if we already processed it as a user) */
1438 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1439 KAUTH_IDENTITY_LOCK();
1440 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1441 /* matching record */
1442 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1443 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1444 kip
->ki_guid
= elp
->el_gguid
;
1445 kip
->ki_valid
|= KI_VALID_GUID
;
1447 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1448 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1449 kip
->ki_ntsid
= elp
->el_gsid
;
1450 kip
->ki_valid
|= KI_VALID_NTSID
;
1452 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1453 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1454 const char *oname
= kip
->ki_name
;
1455 kip
->ki_name
= speculative_name
;
1456 speculative_name
= NULL
;
1457 kip
->ki_valid
|= KI_VALID_GRNAM
;
1460 * free oname (if any) outside
1463 speculative_name
= oname
;
1466 kauth_identity_lru(kip
);
1469 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1473 KAUTH_IDENTITY_UNLOCK();
1474 /* not found in cache, add new record */
1476 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1477 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1478 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1479 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1480 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1481 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_grp_cnt
: 0,
1482 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_SUPGRPS
) ? elp
->el_sup_groups
: NULL
,
1483 (elp
->el_member_valid
) ? tv
.tv_sec
+ elp
->el_member_valid
: 0,
1484 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1489 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1490 speculative_name
= NULL
;
1491 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1492 kauth_identity_register_and_free(kip
);
1497 /* If we have a name reference to drop, drop it here */
1498 if (speculative_name
!= NULL
) {
1499 vfs_removename(speculative_name
);
1505 * Trim older entries from the identity cache.
1507 * Must be called with the identity cache lock held.
1510 kauth_identity_trimcache(int newsize
) {
1511 struct kauth_identity
*kip
;
1513 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1515 while (kauth_identity_count
> newsize
) {
1516 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1517 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1518 kauth_identity_count
--;
1524 * kauth_identity_lru
1526 * Description: Promote the entry to the head of the LRU, assumes the cache
1529 * Parameters: kip kauth identity to move to the
1530 * head of the LRU list, if it's
1535 * Notes: This is called even if the entry has expired; typically an
1536 * expired entry that's been looked up is about to be revalidated,
1537 * and having it closer to the head of the LRU means finding it
1538 * quickly again when the revalidation comes through.
1541 kauth_identity_lru(struct kauth_identity
*kip
)
1543 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1544 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1545 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1551 * kauth_identity_guid_expired
1553 * Description: Handle lazy expiration of GUID translations.
1555 * Parameters: kip kauth identity to check for
1558 * Returns: 1 Expired
1562 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1567 * Expiration time of 0 means this entry is persistent.
1569 if (kip
->ki_guid_expiry
== 0)
1573 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip
->ki_guid_expiry
, tv
.tv_sec
);
1575 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1580 * kauth_identity_ntsid_expired
1582 * Description: Handle lazy expiration of NTSID translations.
1584 * Parameters: kip kauth identity to check for
1587 * Returns: 1 Expired
1591 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1596 * Expiration time of 0 means this entry is persistent.
1598 if (kip
->ki_ntsid_expiry
== 0)
1602 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1604 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1608 * kauth_identity_groups_expired
1610 * Description: Handle lazy expiration of supplemental group translations.
1612 * Parameters: kip kauth identity to check for
1615 * Returns: 1 Expired
1619 kauth_identity_groups_expired(struct kauth_identity
*kip
)
1624 * Expiration time of 0 means this entry is persistent.
1626 if (kip
->ki_groups_expiry
== 0)
1630 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip
->ki_groups_expiry
, tv
.tv_sec
);
1632 return((kip
->ki_groups_expiry
<= tv
.tv_sec
) ? 1 : 0);
1636 * kauth_identity_find_uid
1638 * Description: Search for an entry by UID
1640 * Parameters: uid UID to find
1641 * kir Pointer to return area
1642 * getname Name buffer, if ki_name wanted
1648 * *klr Modified, if found
1651 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1653 struct kauth_identity
*kip
;
1655 KAUTH_IDENTITY_LOCK();
1656 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1657 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1658 kauth_identity_lru(kip
);
1659 /* Copy via structure assignment */
1661 /* If a name is wanted and one exists, copy it out */
1662 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1663 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1667 KAUTH_IDENTITY_UNLOCK();
1668 return((kip
== NULL
) ? ENOENT
: 0);
1673 * kauth_identity_find_gid
1675 * Description: Search for an entry by GID
1677 * Parameters: gid GID to find
1678 * kir Pointer to return area
1679 * getname Name buffer, if ki_name wanted
1685 * *klr Modified, if found
1688 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1690 struct kauth_identity
*kip
;
1692 KAUTH_IDENTITY_LOCK();
1693 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1694 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1695 kauth_identity_lru(kip
);
1696 /* Copy via structure assignment */
1698 /* If a name is wanted and one exists, copy it out */
1699 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1700 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1704 KAUTH_IDENTITY_UNLOCK();
1705 return((kip
== NULL
) ? ENOENT
: 0);
1710 * kauth_identity_find_guid
1712 * Description: Search for an entry by GUID
1714 * Parameters: guidp Pointer to GUID to find
1715 * kir Pointer to return area
1716 * getname Name buffer, if ki_name wanted
1722 * *klr Modified, if found
1724 * Note: The association may be expired, in which case the caller
1725 * may elect to call out to userland to revalidate.
1728 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1730 struct kauth_identity
*kip
;
1732 KAUTH_IDENTITY_LOCK();
1733 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1734 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1735 kauth_identity_lru(kip
);
1736 /* Copy via structure assignment */
1738 /* If a name is wanted and one exists, copy it out */
1739 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1740 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1744 KAUTH_IDENTITY_UNLOCK();
1745 return((kip
== NULL
) ? ENOENT
: 0);
1749 * kauth_identity_find_nam
1751 * Description: Search for an entry by name
1753 * Parameters: name Pointer to name to find
1754 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1755 * kir Pointer to return area
1761 * *klr Modified, if found
1764 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1766 struct kauth_identity
*kip
;
1768 KAUTH_IDENTITY_LOCK();
1769 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1770 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1771 kauth_identity_lru(kip
);
1772 /* Copy via structure assignment */
1777 KAUTH_IDENTITY_UNLOCK();
1778 return((kip
== NULL
) ? ENOENT
: 0);
1783 * kauth_identity_find_ntsid
1785 * Description: Search for an entry by NTSID
1787 * Parameters: ntsid Pointer to NTSID to find
1788 * kir Pointer to return area
1789 * getname Name buffer, if ki_name wanted
1795 * *klr Modified, if found
1797 * Note: The association may be expired, in which case the caller
1798 * may elect to call out to userland to revalidate.
1801 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1803 struct kauth_identity
*kip
;
1805 KAUTH_IDENTITY_LOCK();
1806 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1807 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1808 kauth_identity_lru(kip
);
1809 /* Copy via structure assignment */
1811 /* If a name is wanted and one exists, copy it out */
1812 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1813 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1817 KAUTH_IDENTITY_UNLOCK();
1818 return((kip
== NULL
) ? ENOENT
: 0);
1820 #endif /* CONFIG_EXT_RESOLVER */
1826 guid_t kauth_null_guid
;
1832 * Description: Determine the equality of two GUIDs
1834 * Parameters: guid1 Pointer to first GUID
1835 * guid2 Pointer to second GUID
1837 * Returns: 0 If GUIDs are unequal
1838 * !0 If GUIDs are equal
1841 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1843 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1848 * kauth_wellknown_guid
1850 * Description: Determine if a GUID is a well-known GUID
1852 * Parameters: guid Pointer to GUID to check
1854 * Returns: KAUTH_WKG_NOT Not a well known GUID
1855 * KAUTH_WKG_EVERYBODY "Everybody"
1856 * KAUTH_WKG_NOBODY "Nobody"
1857 * KAUTH_WKG_OWNER "Other"
1858 * KAUTH_WKG_GROUP "Group"
1861 kauth_wellknown_guid(guid_t
*guid
)
1863 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1866 * All WKGs begin with the same 12 bytes.
1868 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1870 * The final 4 bytes are our code (in network byte order).
1872 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1875 return(KAUTH_WKG_EVERYBODY
);
1877 return(KAUTH_WKG_NOBODY
);
1879 return(KAUTH_WKG_OWNER
);
1881 return(KAUTH_WKG_GROUP
);
1884 return(KAUTH_WKG_NOT
);
1891 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1893 * Parameters: sid1 Pointer to first NTSID
1894 * sid2 Pointer to second NTSID
1896 * Returns: 0 If GUIDs are unequal
1897 * !0 If GUIDs are equal
1900 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1902 /* check sizes for equality, also sanity-check size while we're at it */
1903 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1904 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1905 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1914 * We support four tokens representing identity:
1915 * - Credential reference
1918 * - NT security identifier
1920 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1927 * kauth_cred_change_egid
1929 * Description: Set EGID by changing the first element of cr_groups for the
1930 * passed credential; if the new EGID exists in the list of
1931 * groups already, then rotate the old EGID into its position,
1932 * otherwise replace it
1934 * Parameters: cred Pointer to the credential to modify
1935 * new_egid The new EGID to set
1937 * Returns: 0 The egid did not displace a member of
1938 * the supplementary group list
1939 * 1 The egid being set displaced a member
1940 * of the supplementary groups list
1942 * Note: Utility function; internal use only because of locking.
1944 * This function operates on the credential passed; the caller
1945 * must operate either on a newly allocated credential (one for
1946 * which there is no hash cache reference and no externally
1947 * visible pointer reference), or a template credential.
1950 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1956 #endif /* radar_4600026 */
1957 gid_t old_egid
= kauth_cred_getgid(cred
);
1958 posix_cred_t pcred
= posix_cred_get(cred
);
1960 /* Ignoring the first entry, scan for a match for the new egid */
1961 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1963 * If we find a match, swap them so we don't lose overall
1966 if (pcred
->cr_groups
[i
] == new_egid
) {
1967 pcred
->cr_groups
[i
] = old_egid
;
1968 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1975 #error Fix radar 4600026 first!!!
1978 This is correct for memberd behaviour, but incorrect for POSIX; to address
1979 this, we would need to automatically opt-out any SUID/SGID binary, and force
1980 it to use initgroups to opt back in. We take the approach of considering it
1981 opt'ed out in any group of 16 displacement instead, since it's a much more
1982 conservative approach (i.e. less likely to cause things to break).
1986 * If we displaced a member of the supplementary groups list of the
1987 * credential, and we have not opted out of memberd, then if memberd
1988 * says that the credential is a member of the group, then it has not
1989 * actually been displaced.
1991 * NB: This is typically a cold code path.
1993 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1994 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1997 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1999 #endif /* radar_4600026 */
2001 /* set the new EGID into the old spot */
2002 pcred
->cr_groups
[0] = new_egid
;
2011 * Description: Fetch UID from credential
2013 * Parameters: cred Credential to examine
2015 * Returns: (uid_t) UID associated with credential
2018 kauth_cred_getuid(kauth_cred_t cred
)
2020 NULLCRED_CHECK(cred
);
2021 return(posix_cred_get(cred
)->cr_uid
);
2026 * kauth_cred_getruid
2028 * Description: Fetch RUID from credential
2030 * Parameters: cred Credential to examine
2032 * Returns: (uid_t) RUID associated with credential
2035 kauth_cred_getruid(kauth_cred_t cred
)
2037 NULLCRED_CHECK(cred
);
2038 return(posix_cred_get(cred
)->cr_ruid
);
2043 * kauth_cred_getsvuid
2045 * Description: Fetch SVUID from credential
2047 * Parameters: cred Credential to examine
2049 * Returns: (uid_t) SVUID associated with credential
2052 kauth_cred_getsvuid(kauth_cred_t cred
)
2054 NULLCRED_CHECK(cred
);
2055 return(posix_cred_get(cred
)->cr_svuid
);
2062 * Description: Fetch GID from credential
2064 * Parameters: cred Credential to examine
2066 * Returns: (gid_t) GID associated with credential
2069 kauth_cred_getgid(kauth_cred_t cred
)
2071 NULLCRED_CHECK(cred
);
2072 return(posix_cred_get(cred
)->cr_gid
);
2077 * kauth_cred_getrgid
2079 * Description: Fetch RGID from credential
2081 * Parameters: cred Credential to examine
2083 * Returns: (gid_t) RGID associated with credential
2086 kauth_cred_getrgid(kauth_cred_t cred
)
2088 NULLCRED_CHECK(cred
);
2089 return(posix_cred_get(cred
)->cr_rgid
);
2094 * kauth_cred_getsvgid
2096 * Description: Fetch SVGID from credential
2098 * Parameters: cred Credential to examine
2100 * Returns: (gid_t) SVGID associated with credential
2103 kauth_cred_getsvgid(kauth_cred_t cred
)
2105 NULLCRED_CHECK(cred
);
2106 return(posix_cred_get(cred
)->cr_svgid
);
2110 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
2112 #if CONFIG_EXT_RESOLVER == 0
2114 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
2117 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2119 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2120 static const uuid_t _user_compat_prefix
= {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2121 static const uuid_t _group_compat_prefix
= {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2122 #define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2127 case KI_VALID_UID
: {
2128 id_t uid
= htonl(*(id_t
*)src
);
2130 if (to
== KI_VALID_GUID
) {
2132 memcpy(uu
, _user_compat_prefix
, sizeof(_user_compat_prefix
));
2133 memcpy(&uu
[COMPAT_PREFIX_LEN
], &uid
, sizeof(uid
));
2138 case KI_VALID_GID
: {
2139 id_t gid
= htonl(*(id_t
*)src
);
2141 if (to
== KI_VALID_GUID
) {
2143 memcpy(uu
, _group_compat_prefix
, sizeof(_group_compat_prefix
));
2144 memcpy(&uu
[COMPAT_PREFIX_LEN
], &gid
, sizeof(gid
));
2149 case KI_VALID_GUID
: {
2150 const uint8_t *uu
= src
;
2152 if (to
== KI_VALID_UID
) {
2153 if (memcmp(uu
, _user_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2155 memcpy(&uid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(uid
));
2156 *(id_t
*)dst
= ntohl(uid
);
2159 } else if (to
== KI_VALID_GID
) {
2160 if (memcmp(uu
, _group_compat_prefix
, COMPAT_PREFIX_LEN
) == 0) {
2162 memcpy(&gid
, &uu
[COMPAT_PREFIX_LEN
], sizeof(gid
));
2163 *(id_t
*)dst
= ntohl(gid
);
2170 /* NOT IMPLEMENTED */
2177 #if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2179 * Structure to hold supplemental groups. Used for impedance matching with
2180 * kauth_cred_cache_lookup below.
2188 * kauth_cred_uid2groups
2190 * Description: Fetch supplemental GROUPS from UID
2192 * Parameters: uid UID to examine
2193 * groups pointer to an array of gid_ts
2194 * gcount pointer to the number of groups wanted/returned
2196 * Returns: 0 Success
2197 * kauth_cred_cache_lookup:EINVAL
2200 * *groups Modified, if successful
2201 * *gcount Modified, if successful
2205 kauth_cred_uid2groups(uid_t
*uid
, gid_t
*groups
, int *gcount
)
2209 struct supgroups supgroups
;
2210 supgroups
.count
= gcount
;
2211 supgroups
.groups
= groups
;
2213 rv
= kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GROUPS
, uid
, &supgroups
);
2220 * kauth_cred_guid2pwnam
2222 * Description: Fetch PWNAM from GUID
2224 * Parameters: guidp Pointer to GUID to examine
2225 * pwnam Pointer to user@domain buffer
2227 * Returns: 0 Success
2228 * kauth_cred_cache_lookup:EINVAL
2231 * *pwnam Modified, if successful
2233 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2236 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
2238 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
2243 * kauth_cred_guid2grnam
2245 * Description: Fetch GRNAM from GUID
2247 * Parameters: guidp Pointer to GUID to examine
2248 * grnam Pointer to group@domain buffer
2250 * Returns: 0 Success
2251 * kauth_cred_cache_lookup:EINVAL
2254 * *grnam Modified, if successful
2256 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2259 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2261 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
2266 * kauth_cred_pwnam2guid
2268 * Description: Fetch PWNAM from GUID
2270 * Parameters: pwnam String containing user@domain
2271 * guidp Pointer to buffer for GUID
2273 * Returns: 0 Success
2274 * kauth_cred_cache_lookup:EINVAL
2277 * *guidp Modified, if successful
2279 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2280 * bytes in size, including the NUL termination of the string.
2283 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2285 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
2290 * kauth_cred_grnam2guid
2292 * Description: Fetch GRNAM from GUID
2294 * Parameters: grnam String containing group@domain
2295 * guidp Pointer to buffer for GUID
2297 * Returns: 0 Success
2298 * kauth_cred_cache_lookup:EINVAL
2301 * *guidp Modified, if successful
2303 * Notes: grnam should not point to a request larger than MAXPATHLEN
2304 * bytes in size, including the NUL termination of the string.
2307 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2309 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
2314 * kauth_cred_guid2uid
2316 * Description: Fetch UID from GUID
2318 * Parameters: guidp Pointer to GUID to examine
2319 * uidp Pointer to buffer for UID
2321 * Returns: 0 Success
2322 * kauth_cred_cache_lookup:EINVAL
2325 * *uidp Modified, if successful
2328 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2330 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
2335 * kauth_cred_guid2gid
2337 * Description: Fetch GID from GUID
2339 * Parameters: guidp Pointer to GUID to examine
2340 * gidp Pointer to buffer for GID
2342 * Returns: 0 Success
2343 * kauth_cred_cache_lookup:EINVAL
2346 * *gidp Modified, if successful
2349 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2351 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2355 * kauth_cred_nfs4domain2dsnode
2357 * Description: Fetch dsnode from nfs4domain
2359 * Parameters: nfs4domain Pointer to a string nfs4 domain
2360 * dsnode Pointer to buffer for dsnode
2362 * Returns: 0 Success
2363 * ENOENT For now just a stub that always fails
2366 * *dsnode Modified, if successuful
2369 kauth_cred_nfs4domain2dsnode(__unused
char *nfs4domain
, __unused
char *dsnode
)
2375 * kauth_cred_dsnode2nfs4domain
2377 * Description: Fetch nfs4domain from dsnode
2379 * Parameters: nfs4domain Pointer to string dsnode
2380 * dsnode Pointer to buffer for nfs4domain
2382 * Returns: 0 Success
2383 * ENOENT For now just a stub that always fails
2386 * *nfs4domain Modified, if successuful
2389 kauth_cred_dsnode2nfs4domain(__unused
char *dsnode
, __unused
char *nfs4domain
)
2395 * kauth_cred_ntsid2uid
2397 * Description: Fetch UID from NTSID
2399 * Parameters: sidp Pointer to NTSID to examine
2400 * uidp Pointer to buffer for UID
2402 * Returns: 0 Success
2403 * kauth_cred_cache_lookup:EINVAL
2406 * *uidp Modified, if successful
2409 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2411 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2416 * kauth_cred_ntsid2gid
2418 * Description: Fetch GID from NTSID
2420 * Parameters: sidp Pointer to NTSID to examine
2421 * gidp Pointer to buffer for GID
2423 * Returns: 0 Success
2424 * kauth_cred_cache_lookup:EINVAL
2427 * *gidp Modified, if successful
2430 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2432 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2437 * kauth_cred_ntsid2guid
2439 * Description: Fetch GUID from NTSID
2441 * Parameters: sidp Pointer to NTSID to examine
2442 * guidp Pointer to buffer for GUID
2444 * Returns: 0 Success
2445 * kauth_cred_cache_lookup:EINVAL
2448 * *guidp Modified, if successful
2451 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2453 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2458 * kauth_cred_uid2guid
2460 * Description: Fetch GUID from UID
2462 * Parameters: uid UID to examine
2463 * guidp Pointer to buffer for GUID
2465 * Returns: 0 Success
2466 * kauth_cred_cache_lookup:EINVAL
2469 * *guidp Modified, if successful
2472 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2474 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2479 * kauth_cred_getguid
2481 * Description: Fetch GUID from credential
2483 * Parameters: cred Credential to examine
2484 * guidp Pointer to buffer for GUID
2486 * Returns: 0 Success
2487 * kauth_cred_cache_lookup:EINVAL
2490 * *guidp Modified, if successful
2493 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2495 NULLCRED_CHECK(cred
);
2496 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2501 * kauth_cred_getguid
2503 * Description: Fetch GUID from GID
2505 * Parameters: gid GID to examine
2506 * guidp Pointer to buffer for GUID
2508 * Returns: 0 Success
2509 * kauth_cred_cache_lookup:EINVAL
2512 * *guidp Modified, if successful
2515 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2517 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2522 * kauth_cred_uid2ntsid
2524 * Description: Fetch NTSID from UID
2526 * Parameters: uid UID to examine
2527 * sidp Pointer to buffer for NTSID
2529 * Returns: 0 Success
2530 * kauth_cred_cache_lookup:EINVAL
2533 * *sidp Modified, if successful
2536 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2538 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2543 * kauth_cred_getntsid
2545 * Description: Fetch NTSID from credential
2547 * Parameters: cred Credential to examine
2548 * sidp Pointer to buffer for NTSID
2550 * Returns: 0 Success
2551 * kauth_cred_cache_lookup:EINVAL
2554 * *sidp Modified, if successful
2557 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2559 NULLCRED_CHECK(cred
);
2560 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2565 * kauth_cred_gid2ntsid
2567 * Description: Fetch NTSID from GID
2569 * Parameters: gid GID to examine
2570 * sidp Pointer to buffer for NTSID
2572 * Returns: 0 Success
2573 * kauth_cred_cache_lookup:EINVAL
2576 * *sidp Modified, if successful
2579 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2581 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2586 * kauth_cred_guid2ntsid
2588 * Description: Fetch NTSID from GUID
2590 * Parameters: guidp Pointer to GUID to examine
2591 * sidp Pointer to buffer for NTSID
2593 * Returns: 0 Success
2594 * kauth_cred_cache_lookup:EINVAL
2597 * *sidp Modified, if successful
2600 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2602 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2607 * kauth_cred_cache_lookup
2609 * Description: Lookup a translation in the cache; if one is not found, and
2610 * the attempt was not fatal, submit the request to the resolver
2611 * instead, and wait for it to complete or be aborted.
2613 * Parameters: from Identity information we have
2614 * to Identity information we want
2615 * src Pointer to buffer containing
2616 * the source identity
2617 * dst Pointer to buffer to receive
2618 * the target identity
2620 * Returns: 0 Success
2621 * EINVAL Unknown source identity type
2623 #if CONFIG_EXT_RESOLVER
2625 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2627 struct kauth_identity ki
;
2628 struct kauth_identity_extlookup el
;
2630 uint64_t extend_data
= 0ULL;
2631 int (* expired
)(struct kauth_identity
*kip
);
2632 char *namebuf
= NULL
;
2634 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2637 * Look for an existing cache entry for this association.
2638 * If the entry has not expired, return the cached information.
2639 * We do not cache user@domain translations here; they use too
2640 * much memory to hold onto forever, and can not be updated
2643 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2652 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2655 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2658 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2660 case KI_VALID_NTSID
:
2661 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2663 case KI_VALID_PWNAM
:
2664 case KI_VALID_GRNAM
:
2665 /* Names are unique in their 'from' space */
2666 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2671 /* If we didn't get what we're asking for. Call the resolver */
2672 if (!error
&& !(to
& ki
.ki_valid
))
2674 /* lookup failure or error */
2676 /* any other error is fatal */
2677 if (error
!= ENOENT
) {
2678 /* XXX bogus check - this is not possible */
2679 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2683 /* found a valid cached entry, check expiry */
2686 expired
= kauth_identity_guid_expired
;
2688 case KI_VALID_NTSID
:
2689 expired
= kauth_identity_ntsid_expired
;
2691 case KI_VALID_GROUPS
:
2692 expired
= kauth_identity_groups_expired
;
2697 expired
= kauth_identity_guid_expired
;
2699 case KI_VALID_NTSID
:
2700 expired
= kauth_identity_ntsid_expired
;
2708 * If no expiry function, or not expired, we have found
2712 if (!expired(&ki
)) {
2713 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2714 expired
= NULL
; /* must clear it is used as a flag */
2717 * We leave ki_valid set here; it contains a
2718 * translation but the TTL has expired. If we can't
2719 * get a result from the resolver, we will use it as
2720 * a better-than nothing alternative.
2723 KAUTH_DEBUG("CACHE - expired entry found");
2726 KAUTH_DEBUG("CACHE - no expiry function");
2730 /* do we have a translation? */
2731 if (ki
.ki_valid
& to
) {
2732 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2733 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2737 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2738 * If we went looking for a translation from GUID or NTSID and
2739 * found a translation that wasn't for our desired type, then
2740 * don't bother calling the resolver. We know that this
2741 * GUID/NTSID can't translate to our desired type.
2745 case KI_VALID_NTSID
:
2748 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2749 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2754 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2755 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2767 * We failed to find a cache entry; call the resolver.
2769 * Note: We ask for as much non-extended data as we can get,
2770 * and only provide (or ask for) extended information if
2771 * we have a 'from' (or 'to') which requires it. This
2772 * way we don't pay for the extra transfer overhead for
2773 * data we don't need.
2775 bzero(&el
, sizeof(el
));
2776 el
.el_info_pid
= current_proc()->p_pid
;
2779 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2780 el
.el_uid
= *(uid_t
*)src
;
2783 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2784 el
.el_gid
= *(gid_t
*)src
;
2787 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2788 el
.el_uguid
= *(guid_t
*)src
;
2789 el
.el_gguid
= *(guid_t
*)src
;
2791 case KI_VALID_NTSID
:
2792 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2793 el
.el_usid
= *(ntsid_t
*)src
;
2794 el
.el_gsid
= *(ntsid_t
*)src
;
2796 case KI_VALID_PWNAM
:
2797 /* extra overhead */
2798 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2799 extend_data
= CAST_USER_ADDR_T(src
);
2801 case KI_VALID_GRNAM
:
2802 /* extra overhead */
2803 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2804 extend_data
= CAST_USER_ADDR_T(src
);
2810 * Here we ask for everything all at once, to avoid having to work
2811 * out what we really want now, or might want soon.
2813 * Asking for SID translations when we don't know we need them right
2814 * now is going to cause excess work to be done if we're connected
2815 * to a network that thinks it can translate them. This list needs
2816 * to get smaller/smarter.
2818 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2819 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2820 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2821 if (to
== KI_VALID_PWNAM
) {
2822 /* extra overhead */
2823 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2824 extend_data
= CAST_USER_ADDR_T(dst
);
2826 if (to
== KI_VALID_GRNAM
) {
2827 /* extra overhead */
2828 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2829 extend_data
= CAST_USER_ADDR_T(dst
);
2831 if (to
== KI_VALID_GROUPS
) {
2832 /* Expensive and only useful for an NFS client not using kerberos */
2833 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_SUPGRPS
;
2834 if (ki
.ki_valid
& KI_VALID_GROUPS
) {
2836 * Copy the current supplemental groups for the resolver.
2837 * The resolver should check these groups first and if
2838 * the user (uid) is still a member it should endeavor to
2839 * keep them in the list. Otherwise NFS clients could get
2840 * changing access to server file system objects on each
2843 if (ki
.ki_supgrpcnt
> NGROUPS
) {
2844 panic("kauth data structure corrupted. kauth identity 0x%p with %d groups, greater than max of %d",
2845 &ki
, ki
.ki_supgrpcnt
, NGROUPS
);
2848 el
.el_sup_grp_cnt
= ki
.ki_supgrpcnt
;
2850 memcpy(el
.el_sup_groups
, ki
.ki_supgrps
, sizeof (el
.el_sup_groups
[0]) * ki
.ki_supgrpcnt
);
2851 /* Let the resolver know these were the previous valid groups */
2852 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_SUPGRPS
;
2853 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2855 KAUTH_DEBUG("GROUPS: no valid groups to send");
2859 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2861 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2863 error
= kauth_resolver_submit(&el
, extend_data
);
2865 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2867 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2869 /* was the external lookup successful? */
2872 * Save the results from the lookup - we may have other
2873 * information, even if we didn't get a guid or the
2876 * If we came from a name, we know the extend_data is valid.
2878 if (from
== KI_VALID_PWNAM
)
2879 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2880 else if (from
== KI_VALID_GRNAM
)
2881 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2883 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2886 * Check to see if we have a valid cache entry
2887 * originating from the result.
2889 if (!(ki
.ki_valid
& to
)) {
2897 * Copy from the appropriate struct kauth_identity cache entry
2898 * structure into the destination buffer area.
2902 *(uid_t
*)dst
= ki
.ki_uid
;
2905 *(gid_t
*)dst
= ki
.ki_gid
;
2908 *(guid_t
*)dst
= ki
.ki_guid
;
2910 case KI_VALID_NTSID
:
2911 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2913 case KI_VALID_GROUPS
: {
2914 struct supgroups
*gp
= (struct supgroups
*)dst
;
2915 u_int32_t limit
= ki
.ki_supgrpcnt
;
2918 limit
= MIN(ki
.ki_supgrpcnt
, *gp
->count
);
2922 memcpy(gp
->groups
, ki
.ki_supgrps
, sizeof(gid_t
) * limit
);
2925 case KI_VALID_PWNAM
:
2926 case KI_VALID_GRNAM
:
2927 /* handled in kauth_resolver_complete() */
2932 KAUTH_DEBUG("CACHE - returned successfully");
2938 * Group membership cache.
2940 * XXX the linked-list implementation here needs to be optimized.
2946 * Description: Initialize the groups cache
2948 * Parameters: (void)
2952 * Notes: Initialize the groups cache for use; the group cache is used
2953 * to avoid unnecessary calls out to user space.
2955 * This function is called from kauth_init() in the file
2956 * kern_authorization.c.
2959 kauth_groups_init(void)
2961 TAILQ_INIT(&kauth_groups
);
2962 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2967 * kauth_groups_expired
2969 * Description: Handle lazy expiration of group membership cache entries
2971 * Parameters: gm group membership entry to
2972 * check for expiration
2974 * Returns: 1 Expired
2978 kauth_groups_expired(struct kauth_group_membership
*gm
)
2983 * Expiration time of 0 means this entry is persistent.
2985 if (gm
->gm_expiry
== 0)
2990 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2997 * Description: Promote the entry to the head of the LRU, assumes the cache
3000 * Parameters: kip group membership entry to move
3001 * to the head of the LRU list,
3002 * if it's not already there
3006 * Notes: This is called even if the entry has expired; typically an
3007 * expired entry that's been looked up is about to be revalidated,
3008 * and having it closer to the head of the LRU means finding it
3009 * quickly again when the revalidation comes through.
3012 kauth_groups_lru(struct kauth_group_membership
*gm
)
3014 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
3015 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3016 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3022 * kauth_groups_updatecache
3024 * Description: Given a lookup result, add any group cache associations that
3025 * we don't currently have.
3027 * Parameters: elp External lookup result from
3028 * user space daemon to kernel
3029 * rkip pointer to returned kauth
3035 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
3037 struct kauth_group_membership
*gm
;
3040 /* need a valid response if we are to cache anything */
3042 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
3043 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
3049 * Search for an existing record for this association before inserting
3050 * a new one; if we find one, update it instead of creating a new one
3052 KAUTH_GROUPS_LOCK();
3053 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3054 if ((el
->el_uid
== gm
->gm_uid
) &&
3055 (el
->el_gid
== gm
->gm_gid
)) {
3056 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3057 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3059 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3061 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3062 kauth_groups_lru(gm
);
3066 KAUTH_GROUPS_UNLOCK();
3068 /* if we found an entry to update, stop here */
3072 /* allocate a new record */
3073 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
3075 gm
->gm_uid
= el
->el_uid
;
3076 gm
->gm_gid
= el
->el_gid
;
3077 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
3078 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
3080 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
3082 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
3086 * Insert the new entry. Note that it's possible to race ourselves
3087 * here and end up with duplicate entries in the list. Wasteful, but
3088 * harmless since the first into the list will never be looked up,
3089 * and thus will eventually just fall off the end.
3091 KAUTH_GROUPS_LOCK();
3092 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
3093 if (++kauth_groups_count
> kauth_groups_cachemax
) {
3094 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3095 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3096 kauth_groups_count
--;
3100 KAUTH_GROUPS_UNLOCK();
3102 /* free expired cache entry */
3108 * Trim older entries from the group membership cache.
3110 * Must be called with the group cache lock held.
3113 kauth_groups_trimcache(int new_size
) {
3114 struct kauth_group_membership
*gm
;
3116 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
3118 while (kauth_groups_count
> new_size
) {
3119 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
3120 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
3121 kauth_groups_count
--;
3125 #endif /* CONFIG_EXT_RESOLVER */
3128 * Group membership KPI
3132 * kauth_cred_ismember_gid
3134 * Description: Given a credential and a GID, determine if the GID is a member
3135 * of one of the supplementary groups associated with the given
3138 * Parameters: cred Credential to check in
3139 * gid GID to check for membership
3140 * resultp Pointer to int to contain the
3141 * result of the call
3143 * Returns: 0 Success
3144 * ENOENT Could not perform lookup
3145 * kauth_resolver_submit:EWOULDBLOCK
3146 * kauth_resolver_submit:EINTR
3147 * kauth_resolver_submit:ENOMEM
3148 * kauth_resolver_submit:ENOENT User space daemon did not vend
3150 * kauth_resolver_submit:??? Unlikely error from user space
3153 * *resultp (modified) 1 Is member
3156 * Notes: This function guarantees not to modify resultp when returning
3159 * This function effectively checks the EGID as well, since the
3160 * EGID is cr_groups[0] as an implementation detail.
3163 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
3165 posix_cred_t pcred
= posix_cred_get(cred
);
3169 * Check the per-credential list of override groups.
3171 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3172 * the cache should be used for that case.
3174 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
3175 if (gid
== pcred
->cr_groups
[i
]) {
3182 * If we don't have a UID for group membership checks, the in-cred list
3183 * was authoritative and we can stop here.
3185 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
3190 #if CONFIG_EXT_RESOLVER
3191 struct kauth_group_membership
*gm
;
3192 struct kauth_identity_extlookup el
;
3196 * If the resolver hasn't checked in yet, we are early in the boot
3197 * phase and the local group list is complete and authoritative.
3199 if (!kauth_resolver_registered
) {
3205 /* XXX check supplementary groups */
3206 /* XXX check whiteout groups */
3207 /* XXX nesting of supplementary/whiteout groups? */
3210 * Check the group cache.
3212 KAUTH_GROUPS_LOCK();
3213 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
3214 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
3215 kauth_groups_lru(gm
);
3220 /* did we find a membership entry? */
3222 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
3223 KAUTH_GROUPS_UNLOCK();
3225 /* if we did, we can return now */
3227 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
3231 /* nothing in the cache, need to go to userland */
3232 bzero(&el
, sizeof(el
));
3233 el
.el_info_pid
= current_proc()->p_pid
;
3234 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
3235 el
.el_uid
= pcred
->cr_gmuid
;
3237 el
.el_member_valid
= 0; /* XXX set by resolver? */
3239 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
3241 error
= kauth_resolver_submit(&el
, 0ULL);
3243 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
3247 /* save the results from the lookup */
3248 kauth_groups_updatecache(&el
);
3250 /* if we successfully ascertained membership, report */
3251 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
3252 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
3264 * kauth_cred_ismember_guid
3266 * Description: Determine whether the supplied credential is a member of the
3267 * group nominated by GUID.
3269 * Parameters: cred Credential to check in
3270 * guidp Pointer to GUID whose group
3271 * we are testing for membership
3272 * resultp Pointer to int to contain the
3273 * result of the call
3275 * Returns: 0 Success
3276 * kauth_cred_guid2gid:EINVAL
3277 * kauth_cred_ismember_gid:ENOENT
3278 * kauth_resolver_submit:ENOENT User space daemon did not vend
3280 * kauth_cred_ismember_gid:EWOULDBLOCK
3281 * kauth_cred_ismember_gid:EINTR
3282 * kauth_cred_ismember_gid:ENOMEM
3283 * kauth_cred_ismember_gid:??? Unlikely error from user space
3286 * *resultp (modified) 1 Is member
3290 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
3294 switch (kauth_wellknown_guid(guidp
)) {
3295 case KAUTH_WKG_NOBODY
:
3298 case KAUTH_WKG_EVERYBODY
:
3304 #if CONFIG_EXT_RESOLVER
3305 struct kauth_identity ki
;
3308 * Grovel the identity cache looking for this GUID.
3309 * If we find it, and it is for a user record, return
3310 * false because it's not a group.
3312 * This is necessary because we don't have -ve caching
3313 * of group memberships, and we really want to avoid
3314 * calling out to the resolver if at all possible.
3316 * Because we're called by the ACL evaluator, and the
3317 * ACL evaluator is likely to encounter ACEs for users,
3318 * this is expected to be a common case.
3321 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
3322 !kauth_identity_guid_expired(&ki
)) {
3323 if (ki
.ki_valid
& KI_VALID_GID
) {
3324 /* It's a group after all... */
3328 if (ki
.ki_valid
& KI_VALID_UID
) {
3333 #endif /* CONFIG_EXT_RESOLVER */
3335 * Attempt to translate the GUID to a GID. Even if
3336 * this fails, we will have primed the cache if it is
3337 * a user record and we'll see it above the next time
3340 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
3342 * If we have no guid -> gid translation, it's not a group and
3343 * thus the cred can't be a member.
3345 if (error
== ENOENT
) {
3350 #if CONFIG_EXT_RESOLVER
3352 #endif /* CONFIG_EXT_RESOLVER */
3353 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3362 * kauth_cred_gid_subset
3364 * Description: Given two credentials, determine if all GIDs associated with
3365 * the first are also associated with the second
3367 * Parameters: cred1 Credential to check for
3368 * cred2 Credential to check in
3369 * resultp Pointer to int to contain the
3370 * result of the call
3372 * Returns: 0 Success
3373 * non-zero See kauth_cred_ismember_gid for
3377 * *resultp (modified) 1 Is subset
3380 * Notes: This function guarantees not to modify resultp when returning
3384 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3386 int i
, err
, res
= 1;
3388 posix_cred_t pcred1
= posix_cred_get(cred1
);
3389 posix_cred_t pcred2
= posix_cred_get(cred2
);
3391 /* First, check the local list of groups */
3392 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3393 gid
= pcred1
->cr_groups
[i
];
3394 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3398 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3404 /* Check real gid */
3405 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3409 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3410 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3415 /* Finally, check saved gid */
3416 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
3420 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3421 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3432 * kauth_cred_issuser
3434 * Description: Fast replacement for issuser()
3436 * Parameters: cred Credential to check for super
3439 * Returns: 0 Not super user
3442 * Notes: This function uses a magic number which is not a manifest
3443 * constant; this is bad practice.
3446 kauth_cred_issuser(kauth_cred_t cred
)
3448 return(kauth_cred_getuid(cred
) == 0);
3456 /* lock protecting credential hash table */
3457 static lck_mtx_t
*kauth_cred_hash_mtx
;
3458 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3459 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
3460 #if KAUTH_CRED_HASH_DEBUG
3461 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3462 #else /* !KAUTH_CRED_HASH_DEBUG */
3463 #define KAUTH_CRED_HASH_LOCK_ASSERT()
3464 #endif /* !KAUTH_CRED_HASH_DEBUG */
3470 * Description: Initialize the credential hash cache
3472 * Parameters: (void)
3476 * Notes: Intialize the credential hash cache for use; the credential
3477 * hash cache is used convert duplicate credentials into a
3478 * single reference counted credential in order to save wired
3479 * kernel memory. In practice, this generally means a desktop
3480 * system runs with a few tens of credentials, instead of one
3481 * per process, one per thread, one per vnode cache entry, and
3482 * so on. This generally results in savings of 200K or more
3483 * (potentially much more on server systems).
3485 * The hash cache internally has a reference on the credential
3486 * for itself as a means of avoiding a reclaim race for a
3487 * credential in the process of having it's last non-hash
3488 * reference released. This would otherwise result in the
3489 * possibility of a freed credential that was still in uses due
3490 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3492 * On final release, the hash reference is droped, and the
3493 * credential is freed back to the system.
3495 * This function is called from kauth_init() in the file
3496 * kern_authorization.c.
3499 kauth_cred_init(void)
3503 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3505 /*allocate credential hash table */
3506 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3507 (sizeof(struct kauth_cred_entry_head
) * KAUTH_CRED_TABLE_SIZE
),
3508 M_KAUTH
, M_WAITOK
| M_ZERO
);
3509 if (kauth_cred_table_anchor
== NULL
)
3510 panic("startup: kauth_cred_init");
3511 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
3512 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3520 * Description: Get the current thread's effective UID.
3522 * Parameters: (void)
3524 * Returns: (uid_t) The effective UID of the
3530 return(kauth_cred_getuid(kauth_cred_get()));
3537 * Description: Get the current thread's real UID.
3539 * Parameters: (void)
3541 * Returns: (uid_t) The real UID of the current
3547 return(kauth_cred_getruid(kauth_cred_get()));
3554 * Description: Get the current thread's effective GID.
3556 * Parameters: (void)
3558 * Returns: (gid_t) The effective GID of the
3564 return(kauth_cred_getgid(kauth_cred_get()));
3571 * Description: Get the current thread's real GID.
3573 * Parameters: (void)
3575 * Returns: (gid_t) The real GID of the current
3581 return(kauth_cred_getrgid(kauth_cred_get()));
3588 * Description: Returns a pointer to the current thread's credential
3590 * Parameters: (void)
3592 * Returns: (kauth_cred_t) Pointer to the current thread's
3595 * Notes: This function does not take a reference; because of this, the
3596 * caller MUST NOT do anything that would let the thread's
3597 * credential change while using the returned value, without
3598 * first explicitly taking their own reference.
3600 * If a caller intends to take a reference on the resulting
3601 * credential pointer from calling this function, it is strongly
3602 * recommended that the caller use kauth_cred_get_with_ref()
3603 * instead, to protect against any future changes to the cred
3604 * locking protocols; such changes could otherwise potentially
3605 * introduce race windows in the callers code.
3608 kauth_cred_get(void)
3611 struct uthread
*uthread
;
3613 uthread
= get_bsdthread_info(current_thread());
3615 if (uthread
== NULL
)
3616 panic("thread wants credential but has no BSD thread info");
3618 * We can lazy-bind credentials to threads, as long as their processes
3621 * XXX If we later inline this function, the code in this block
3622 * XXX should probably be called out in a function.
3624 if (uthread
->uu_ucred
== NOCRED
) {
3625 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3626 panic("thread wants credential but has no BSD process");
3627 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3629 return(uthread
->uu_ucred
);
3633 mach_kauth_cred_uthread_update(void)
3638 uthread
= get_bsdthread_info(current_thread());
3639 proc
= current_proc();
3641 kauth_cred_uthread_update(uthread
, proc
);
3645 * kauth_cred_uthread_update
3647 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3648 * late-bind the uthread cred to the proc cred.
3650 * Parameters: uthread_t The uthread to update
3651 * proc_t The process to update to
3655 * Notes: This code is common code called from system call or trap entry
3656 * in the case that the process thread may have been changed
3657 * since the last time the thread entered the kernel. It is
3658 * generally only called with the current uthread and process as
3662 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3664 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3665 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3666 kauth_cred_t old
= uthread
->uu_ucred
;
3667 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3668 if (IS_VALID_CRED(old
))
3669 kauth_cred_unref(&old
);
3675 * kauth_cred_get_with_ref
3677 * Description: Takes a reference on the current thread's credential, and then
3678 * returns a pointer to it to the caller.
3680 * Parameters: (void)
3682 * Returns: (kauth_cred_t) Pointer to the current thread's
3683 * newly referenced credential
3685 * Notes: This function takes a reference on the credential before
3686 * returning it to the caller.
3688 * It is the responsibility of the calling code to release this
3689 * reference when the credential is no longer in use.
3691 * Since the returned reference may be a persistent reference
3692 * (e.g. one cached in another data structure with a lifetime
3693 * longer than the calling function), this release may be delayed
3694 * until such time as the persistent reference is to be destroyed.
3695 * An example of this would be the per vnode credential cache used
3696 * to accelerate lookup operations.
3699 kauth_cred_get_with_ref(void)
3702 struct uthread
*uthread
;
3704 uthread
= get_bsdthread_info(current_thread());
3706 if (uthread
== NULL
)
3707 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3708 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3709 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3712 * We can lazy-bind credentials to threads, as long as their processes
3715 * XXX If we later inline this function, the code in this block
3716 * XXX should probably be called out in a function.
3718 if (uthread
->uu_ucred
== NOCRED
) {
3719 /* take reference for new cred in thread */
3720 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3722 /* take a reference for our caller */
3723 kauth_cred_ref(uthread
->uu_ucred
);
3724 return(uthread
->uu_ucred
);
3729 * kauth_cred_proc_ref
3731 * Description: Takes a reference on the current process's credential, and
3732 * then returns a pointer to it to the caller.
3734 * Parameters: procp Process whose credential we
3735 * intend to take a reference on
3737 * Returns: (kauth_cred_t) Pointer to the process's
3738 * newly referenced credential
3740 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3741 * after the refeence is taken to protect the p_ucred field of
3742 * the process referred to by procp.
3744 * Notes: This function takes a reference on the credential before
3745 * returning it to the caller.
3747 * It is the responsibility of the calling code to release this
3748 * reference when the credential is no longer in use.
3750 * Since the returned reference may be a persistent reference
3751 * (e.g. one cached in another data structure with a lifetime
3752 * longer than the calling function), this release may be delayed
3753 * until such time as the persistent reference is to be destroyed.
3754 * An example of this would be the per vnode credential cache used
3755 * to accelerate lookup operations.
3758 kauth_cred_proc_ref(proc_t procp
)
3762 proc_ucred_lock(procp
);
3763 cred
= proc_ucred(procp
);
3764 kauth_cred_ref(cred
);
3765 proc_ucred_unlock(procp
);
3773 * Description: Allocate a new credential
3775 * Parameters: (void)
3777 * Returns: !NULL Newly allocated credential
3778 * NULL Insufficient memory
3780 * Notes: The newly allocated credential is zero'ed as part of the
3781 * allocation process, with the exception of the reference
3782 * count, which is set to 1 to indicate a single reference
3783 * held by the caller.
3785 * Since newly allocated credentials have no external pointers
3786 * referencing them, prior to making them visible in an externally
3787 * visible pointer (e.g. by adding them to the credential hash
3788 * cache) is the only legal time in which an existing credential
3789 * can be safely iinitialized or modified directly.
3791 * After initialization, the caller is expected to call the
3792 * function kauth_cred_add() to add the credential to the hash
3793 * cache, after which time it's frozen and becomes publically
3796 * The release protocol depends on kauth_hash_add() being called
3797 * before kauth_cred_rele() (there is a diagnostic panic which
3798 * will trigger if this protocol is not observed).
3800 * XXX: This function really ought to be static, rather than being
3801 * exported as KPI, since a failure of kauth_cred_add() can only
3802 * be handled by an explicit free of the credential; such frees
3803 * depend on knowlegdge of the allocation method used, which is
3804 * permitted to change between kernel revisions.
3806 * XXX: In the insufficient resource case, this code panic's rather
3807 * than returning a NULL pointer; the code that calls this
3808 * function needs to be audited before this can be changed.
3811 kauth_cred_alloc(void)
3813 kauth_cred_t newcred
;
3815 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3817 posix_cred_t newpcred
= posix_cred_get(newcred
);
3818 bzero(newcred
, sizeof(*newcred
));
3819 newcred
->cr_ref
= 1;
3820 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3821 /* must do this, or cred has same group membership as uid 0 */
3822 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3825 panic("kauth_cred_alloc: couldn't allocate credential");
3829 #if KAUTH_CRED_HASH_DEBUG
3834 mac_cred_label_init(newcred
);
3844 * Description: Look to see if we already have a known credential in the hash
3845 * cache; if one is found, bump the reference count and return
3846 * it. If there are no credentials that match the given
3847 * credential, then allocate a new credential.
3849 * Parameters: cred Template for credential to
3852 * Returns: (kauth_cred_t) The credential that was found
3853 * in the hash or created
3854 * NULL kauth_cred_add() failed, or
3855 * there was not an egid specified
3857 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3858 * maintain this field, we can't expect callers to know how it
3859 * needs to be set. Callers should be prepared for this field
3860 * to be overwritten.
3862 * XXX: This code will tight-loop if memory for a new credential is
3863 * persistently unavailable; this is perhaps not the wisest way
3864 * to handle this condition, but current callers do not expect
3868 kauth_cred_create(kauth_cred_t cred
)
3870 kauth_cred_t found_cred
, new_cred
= NULL
;
3871 posix_cred_t pcred
= posix_cred_get(cred
);
3874 KAUTH_CRED_HASH_LOCK_ASSERT();
3876 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3877 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3880 * If the template credential is not opting out of external
3881 * group membership resolution, then we need to check that
3882 * the UID we will be using is resolvable by the external
3883 * resolver. If it's not, then we opt it out anyway, since
3884 * all future external resolution requests will be failing
3885 * anyway, and potentially taking a long time to do it. We
3886 * use gid 0 because we always know it will exist and not
3887 * trigger additional lookups. This is OK, because we end up
3888 * precatching the information here as a result.
3890 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3892 * It's a recognized value; we don't really care about
3893 * the answer, so long as it's something the external
3894 * resolver could have vended.
3896 pcred
->cr_gmuid
= pcred
->cr_uid
;
3899 * It's not something the external resolver could
3900 * have vended, so we don't want to ask it more
3901 * questions about the credential in the future. This
3902 * speeds up future lookups, as long as the caller
3903 * caches results; otherwise, it the same recurring
3904 * cost. Since most credentials are used multiple
3905 * times, we still get some performance win from this.
3907 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3908 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3912 /* Caller *must* specify at least the egid in cr_groups[0] */
3913 if (pcred
->cr_ngroups
< 1)
3917 KAUTH_CRED_HASH_LOCK();
3918 found_cred
= kauth_cred_find(cred
);
3919 if (found_cred
!= NULL
) {
3921 * Found an existing credential so we'll bump
3922 * reference count and return
3924 kauth_cred_ref(found_cred
);
3925 KAUTH_CRED_HASH_UNLOCK();
3928 KAUTH_CRED_HASH_UNLOCK();
3931 * No existing credential found. Create one and add it to
3934 new_cred
= kauth_cred_alloc();
3935 if (new_cred
!= NULL
) {
3937 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3938 new_pcred
->cr_uid
= pcred
->cr_uid
;
3939 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3940 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3941 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3942 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3943 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3944 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3945 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3947 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3948 sizeof(new_cred
->cr_audit
));
3950 new_pcred
->cr_flags
= pcred
->cr_flags
;
3952 KAUTH_CRED_HASH_LOCK();
3953 err
= kauth_cred_add(new_cred
);
3954 KAUTH_CRED_HASH_UNLOCK();
3956 /* Retry if kauth_cred_add returns non zero value */
3960 mac_cred_label_destroy(new_cred
);
3962 AUDIT_SESSION_UNREF(new_cred
);
3964 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3974 * kauth_cred_setresuid
3976 * Description: Update the given credential using the UID arguments. The given
3977 * UIDs are used to set the effective UID, real UID, saved UID,
3978 * and GMUID (used for group membership checking).
3980 * Parameters: cred The original credential
3981 * ruid The new real UID
3982 * euid The new effective UID
3983 * svuid The new saved UID
3984 * gmuid KAUTH_UID_NONE -or- the new
3985 * group membership UID
3987 * Returns: (kauth_cred_t) The updated credential
3989 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3990 * setting, so if you don't want it to change, pass it the
3991 * previous value, explicitly.
3993 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3994 * if it returns a credential other than the one it is passed,
3995 * will have dropped the reference on the passed credential. All
3996 * callers should be aware of this, and treat this function as an
3997 * unref + ref, potentially on different credentials.
3999 * Because of this, the caller is expected to take its own
4000 * reference on the credential passed as the first parameter,
4001 * and be prepared to release the reference on the credential
4002 * that is returned to them, if it is not intended to be a
4003 * persistent reference.
4006 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
4008 struct ucred temp_cred
;
4009 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4010 posix_cred_t pcred
= posix_cred_get(cred
);
4012 NULLCRED_CHECK(cred
);
4015 * We don't need to do anything if the UIDs we are changing are
4016 * already the same as the UIDs passed in
4018 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
4019 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
4020 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
4021 (pcred
->cr_gmuid
== gmuid
)) {
4022 /* no change needed */
4027 * Look up in cred hash table to see if we have a matching credential
4028 * with the new values; this is done by calling kauth_cred_update().
4030 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4031 if (euid
!= KAUTH_UID_NONE
) {
4032 temp_pcred
->cr_uid
= euid
;
4034 if (ruid
!= KAUTH_UID_NONE
) {
4035 temp_pcred
->cr_ruid
= ruid
;
4037 if (svuid
!= KAUTH_UID_NONE
) {
4038 temp_pcred
->cr_svuid
= svuid
;
4042 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
4043 * opt out of participation in external group resolution, unless we
4044 * unless we explicitly opt back in later.
4046 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
4047 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4050 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4055 * kauth_cred_setresgid
4057 * Description: Update the given credential using the GID arguments. The given
4058 * GIDs are used to set the effective GID, real GID, and saved
4061 * Parameters: cred The original credential
4062 * rgid The new real GID
4063 * egid The new effective GID
4064 * svgid The new saved GID
4066 * Returns: (kauth_cred_t) The updated credential
4068 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4069 * if it returns a credential other than the one it is passed,
4070 * will have dropped the reference on the passed credential. All
4071 * callers should be aware of this, and treat this function as an
4072 * unref + ref, potentially on different credentials.
4074 * Because of this, the caller is expected to take its own
4075 * reference on the credential passed as the first parameter,
4076 * and be prepared to release the reference on the credential
4077 * that is returned to them, if it is not intended to be a
4078 * persistent reference.
4081 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
4083 struct ucred temp_cred
;
4084 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4085 posix_cred_t pcred
= posix_cred_get(cred
);
4087 NULLCRED_CHECK(cred
);
4088 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
4091 * We don't need to do anything if the given GID are already the
4092 * same as the GIDs in the credential.
4094 if (pcred
->cr_groups
[0] == egid
&&
4095 pcred
->cr_rgid
== rgid
&&
4096 pcred
->cr_svgid
== svgid
) {
4097 /* no change needed */
4102 * Look up in cred hash table to see if we have a matching credential
4103 * with the new values; this is done by calling kauth_cred_update().
4105 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4106 if (egid
!= KAUTH_GID_NONE
) {
4107 /* displacing a supplementary group opts us out of memberd */
4108 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
4109 DEBUG_CRED_CHANGE("displaced!\n");
4110 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4111 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4113 DEBUG_CRED_CHANGE("not displaced\n");
4116 if (rgid
!= KAUTH_GID_NONE
) {
4117 temp_pcred
->cr_rgid
= rgid
;
4119 if (svgid
!= KAUTH_GID_NONE
) {
4120 temp_pcred
->cr_svgid
= svgid
;
4123 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4128 * Update the given credential with the given groups. We only allocate a new
4129 * credential when the given gid actually results in changes to the existing
4131 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4132 * which will be used for group membership checking.
4135 * kauth_cred_setgroups
4137 * Description: Update the given credential using the provide supplementary
4138 * group list and group membership UID
4140 * Parameters: cred The original credential
4141 * groups Pointer to gid_t array which
4142 * contains the new group list
4143 * groupcount The count of valid groups which
4144 * are contained in 'groups'
4145 * gmuid KAUTH_UID_NONE -or- the new
4146 * group membership UID
4148 * Returns: (kauth_cred_t) The updated credential
4150 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4151 * setting, so if you don't want it to change, pass it the
4152 * previous value, explicitly.
4154 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4155 * if it returns a credential other than the one it is passed,
4156 * will have dropped the reference on the passed credential. All
4157 * callers should be aware of this, and treat this function as an
4158 * unref + ref, potentially on different credentials.
4160 * Because of this, the caller is expected to take its own
4161 * reference on the credential passed as the first parameter,
4162 * and be prepared to release the reference on the credential
4163 * that is returned to them, if it is not intended to be a
4164 * persistent reference.
4166 * XXX: Changes are determined in ordinal order - if the caller passes
4167 * in the same groups list that is already present in the
4168 * credential, but the members are in a different order, even if
4169 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4170 * is considered a modification to the credential, and a new
4171 * credential is created.
4173 * This should perhaps be better optimized, but it is considered
4174 * to be the caller's problem.
4177 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
4180 struct ucred temp_cred
;
4181 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4184 NULLCRED_CHECK(cred
);
4186 pcred
= posix_cred_get(cred
);
4189 * We don't need to do anything if the given list of groups does not
4192 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
4193 for (i
= 0; i
< groupcount
; i
++) {
4194 if (pcred
->cr_groups
[i
] != groups
[i
])
4197 if (i
== groupcount
) {
4198 /* no change needed */
4204 * Look up in cred hash table to see if we have a matching credential
4205 * with new values. If we are setting or clearing the gmuid, then
4206 * update the cr_flags, since clearing it is sticky. This permits an
4207 * opt-out of memberd processing using setgroups(), and an opt-in
4208 * using initgroups(). This is required for POSIX conformance.
4210 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4211 temp_pcred
->cr_ngroups
= groupcount
;
4212 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
4213 temp_pcred
->cr_gmuid
= gmuid
;
4214 if (gmuid
== KAUTH_UID_NONE
)
4215 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4217 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4219 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4223 * Notes: The return value exists to account for the possibility of a
4224 * kauth_cred_t without a POSIX label. This will be the case in
4225 * the future (see posix_cred_get() below, for more details).
4227 #if CONFIG_EXT_RESOLVER
4228 int kauth_external_supplementary_groups_supported
= 1;
4230 SYSCTL_INT(_kern
, OID_AUTO
, ds_supgroups_supported
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &kauth_external_supplementary_groups_supported
, 0, "");
4234 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
4236 int limit
= NGROUPS
;
4239 pcred
= posix_cred_get(cred
);
4241 #if CONFIG_EXT_RESOLVER
4243 * If we've not opted out of using the resolver, then convert the cred to a list
4244 * of supplemental groups. We do this only if there has been a resolver to talk to,
4245 * since we may be too early in boot, or in an environment that isn't using DS.
4247 if (kauth_identitysvc_has_registered
&& kauth_external_supplementary_groups_supported
&& (pcred
->cr_flags
& CRF_NOMEMBERD
) == 0) {
4248 uid_t uid
= kauth_cred_getuid(cred
);
4251 err
= kauth_cred_uid2groups(&uid
, grouplist
, countp
);
4255 /* On error just fall through */
4256 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err
);
4258 #endif /* CONFIG_EXT_RESOLVER */
4261 * If they just want a copy of the groups list, they may not care
4262 * about the actual count. If they specify an input count, however,
4263 * treat it as an indicator of the buffer size available in grouplist,
4264 * and limit the returned list to that size.
4267 limit
= MIN(*countp
, pcred
->cr_ngroups
);
4271 memcpy(grouplist
, pcred
->cr_groups
, sizeof(gid_t
) * limit
);
4278 * kauth_cred_setuidgid
4280 * Description: Update the given credential using the UID and GID arguments.
4281 * The given UID is used to set the effective UID, real UID, and
4282 * saved UID. The given GID is used to set the effective GID,
4283 * real GID, and saved GID.
4285 * Parameters: cred The original credential
4286 * uid The new UID to use
4287 * gid The new GID to use
4289 * Returns: (kauth_cred_t) The updated credential
4291 * Notes: We set the gmuid to uid if the credential we are inheriting
4292 * from has not opted out of memberd participation; otherwise
4293 * we set it to KAUTH_UID_NONE
4295 * This code is only ever called from the per-thread credential
4296 * code path in the "set per thread credential" case; and in
4297 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4300 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4301 * if it returns a credential other than the one it is passed,
4302 * will have dropped the reference on the passed credential. All
4303 * callers should be aware of this, and treat this function as an
4304 * unref + ref, potentially on different credentials.
4306 * Because of this, the caller is expected to take its own
4307 * reference on the credential passed as the first parameter,
4308 * and be prepared to release the reference on the credential
4309 * that is returned to them, if it is not intended to be a
4310 * persistent reference.
4313 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4315 struct ucred temp_cred
;
4316 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4319 NULLCRED_CHECK(cred
);
4321 pcred
= posix_cred_get(cred
);
4324 * We don't need to do anything if the effective, real and saved
4325 * user IDs are already the same as the user ID passed into us.
4327 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
4328 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
4329 /* no change needed */
4334 * Look up in cred hash table to see if we have a matching credential
4335 * with the new values.
4337 bzero(&temp_cred
, sizeof(temp_cred
));
4338 temp_pcred
->cr_uid
= uid
;
4339 temp_pcred
->cr_ruid
= uid
;
4340 temp_pcred
->cr_svuid
= uid
;
4341 temp_pcred
->cr_flags
= pcred
->cr_flags
;
4342 /* inherit the opt-out of memberd */
4343 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
4344 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4345 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4347 temp_pcred
->cr_gmuid
= uid
;
4348 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
4350 temp_pcred
->cr_ngroups
= 1;
4351 /* displacing a supplementary group opts us out of memberd */
4352 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
4353 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4354 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4356 temp_pcred
->cr_rgid
= gid
;
4357 temp_pcred
->cr_svgid
= gid
;
4359 temp_cred
.cr_label
= cred
->cr_label
;
4362 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4367 * kauth_cred_setsvuidgid
4369 * Description: Function used by execve to set the saved uid and gid values
4370 * for suid/sgid programs
4372 * Parameters: cred The credential to update
4373 * uid The saved uid to set
4374 * gid The saved gid to set
4376 * Returns: (kauth_cred_t) The updated credential
4378 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4379 * if it returns a credential other than the one it is passed,
4380 * will have dropped the reference on the passed credential. All
4381 * callers should be aware of this, and treat this function as an
4382 * unref + ref, potentially on different credentials.
4384 * Because of this, the caller is expected to take its own
4385 * reference on the credential passed as the first parameter,
4386 * and be prepared to release the reference on the credential
4387 * that is returned to them, if it is not intended to be a
4388 * persistent reference.
4391 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4393 struct ucred temp_cred
;
4394 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4397 NULLCRED_CHECK(cred
);
4399 pcred
= posix_cred_get(cred
);
4401 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4404 * We don't need to do anything if the effective, real and saved
4405 * uids are already the same as the uid provided. This check is
4406 * likely insufficient.
4408 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4409 /* no change needed */
4412 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4414 /* look up in cred hash table to see if we have a matching credential
4417 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4418 temp_pcred
->cr_svuid
= uid
;
4419 temp_pcred
->cr_svgid
= gid
;
4421 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4426 * kauth_cred_setauditinfo
4428 * Description: Update the given credential using the given au_session_t.
4430 * Parameters: cred The original credential
4431 * auditinfo_p Pointer to ne audit information
4433 * Returns: (kauth_cred_t) The updated credential
4435 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4436 * if it returns a credential other than the one it is passed,
4437 * will have dropped the reference on the passed credential. All
4438 * callers should be aware of this, and treat this function as an
4439 * unref + ref, potentially on different credentials.
4441 * Because of this, the caller is expected to take its own
4442 * reference on the credential passed as the first parameter,
4443 * and be prepared to release the reference on the credential
4444 * that is returned to them, if it is not intended to be a
4445 * persistent reference.
4448 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4450 struct ucred temp_cred
;
4452 NULLCRED_CHECK(cred
);
4455 * We don't need to do anything if the audit info is already the
4456 * same as the audit info in the credential provided.
4458 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4459 /* no change needed */
4463 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4464 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4466 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
4471 * kauth_cred_label_update
4473 * Description: Update the MAC label associated with a credential
4475 * Parameters: cred The original credential
4476 * label The MAC label to set
4478 * Returns: (kauth_cred_t) The updated credential
4480 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4481 * if it returns a credential other than the one it is passed,
4482 * will have dropped the reference on the passed credential. All
4483 * callers should be aware of this, and treat this function as an
4484 * unref + ref, potentially on different credentials.
4486 * Because of this, the caller is expected to take its own
4487 * reference on the credential passed as the first parameter,
4488 * and be prepared to release the reference on the credential
4489 * that is returned to them, if it is not intended to be a
4490 * persistent reference.
4493 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4495 kauth_cred_t newcred
;
4496 struct ucred temp_cred
;
4498 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4500 mac_cred_label_init(&temp_cred
);
4501 mac_cred_label_associate(cred
, &temp_cred
);
4502 mac_cred_label_update(&temp_cred
, label
);
4504 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4505 mac_cred_label_destroy(&temp_cred
);
4510 * kauth_cred_label_update_execve
4512 * Description: Update the MAC label associated with a credential as
4515 * Parameters: cred The original credential
4517 * scriptl The script MAC label
4518 * execl The executable MAC label
4519 * disjointp Pointer to flag to set if old
4520 * and returned credentials are
4523 * Returns: (kauth_cred_t) The updated credential
4526 * *disjointp Set to 1 for disjoint creds
4528 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4529 * if it returns a credential other than the one it is passed,
4530 * will have dropped the reference on the passed credential. All
4531 * callers should be aware of this, and treat this function as an
4532 * unref + ref, potentially on different credentials.
4534 * Because of this, the caller is expected to take its own
4535 * reference on the credential passed as the first parameter,
4536 * and be prepared to release the reference on the credential
4537 * that is returned to them, if it is not intended to be a
4538 * persistent reference.
4543 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4544 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4545 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjointp
, int *labelupdateerror
)
4547 kauth_cred_t newcred
;
4548 struct ucred temp_cred
;
4550 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4552 mac_cred_label_init(&temp_cred
);
4553 mac_cred_label_associate(cred
, &temp_cred
);
4554 mac_cred_label_update_execve(ctx
, &temp_cred
,
4555 vp
, offset
, scriptvp
, scriptl
, execl
, csflags
,
4556 macextensions
, disjointp
, labelupdateerror
);
4558 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4559 mac_cred_label_destroy(&temp_cred
);
4564 * kauth_proc_label_update
4566 * Description: Update the label inside the credential associated with the process.
4568 * Parameters: p The process to modify
4569 * label The label to place in the process credential
4571 * Notes: The credential associated with the process may change as a result
4572 * of this call. The caller should not assume the process reference to
4573 * the old credential still exists.
4575 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4577 kauth_cred_t my_cred
, my_new_cred
;
4579 my_cred
= kauth_cred_proc_ref(p
);
4581 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4583 /* get current credential and take a reference while we muck with it */
4587 * Set the credential with new info. If there is no change,
4588 * we get back the same credential we passed in; if there is
4589 * a change, we drop the reference on the credential we
4590 * passed in. The subsequent compare is safe, because it is
4591 * a pointer compare rather than a contents compare.
4593 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4594 if (my_cred
!= my_new_cred
) {
4596 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
);
4600 * We need to protect for a race where another thread
4601 * also changed the credential after we took our
4602 * reference. If p_ucred has changed then we should
4603 * restart this again with the new cred.
4605 if (p
->p_ucred
!= my_cred
) {
4606 proc_ucred_unlock(p
);
4607 kauth_cred_unref(&my_new_cred
);
4608 my_cred
= kauth_cred_proc_ref(p
);
4612 p
->p_ucred
= my_new_cred
;
4613 /* update cred on proc */
4614 PROC_UPDATE_CREDS_ONPROC(p
);
4616 proc_ucred_unlock(p
);
4620 /* Drop old proc reference or our extra reference */
4621 kauth_cred_unref(&my_cred
);
4627 * kauth_proc_label_update_execve
4629 * Description: Update the label inside the credential associated with the
4630 * process as part of a transitioning execve. The label will
4631 * be updated by the policies as part of this processing, not
4632 * provided up front.
4634 * Parameters: p The process to modify
4635 * ctx The context of the exec
4636 * vp The vnode being exec'ed
4637 * scriptl The script MAC label
4638 * execl The executable MAC label
4639 * lupdateerror The error place holder for MAC label authority
4640 * to update about possible termination
4642 * Returns: 0 Label update did not make credential
4644 * 1 Label update caused credential to be
4647 * Notes: The credential associated with the process WILL change as a
4648 * result of this call. The caller should not assume the process
4649 * reference to the old credential still exists.
4653 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4654 struct vnode
*vp
, off_t offset
, struct vnode
*scriptvp
, struct label
*scriptl
,
4655 struct label
*execl
, unsigned int *csflags
, void *macextensions
, int *disjoint
, int *update_return
)
4657 kauth_cred_t my_cred
, my_new_cred
;
4658 my_cred
= kauth_cred_proc_ref(p
);
4660 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4662 /* get current credential and take a reference while we muck with it */
4666 * Set the credential with new info. If there is no change,
4667 * we get back the same credential we passed in; if there is
4668 * a change, we drop the reference on the credential we
4669 * passed in. The subsequent compare is safe, because it is
4670 * a pointer compare rather than a contents compare.
4672 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, offset
, scriptvp
, scriptl
, execl
, csflags
, macextensions
, disjoint
, update_return
);
4673 if (my_cred
!= my_new_cred
) {
4675 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
);
4679 * We need to protect for a race where another thread
4680 * also changed the credential after we took our
4681 * reference. If p_ucred has changed then we should
4682 * restart this again with the new cred.
4684 if (p
->p_ucred
!= my_cred
) {
4685 proc_ucred_unlock(p
);
4686 kauth_cred_unref(&my_new_cred
);
4687 my_cred
= kauth_cred_proc_ref(p
);
4691 p
->p_ucred
= my_new_cred
;
4692 /* update cred on proc */
4693 PROC_UPDATE_CREDS_ONPROC(p
);
4694 proc_ucred_unlock(p
);
4698 /* Drop old proc reference or our extra reference */
4699 kauth_cred_unref(&my_cred
);
4704 * for temporary binary compatibility
4706 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4708 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4710 return kauth_cred_label_update(cred
, label
);
4713 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4715 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4717 return kauth_proc_label_update(p
, label
);
4723 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4724 * Since we cannot build our export lists based on the kernel configuration we need
4728 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4734 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4741 * for temporary binary compatibility
4743 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4745 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4750 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4752 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4762 * Description: Add a reference to the passed credential
4764 * Parameters: cred The credential to reference
4768 * Notes: This function adds a reference to the provided credential;
4769 * the existing reference on the credential is assumed to be
4770 * held stable over this operation by taking the appropriate
4771 * lock to protect the pointer from which it is being referenced,
4772 * if necessary (e.g. the proc lock is held over the call if the
4773 * credential being referenced is from p_ucred, the vnode lock
4774 * if from the per vnode name cache cred cache, and so on).
4776 * This is safe from the kauth_cred_unref() path, since an atomic
4777 * add is used, and the unref path specifically checks to see that
4778 * the value has not been changed to add a reference between the
4779 * time the credential is unreferenced by another pointer and the
4780 * time it is unreferenced from the cred hash cache.
4783 kauth_cred_ref(kauth_cred_t cred
)
4787 NULLCRED_CHECK(cred
);
4789 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4792 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4794 #if 0 // use this to watch a specific credential
4795 if ( is_target_cred( cred
) != 0 ) {
4805 * kauth_cred_unref_hashlocked
4807 * Description: release a credential reference; when the last reference is
4808 * released, the credential will be freed.
4810 * Parameters: credp Pointer to address containing
4811 * credential to be freed
4813 * Returns: TRUE if the credential must be destroyed by the caller.
4817 * *credp Set to NOCRED
4819 * Notes: This function assumes the credential hash lock is held.
4821 * This function is internal use only, since the hash lock is
4822 * scoped to this compilation unit.
4824 * This function destroys the contents of the pointer passed by
4825 * the caller to prevent the caller accidentally attempting to
4826 * release a given reference twice in error.
4828 * The last reference is considered to be released when a release
4829 * of a credential of a reference count of 2 occurs; this is an
4830 * intended effect, to take into account the reference held by
4831 * the credential hash, which is released at the same time.
4834 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4837 boolean_t destroy_it
= FALSE
;
4839 KAUTH_CRED_HASH_LOCK_ASSERT();
4840 NULLCRED_CHECK(*credp
);
4842 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4846 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4848 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4851 #if 0 // use this to watch a specific credential
4852 if ( is_target_cred( *credp
) != 0 ) {
4858 * If the old_value is 2, then we have just released the last external
4859 * reference to this credential
4861 if (old_value
< 3) {
4862 /* The last absolute reference is our credential hash table */
4863 destroy_it
= kauth_cred_remove(*credp
);
4866 if (destroy_it
== FALSE
) {
4870 return (destroy_it
);
4877 * Description: Release a credential reference while holding the credential
4878 * hash lock; when the last reference is released, the credential
4881 * Parameters: credp Pointer to address containing
4882 * credential to be freed
4887 * *credp Set to NOCRED
4889 * Notes: See kauth_cred_unref_hashlocked() for more information.
4893 kauth_cred_unref(kauth_cred_t
*credp
)
4895 boolean_t destroy_it
;
4897 KAUTH_CRED_HASH_LOCK();
4898 destroy_it
= kauth_cred_unref_hashlocked(credp
);
4899 KAUTH_CRED_HASH_UNLOCK();
4901 if (destroy_it
== TRUE
) {
4902 assert(*credp
!= NOCRED
);
4904 mac_cred_label_destroy(*credp
);
4906 AUDIT_SESSION_UNREF(*credp
);
4908 (*credp
)->cr_ref
= 0;
4909 FREE_ZONE(*credp
, sizeof(*(*credp
)), M_CRED
);
4919 * Description: release a credential reference; when the last reference is
4920 * released, the credential will be freed
4922 * Parameters: cred Credential to release
4926 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4927 * clear the pointer in the caller to avoid multiple releases of
4928 * the same credential. The currently recommended interface is
4929 * kauth_cred_unref().
4932 kauth_cred_rele(kauth_cred_t cred
)
4934 kauth_cred_unref(&cred
);
4936 #endif /* !__LP64__ */
4942 * Description: Duplicate a credential via alloc and copy; the new credential
4945 * Parameters: cred The credential to duplicate
4947 * Returns: (kauth_cred_t) The duplicate credential
4949 * Notes: The typical value to calling this routine is if you are going
4950 * to modify an existing credential, and expect to need a new one
4951 * from the hash cache.
4953 * This should probably not be used in the majority of cases;
4954 * if you are using it instead of kauth_cred_create(), you are
4955 * likely making a mistake.
4957 * The newly allocated credential is copied as part of the
4958 * allocation process, with the exception of the reference
4959 * count, which is set to 1 to indicate a single reference
4960 * held by the caller.
4962 * Since newly allocated credentials have no external pointers
4963 * referencing them, prior to making them visible in an externally
4964 * visible pointer (e.g. by adding them to the credential hash
4965 * cache) is the only legal time in which an existing credential
4966 * can be safely initialized or modified directly.
4968 * After initialization, the caller is expected to call the
4969 * function kauth_cred_add() to add the credential to the hash
4970 * cache, after which time it's frozen and becomes publicly
4973 * The release protocol depends on kauth_hash_add() being called
4974 * before kauth_cred_rele() (there is a diagnostic panic which
4975 * will trigger if this protocol is not observed).
4979 kauth_cred_dup(kauth_cred_t cred
)
4981 kauth_cred_t newcred
;
4983 struct label
*temp_label
;
4987 if (cred
== NOCRED
|| cred
== FSCRED
)
4988 panic("kauth_cred_dup: bad credential");
4990 newcred
= kauth_cred_alloc();
4991 if (newcred
!= NULL
) {
4993 temp_label
= newcred
->cr_label
;
4995 bcopy(cred
, newcred
, sizeof(*newcred
));
4997 newcred
->cr_label
= temp_label
;
4998 mac_cred_label_associate(cred
, newcred
);
5000 AUDIT_SESSION_REF(cred
);
5001 newcred
->cr_ref
= 1;
5007 * kauth_cred_copy_real
5009 * Description: Returns a credential based on the passed credential but which
5010 * reflects the real rather than effective UID and GID.
5012 * Parameters: cred The credential from which to
5013 * derive the new credential
5015 * Returns: (kauth_cred_t) The copied credential
5017 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
5018 * result, the caller is responsible for dropping BOTH the
5019 * additional reference on the passed cred (if any), and the
5020 * credential returned by this function. The drop should be
5021 * via the kauth_cred_unref() KPI.
5024 kauth_cred_copy_real(kauth_cred_t cred
)
5026 kauth_cred_t newcred
= NULL
, found_cred
;
5027 struct ucred temp_cred
;
5028 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
5029 posix_cred_t pcred
= posix_cred_get(cred
);
5031 /* if the credential is already 'real', just take a reference */
5032 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
5033 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
5034 kauth_cred_ref(cred
);
5039 * Look up in cred hash table to see if we have a matching credential
5040 * with the new values.
5042 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
5043 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
5044 /* displacing a supplementary group opts us out of memberd */
5045 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
5046 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
5047 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
5050 * If the cred is not opted out, make sure we are using the r/euid
5053 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
5054 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
5059 KAUTH_CRED_HASH_LOCK();
5060 found_cred
= kauth_cred_find(&temp_cred
);
5061 if (found_cred
== cred
) {
5062 /* same cred so just bail */
5063 KAUTH_CRED_HASH_UNLOCK();
5066 if (found_cred
!= NULL
) {
5068 * Found a match so we bump reference count on new
5069 * one. We leave the old one alone.
5071 kauth_cred_ref(found_cred
);
5072 KAUTH_CRED_HASH_UNLOCK();
5077 * Must allocate a new credential, copy in old credential
5078 * data and update the real user and group IDs.
5080 newcred
= kauth_cred_dup(&temp_cred
);
5081 err
= kauth_cred_add(newcred
);
5082 KAUTH_CRED_HASH_UNLOCK();
5084 /* Retry if kauth_cred_add() fails */
5088 mac_cred_label_destroy(newcred
);
5090 AUDIT_SESSION_UNREF(newcred
);
5092 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
5103 * Description: Common code to update a credential
5105 * Parameters: old_cred Reference counted credential
5107 * model_cred Non-reference counted model
5108 * credential to apply to the
5109 * credential to be updated
5110 * retain_auditinfo Flag as to whether or not the
5111 * audit information should be
5112 * copied from the old_cred into
5115 * Returns: (kauth_cred_t) The updated credential
5117 * IMPORTANT: This function will potentially return a credential other than
5118 * the one it is passed, and if so, it will have dropped the
5119 * reference on the passed credential. All callers should be
5120 * aware of this, and treat this function as an unref + ref,
5121 * potentially on different credentials.
5123 * Because of this, the caller is expected to take its own
5124 * reference on the credential passed as the first parameter,
5125 * and be prepared to release the reference on the credential
5126 * that is returned to them, if it is not intended to be a
5127 * persistent reference.
5130 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
5131 boolean_t retain_auditinfo
)
5133 kauth_cred_t found_cred
, new_cred
= NULL
;
5136 * Make sure we carry the auditinfo forward to the new credential
5137 * unless we are actually updating the auditinfo.
5139 if (retain_auditinfo
) {
5140 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
5141 sizeof(model_cred
->cr_audit
));
5147 KAUTH_CRED_HASH_LOCK();
5148 found_cred
= kauth_cred_find(model_cred
);
5149 if (found_cred
== old_cred
) {
5150 /* same cred so just bail */
5151 KAUTH_CRED_HASH_UNLOCK();
5154 if (found_cred
!= NULL
) {
5155 boolean_t destroy_it
;
5157 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
5159 * Found a match so we bump reference count on new
5160 * one and decrement reference count on the old one.
5162 kauth_cred_ref(found_cred
);
5163 destroy_it
= kauth_cred_unref_hashlocked(&old_cred
);
5164 KAUTH_CRED_HASH_UNLOCK();
5165 if (destroy_it
== TRUE
) {
5166 assert(old_cred
!= NOCRED
);
5168 mac_cred_label_destroy(old_cred
);
5170 AUDIT_SESSION_UNREF(old_cred
);
5172 old_cred
->cr_ref
= 0;
5173 FREE_ZONE(old_cred
, sizeof(*old_cred
), M_CRED
);
5181 * Must allocate a new credential using the model. also
5182 * adds the new credential to the credential hash table.
5184 new_cred
= kauth_cred_dup(model_cred
);
5185 err
= kauth_cred_add(new_cred
);
5186 KAUTH_CRED_HASH_UNLOCK();
5188 /* retry if kauth_cred_add returns non zero value */
5192 mac_cred_label_destroy(new_cred
);
5194 AUDIT_SESSION_UNREF(new_cred
);
5196 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
5200 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
5201 kauth_cred_unref(&old_cred
);
5209 * Description: Add the given credential to our credential hash table and
5210 * take an additional reference to account for our use of the
5211 * credential in the hash table
5213 * Parameters: new_cred Credential to insert into cred
5216 * Returns: 0 Success
5217 * -1 Hash insertion failed: caller
5220 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5222 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5225 kauth_cred_add(kauth_cred_t new_cred
)
5229 KAUTH_CRED_HASH_LOCK_ASSERT();
5231 hash_key
= kauth_cred_get_hashkey(new_cred
);
5232 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5234 /* race fix - there is a window where another matching credential
5235 * could have been inserted between the time this one was created and we
5236 * got the hash lock. If we find a match return an error and have the
5239 if (kauth_cred_find(new_cred
) != NULL
) {
5243 /* take a reference for our use in credential hash table */
5244 kauth_cred_ref(new_cred
);
5246 /* insert the credential into the hash table */
5247 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
5256 * Description: Remove the given credential from our credential hash table
5258 * Parameters: cred Credential to remove from cred
5261 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not.
5263 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5265 * Notes: The check for the reference increment after entry is generally
5266 * agree to be safe, since we use atomic operations, and the
5267 * following code occurs with the hash lock held; in theory, this
5268 * protects us from the 2->1 reference that gets us here.
5271 kauth_cred_remove(kauth_cred_t cred
)
5274 kauth_cred_t found_cred
;
5276 hash_key
= kauth_cred_get_hashkey(cred
);
5277 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5280 if (cred
->cr_ref
< 1)
5281 panic("cred reference underflow");
5282 if (cred
->cr_ref
> 1)
5283 return (FALSE
); /* someone else got a ref */
5285 /* Find cred in the credential hash table */
5286 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5287 if (found_cred
== cred
) {
5288 /* found a match, remove it from the hash table */
5289 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
5290 #if KAUTH_CRED_HASH_DEBUG
5297 /* Did not find a match... this should not happen! XXX Make panic? */
5298 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
5306 * Description: Using the given credential data, look for a match in our
5307 * credential hash table
5309 * Parameters: cred Credential to lookup in cred
5312 * Returns: NULL Not found
5313 * !NULL Matching credential already in
5316 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5319 kauth_cred_find(kauth_cred_t cred
)
5322 kauth_cred_t found_cred
;
5323 posix_cred_t pcred
= posix_cred_get(cred
);
5325 KAUTH_CRED_HASH_LOCK_ASSERT();
5327 #if KAUTH_CRED_HASH_DEBUG
5328 static int test_count
= 0;
5331 if ((test_count
% 200) == 0) {
5332 kauth_cred_hash_print();
5336 hash_key
= kauth_cred_get_hashkey(cred
);
5337 hash_key
%= KAUTH_CRED_TABLE_SIZE
;
5339 /* Find cred in the credential hash table */
5340 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
5342 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
5345 * don't worry about the label unless the flags in
5346 * either credential tell us to.
5348 match
= (bcmp(found_pcred
, pcred
, sizeof (*pcred
)) == 0) ? TRUE
: FALSE
;
5349 match
= match
&& ((bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
5350 sizeof(cred
->cr_audit
)) == 0) ? TRUE
: FALSE
);
5352 if (((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) ||
5353 ((pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0)) {
5354 match
= match
&& mac_cred_label_compare(found_cred
->cr_label
,
5363 /* No match found */
5372 * Description: Generates a hash key using data that makes up a credential;
5375 * Parameters: datap Pointer to data to hash
5376 * data_len Count of bytes to hash
5377 * start_key Start key value
5379 * Returns: (u_long) Returned hash key
5381 static inline u_long
5382 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
5384 u_long hash_key
= start_key
;
5387 while (data_len
> 0) {
5388 hash_key
= (hash_key
<< 4) + *datap
++;
5389 temp
= hash_key
& 0xF0000000;
5391 hash_key
^= temp
>> 24;
5401 * kauth_cred_get_hashkey
5403 * Description: Generate a hash key using data that makes up a credential;
5404 * based on ElfHash. We hash on the entire credential data,
5405 * not including the ref count or the TAILQ, which are mutable;
5406 * everything else isn't.
5408 * Parameters: cred Credential for which hash is
5411 * Returns: (u_long) Returned hash key
5413 * Notes: When actually moving the POSIX credential into a real label,
5414 * remember to update this hash computation.
5417 kauth_cred_get_hashkey(kauth_cred_t cred
)
5420 posix_cred_t pcred
= posix_cred_get(cred
);
5422 u_long hash_key
= 0;
5424 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5425 sizeof (struct posix_cred
),
5427 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5428 sizeof(struct au_session
),
5431 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5432 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5433 sizeof (struct label
),
5441 #if KAUTH_CRED_HASH_DEBUG
5443 * kauth_cred_hash_print
5445 * Description: Print out cred hash cache table information for debugging
5446 * purposes, including the credential contents
5448 * Parameters: (void)
5452 * Implicit returns: Results in console output
5455 kauth_cred_hash_print(void)
5458 kauth_cred_t found_cred
;
5460 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
5461 /* count slot hits, misses, collisions, and max depth */
5462 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5463 printf("[%02d] ", i
);
5465 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5470 kauth_cred_print(found_cred
);
5474 printf("NOCRED \n");
5478 #endif /* KAUTH_CRED_HASH_DEBUG */
5481 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5485 * Description: Print out an individual credential's contents for debugging
5488 * Parameters: cred The credential to print out
5492 * Implicit returns: Results in console output
5495 kauth_cred_print(kauth_cred_t cred
)
5499 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
);
5500 printf("group count %d gids ", cred
->cr_ngroups
);
5501 for (i
= 0; i
< NGROUPS
; i
++) {
5504 printf("%d ", cred
->cr_groups
[i
]);
5506 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5507 printf("auditinfo_addr %d %d %d %d %d %d\n",
5508 cred
->cr_audit
.s_aia_p
->ai_auid
,
5509 cred
->cr_audit
.as_mask
.am_success
,
5510 cred
->cr_audit
.as_mask
.am_failure
,
5511 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5512 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5513 cred
->cr_audit
.as_aia_p
->ai_asid
);
5516 int is_target_cred( kauth_cred_t the_cred
)
5518 if ( the_cred
->cr_uid
!= 0 )
5520 if ( the_cred
->cr_ruid
!= 0 )
5522 if ( the_cred
->cr_svuid
!= 0 )
5524 if ( the_cred
->cr_ngroups
!= 11 )
5526 if ( the_cred
->cr_groups
[0] != 11 )
5528 if ( the_cred
->cr_groups
[1] != 81 )
5530 if ( the_cred
->cr_groups
[2] != 63947 )
5532 if ( the_cred
->cr_groups
[3] != 80288 )
5534 if ( the_cred
->cr_groups
[4] != 89006 )
5536 if ( the_cred
->cr_groups
[5] != 52173 )
5538 if ( the_cred
->cr_groups
[6] != 84524 )
5540 if ( the_cred
->cr_groups
[7] != 79 )
5542 if ( the_cred
->cr_groups
[8] != 80292 )
5544 if ( the_cred
->cr_groups
[9] != 80 )
5546 if ( the_cred
->cr_groups
[10] != 90824 )
5548 if ( the_cred
->cr_rgid
!= 11 )
5550 if ( the_cred
->cr_svgid
!= 11 )
5552 if ( the_cred
->cr_gmuid
!= 3475 )
5554 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5557 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5559 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5561 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5563 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5565 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5567 if ( the_cred->cr_flags != 0 )
5570 return( -1 ); // found target cred
5573 void get_backtrace( void )
5576 void * my_stack
[ MAX_STACK_DEPTH
];
5579 if ( cred_debug_buf_p
== NULL
) {
5580 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5581 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5584 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5585 /* buffer is full */
5589 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5590 if ( my_depth
== 0 ) {
5591 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5595 /* fill new backtrace */
5596 my_slot
= cred_debug_buf_p
->next_slot
;
5597 cred_debug_buf_p
->next_slot
++;
5598 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5599 for ( i
= 0; i
< my_depth
; i
++ ) {
5600 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5607 /* subset of struct ucred for use in sysctl_dump_creds */
5608 struct debug_ucred
{
5610 u_long cr_ref
; /* reference count */
5611 uid_t cr_uid
; /* effective user id */
5612 uid_t cr_ruid
; /* real user id */
5613 uid_t cr_svuid
; /* saved user id */
5614 short cr_ngroups
; /* number of groups in advisory list */
5615 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5616 gid_t cr_rgid
; /* real group id */
5617 gid_t cr_svgid
; /* saved group id */
5618 uid_t cr_gmuid
; /* UID for group membership purposes */
5619 struct auditinfo_addr cr_audit
; /* user auditing data. */
5620 void *cr_label
; /* MACF label */
5621 int cr_flags
; /* flags on credential */
5623 typedef struct debug_ucred debug_ucred
;
5625 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5626 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5629 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5633 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5635 int i
, j
, counter
= 0;
5638 kauth_cred_t found_cred
;
5639 debug_ucred
* cred_listp
;
5640 debug_ucred
* nextp
;
5642 /* This is a readonly node. */
5643 if (req
->newptr
!= USER_ADDR_NULL
)
5646 /* calculate space needed */
5647 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5648 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5653 /* they are querying us so just return the space required. */
5654 if (req
->oldptr
== USER_ADDR_NULL
) {
5655 counter
+= 10; // add in some padding;
5656 req
->oldidx
= counter
* sizeof(debug_ucred
);
5660 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5661 if ( cred_listp
== NULL
) {
5665 /* fill in creds to send back */
5668 for (i
= 0; i
< KAUTH_CRED_TABLE_SIZE
; i
++) {
5669 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5670 nextp
->credp
= found_cred
;
5671 nextp
->cr_ref
= found_cred
->cr_ref
;
5672 nextp
->cr_uid
= found_cred
->cr_uid
;
5673 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5674 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5675 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5676 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5677 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5679 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5680 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5681 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5682 nextp
->cr_audit
.ai_auid
=
5683 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5684 nextp
->cr_audit
.ai_mask
.am_success
=
5685 found_cred
->cr_audit
.as_mask
.am_success
;
5686 nextp
->cr_audit
.ai_mask
.am_failure
=
5687 found_cred
->cr_audit
.as_mask
.am_failure
;
5688 nextp
->cr_audit
.ai_termid
.at_port
=
5689 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5690 nextp
->cr_audit
.ai_termid
.at_type
=
5691 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5692 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5693 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5694 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5695 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5696 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5697 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5698 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5699 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5700 nextp
->cr_audit
.ai_asid
=
5701 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5702 nextp
->cr_audit
.ai_flags
=
5703 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5704 nextp
->cr_label
= found_cred
->cr_label
;
5705 nextp
->cr_flags
= found_cred
->cr_flags
;
5707 space
+= sizeof(debug_ucred
);
5708 if ( space
> req
->oldlen
) {
5709 FREE(cred_listp
, M_TEMP
);
5714 req
->oldlen
= space
;
5715 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5716 FREE(cred_listp
, M_TEMP
);
5721 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5722 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5725 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5729 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5734 cred_debug_buffer
* bt_bufp
;
5735 cred_backtrace
* nextp
;
5737 /* This is a readonly node. */
5738 if (req
->newptr
!= USER_ADDR_NULL
)
5741 if ( cred_debug_buf_p
== NULL
) {
5745 /* calculate space needed */
5746 space
= sizeof( cred_debug_buf_p
->next_slot
);
5747 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5749 /* they are querying us so just return the space required. */
5750 if (req
->oldptr
== USER_ADDR_NULL
) {
5751 req
->oldidx
= space
;
5755 if ( space
> req
->oldlen
) {
5759 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
| M_ZERO
);
5760 if ( bt_bufp
== NULL
) {
5764 /* fill in backtrace info to send back */
5765 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5766 space
= sizeof(bt_bufp
->next_slot
);
5768 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5769 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5770 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5771 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5772 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5774 space
+= sizeof(*nextp
);
5777 req
->oldlen
= space
;
5778 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5779 FREE(bt_bufp
, M_TEMP
);
5783 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5787 **********************************************************************
5788 * The following routines will be moved to a policy_posix.c module at
5789 * some future point.
5790 **********************************************************************
5796 * Description: Helper function to create a kauth_cred_t credential that is
5797 * initally labelled with a specific POSIX credential label
5799 * Parameters: pcred The posix_cred_t to use as the initial
5802 * Returns: (kauth_cred_t) The credential that was found in the
5804 * NULL kauth_cred_add() failed, or there was
5805 * no egid specified, or we failed to
5806 * attach a label to the new credential
5808 * Notes: This function currently wraps kauth_cred_create(), and is the
5809 * only consumer of that ill-fated function, apart from bsd_init().
5810 * It exists solely to support the NFS server code creation of
5811 * credentials based on the over-the-wire RPC calls containing
5812 * traditional POSIX credential information being tunneled to
5813 * the server host from the client machine.
5815 * In the future, we hope this function goes away.
5817 * In the short term, it creates a temporary credential, puts
5818 * the POSIX information from NFS into it, and then calls
5819 * kauth_cred_create(), as an internal implementation detail.
5821 * If we have to keep it around in the medium term, it will
5822 * create a new kauth_cred_t, then label it with a POSIX label
5823 * corresponding to the contents of the kauth_cred_t. If the
5824 * policy_posix MACF module is not loaded, it will instead
5825 * substitute a posix_cred_t which GRANTS all access (effectively
5826 * a "root" credential) in order to not prevent NFS from working
5827 * in the case that we are not supporting POSIX credentials.
5830 posix_cred_create(posix_cred_t pcred
)
5832 struct ucred temp_cred
;
5834 bzero(&temp_cred
, sizeof(temp_cred
));
5835 temp_cred
.cr_posix
= *pcred
;
5837 return kauth_cred_create(&temp_cred
);
5844 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5845 * any, which is associated with it.
5847 * Parameters: cred The credential to obtain the label from
5849 * Returns: posix_cred_t The POSIX credential label
5851 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5852 * this function will return a pointer to a posix_cred_t which
5853 * GRANTS all access (effectively, a "root" credential). This is
5854 * necessary to support legacy code which insists on tightly
5855 * integrating POSIX credentials into its APIs, including, but
5856 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5857 * NFSv3, signals, dtrace, and a large number of kauth routines
5858 * used to implement POSIX permissions related system calls.
5860 * In the event that the policy_posix MACF module IS loaded, and
5861 * there is no POSIX label on the kauth_cred_t credential, this
5862 * function will return a pointer to a posix_cred_t which DENIES
5863 * all access (effectively, a "deny rights granted by POSIX"
5864 * credential). This is necessary to support the concept of a
5865 * transiently loaded POSIX policy, or kauth_cred_t credentials
5866 * which can not be used in conjunctions with POSIX permissions
5869 * This function currently returns the address of the cr_posix
5870 * field of the supplied kauth_cred_t credential, and as such
5871 * currently can not fail. In the future, this will not be the
5875 posix_cred_get(kauth_cred_t cred
)
5877 return(&cred
->cr_posix
);
5884 * Description: Label a kauth_cred_t with a POSIX credential label
5886 * Parameters: cred The credential to label
5887 * pcred The POSIX credential t label it with
5891 * Notes: This function is currently void in order to permit it to fit
5892 * in with the current MACF framework label methods which allow
5893 * labeling to fail silently. This is like acceptable for
5894 * mandatory access controls, but not for POSIX, since those
5895 * access controls are advisory. We will need to consider a
5896 * return value in a future version of the MACF API.
5898 * This operation currently cannot fail, as currently the POSIX
5899 * credential is a subfield of the kauth_cred_t (ucred), which
5900 * MUST be valid. In the future, this will not be the case.
5903 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5905 cred
->cr_posix
= *pcred
; /* structure assign for now */
5912 * Description: Perform a POSIX access check for a protected object
5914 * Parameters: cred The credential to check
5915 * object_uid The POSIX UID of the protected object
5916 * object_gid The POSIX GID of the protected object
5917 * object_mode The POSIX mode of the protected object
5918 * mode_req The requested POSIX access rights
5920 * Returns 0 Access is granted
5921 * EACCES Access is denied
5923 * Notes: This code optimizes the case where the world and group rights
5924 * would both grant the requested rights to avoid making a group
5925 * membership query. This is a big performance win in the case
5926 * where this is true.
5929 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5932 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5933 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5934 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5937 * Check first for owner rights
5939 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5943 * Combined group and world rights check, if we don't have owner rights
5945 * OPTIMIZED: If group and world rights would grant the same bits, and
5946 * they set of requested bits is in both, then we can simply check the
5947 * world rights, avoiding a group membership check, which is expensive.
5949 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5953 * NON-OPTIMIZED: requires group membership check.
5955 if ((mode_req
& mode_group
) != mode_req
) {
5957 * exclusion group : treat errors as "is a member"
5959 * NON-OPTIMIZED: +group would deny; must check group
5961 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5963 * DENY: +group denies
5967 if ((mode_req
& mode_world
) != mode_req
) {
5969 * DENY: both -group & world would deny
5974 * ALLOW: allowed by -group and +world
5981 * inclusion group; treat errors as "not a member"
5983 * NON-OPTIMIZED: +group allows, world denies; must
5986 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5988 * ALLOW: allowed by +group
5992 if ((mode_req
& mode_world
) != mode_req
) {
5994 * DENY: both -group & world would deny
5999 * ALLOW: allowed by -group and +world