2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
36 * Kernel Authorization framework: Management of process/thread credentials
37 * and identity information.
40 #include <sys/param.h> /* XXX trim includes */
42 #include <sys/systm.h>
43 #include <sys/ucred.h>
44 #include <sys/proc_internal.h>
46 #include <sys/timeb.h>
47 #include <sys/times.h>
48 #include <sys/malloc.h>
49 #include <sys/kauth.h>
50 #include <sys/kernel.h>
53 #include <security/audit/audit.h>
55 #include <sys/mount.h>
56 #include <sys/stat.h> /* For manifest constants in posix_cred_access */
57 #include <sys/sysproto.h>
58 #include <mach/message.h>
59 #include <mach/host_security.h>
61 #include <libkern/OSAtomic.h>
63 #include <kern/task.h>
64 #include <kern/lock.h>
68 #define MACH_ASSERT 1 /* XXX so bogus */
69 #include <kern/assert.h>
72 #include <security/mac.h>
73 #include <security/mac_framework.h>
74 #include <security/_label.h>
77 void mach_kauth_cred_uthread_update( void );
79 #define CRED_DIAGNOSTIC 0
81 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
84 * Credential debugging; we can track entry into a function that might
85 * change a credential, and we can track actual credential changes that
88 * Note: Does *NOT* currently include per-thread credential changes
92 #define DEBUG_CRED_ENTER printf
93 #define DEBUG_CRED_CHANGE printf
94 extern void kauth_cred_print(kauth_cred_t cred
);
96 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
98 int is_target_cred( kauth_cred_t the_cred
);
99 void get_backtrace( void );
101 static int sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
102 __unused
int arg2
, struct sysctl_req
*req
);
104 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
,
105 __unused
int arg2
, struct sysctl_req
*req
);
107 #define MAX_STACK_DEPTH 8
108 struct cred_backtrace
{
110 void * stack
[ MAX_STACK_DEPTH
];
112 typedef struct cred_backtrace cred_backtrace
;
114 #define MAX_CRED_BUFFER_SLOTS 200
115 struct cred_debug_buffer
{
117 cred_backtrace stack_buffer
[ MAX_CRED_BUFFER_SLOTS
];
119 typedef struct cred_debug_buffer cred_debug_buffer
;
120 cred_debug_buffer
* cred_debug_buf_p
= NULL
;
122 #else /* !DEBUG_CRED */
124 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
125 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
127 #endif /* !DEBUG_CRED */
129 #if CONFIG_EXT_RESOLVER
131 * Interface to external identity resolver.
133 * The architecture of the interface is simple; the external resolver calls
134 * in to get work, then calls back with completed work. It also calls us
135 * to let us know that it's (re)started, so that we can resubmit work if it
139 static lck_mtx_t
*kauth_resolver_mtx
;
140 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
141 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
143 static volatile pid_t kauth_resolver_identity
;
144 static int kauth_resolver_registered
;
145 static uint32_t kauth_resolver_sequence
;
146 static int kauth_resolver_timeout
= 30; /* default: 30 seconds */
148 struct kauth_resolver_work
{
149 TAILQ_ENTRY(kauth_resolver_work
) kr_link
;
150 struct kauth_identity_extlookup kr_work
;
155 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
156 #define KAUTH_REQUEST_SUBMITTED (1<<1)
157 #define KAUTH_REQUEST_DONE (1<<2)
161 TAILQ_HEAD(kauth_resolver_unsubmitted_head
, kauth_resolver_work
) kauth_resolver_unsubmitted
;
162 TAILQ_HEAD(kauth_resolver_submitted_head
, kauth_resolver_work
) kauth_resolver_submitted
;
163 TAILQ_HEAD(kauth_resolver_done_head
, kauth_resolver_work
) kauth_resolver_done
;
165 /* Number of resolver timeouts between logged complaints */
166 #define KAUTH_COMPLAINT_INTERVAL 1000
167 int kauth_resolver_timeout_cnt
= 0;
169 static int kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
);
170 static int kauth_resolver_complete(user_addr_t message
);
171 static int kauth_resolver_getwork(user_addr_t message
);
172 static int kauth_resolver_getwork2(user_addr_t message
);
174 #define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
176 struct kauth_identity
{
177 TAILQ_ENTRY(kauth_identity
) ki_link
;
183 const char *ki_name
; /* string name from string cache */
185 * Expiry times are the earliest time at which we will disregard the
186 * cached state and go to userland. Before then if the valid bit is
187 * set, we will return the cached value. If it's not set, we will
188 * not go to userland to resolve, just assume that there is no answer
191 time_t ki_guid_expiry
;
192 time_t ki_ntsid_expiry
;
195 static TAILQ_HEAD(kauth_identity_head
, kauth_identity
) kauth_identities
;
196 static lck_mtx_t
*kauth_identity_mtx
;
197 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
198 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
199 #define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
200 static int kauth_identity_cachemax
= KAUTH_IDENTITY_CACHEMAX_DEFAULT
;
201 static int kauth_identity_count
;
203 static struct kauth_identity
*kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
,
204 ntsid_t
*ntsidp
, time_t ntsid_expiry
, const char *name
, int nametype
);
205 static void kauth_identity_register_and_free(struct kauth_identity
*kip
);
206 static void kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*kip
, uint64_t extend_data
);
207 static void kauth_identity_trimcache(int newsize
);
208 static void kauth_identity_lru(struct kauth_identity
*kip
);
209 static int kauth_identity_guid_expired(struct kauth_identity
*kip
);
210 static int kauth_identity_ntsid_expired(struct kauth_identity
*kip
);
211 static int kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
);
212 static int kauth_identity_find_gid(gid_t gid
, struct kauth_identity
*kir
, char *getname
);
213 static int kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
);
214 static int kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
);
215 static int kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
);
217 struct kauth_group_membership
{
218 TAILQ_ENTRY(kauth_group_membership
) gm_link
;
219 uid_t gm_uid
; /* the identity whose membership we're recording */
220 gid_t gm_gid
; /* group of which they are a member */
221 time_t gm_expiry
; /* TTL for the membership, or 0 for persistent entries */
223 #define KAUTH_GROUP_ISMEMBER (1<<0)
226 TAILQ_HEAD(kauth_groups_head
, kauth_group_membership
) kauth_groups
;
227 static lck_mtx_t
*kauth_groups_mtx
;
228 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
229 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
230 #define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */
231 static int kauth_groups_cachemax
= KAUTH_GROUPS_CACHEMAX_DEFAULT
;
232 static int kauth_groups_count
;
234 static int kauth_groups_expired(struct kauth_group_membership
*gm
);
235 static void kauth_groups_lru(struct kauth_group_membership
*gm
);
236 static void kauth_groups_updatecache(struct kauth_identity_extlookup
*el
);
237 static void kauth_groups_trimcache(int newsize
);
239 #endif /* CONFIG_EXT_RESOLVER */
241 static const int kauth_cred_primes
[KAUTH_CRED_PRIMES_COUNT
] = KAUTH_CRED_PRIMES
;
242 static int kauth_cred_primes_index
= 0;
243 static int kauth_cred_table_size
= 0;
245 TAILQ_HEAD(kauth_cred_entry_head
, ucred
);
246 static struct kauth_cred_entry_head
* kauth_cred_table_anchor
= NULL
;
248 #define KAUTH_CRED_HASH_DEBUG 0
250 static int kauth_cred_add(kauth_cred_t new_cred
);
251 static void kauth_cred_remove(kauth_cred_t cred
);
252 static inline u_long
kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
);
253 static u_long
kauth_cred_get_hashkey(kauth_cred_t cred
);
254 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t new_cred
, boolean_t retain_auditinfo
);
255 static void kauth_cred_unref_hashlocked(kauth_cred_t
*credp
);
257 #if KAUTH_CRED_HASH_DEBUG
258 static int kauth_cred_count
= 0;
259 static void kauth_cred_hash_print(void);
260 static void kauth_cred_print(kauth_cred_t cred
);
263 #if CONFIG_EXT_RESOLVER
265 * kauth_resolver_init
267 * Description: Initialize the daemon side of the credential identity resolver
273 * Notes: Initialize the credential identity resolver for use; the
274 * credential identity resolver is the KPI used by the user
275 * space credential identity resolver daemon to communicate
276 * with the kernel via the identitysvc() system call..
278 * This is how membership in more than 16 groups (1 effective
279 * and 15 supplementary) is supported, and also how UID's,
280 * UUID's, and so on, are translated to/from POSIX credential
283 * The credential identity resolver operates by attempting to
284 * determine identity first from the credential, then from
285 * the kernel credential identity cache, and finally by
286 * enqueueing a request to a user space daemon.
288 * This function is called from kauth_init() in the file
289 * kern_authorization.c.
292 kauth_resolver_init(void)
294 TAILQ_INIT(&kauth_resolver_unsubmitted
);
295 TAILQ_INIT(&kauth_resolver_submitted
);
296 TAILQ_INIT(&kauth_resolver_done
);
297 kauth_resolver_sequence
= 31337;
298 kauth_resolver_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
303 * kauth_resolver_submit
305 * Description: Submit an external credential identity resolution request to
306 * the user space daemon.
308 * Parameters: lkp A pointer to an external
310 * extend_data extended data for kr_extend
313 * EWOULDBLOCK No resolver registered
314 * EINTR Operation interrupted (e.g. by
316 * ENOMEM Could not allocate work item
317 * copyinstr:EFAULT Bad message from user space
318 * workp->kr_result:??? An error from the user space
319 * daemon (includes ENOENT!)
324 * Notes: Allocate a work queue entry, submit the work and wait for
325 * the operation to either complete or time out. Outstanding
326 * operations may also be cancelled.
328 * Submission is by means of placing the item on a work queue
329 * which is serviced by an external resolver thread calling
330 * into the kernel. The caller then sleeps until timeout,
331 * cancellation, or an external resolver thread calls in with
332 * a result message to kauth_resolver_complete(). All of these
333 * events wake the caller back up.
335 * This code is called from either kauth_cred_ismember_gid()
336 * for a group membership request, or it is called from
337 * kauth_cred_cache_lookup() when we get a cache miss.
340 kauth_resolver_submit(struct kauth_identity_extlookup
*lkp
, uint64_t extend_data
)
342 struct kauth_resolver_work
*workp
, *killp
;
344 int error
, shouldfree
;
346 /* no point actually blocking if the resolver isn't up yet */
347 if (kauth_resolver_identity
== 0) {
349 * We've already waited an initial <kauth_resolver_timeout>
350 * seconds with no result.
352 * Sleep on a stack address so no one wakes us before timeout;
353 * we sleep a half a second in case we are a high priority
354 * process, so that memberd doesn't starve while we are in a
355 * tight loop between user and kernel, eating all the CPU.
357 error
= tsleep(&ts
, PZERO
| PCATCH
, "kr_submit", hz
/2);
358 if (kauth_resolver_identity
== 0) {
360 * if things haven't changed while we were asleep,
361 * tell the caller we couldn't get an authoritative
368 MALLOC(workp
, struct kauth_resolver_work
*, sizeof(*workp
), M_KAUTH
, M_WAITOK
);
372 workp
->kr_work
= *lkp
;
373 workp
->kr_extend
= extend_data
;
375 workp
->kr_flags
= KAUTH_REQUEST_UNSUBMITTED
;
376 workp
->kr_result
= 0;
379 * We insert the request onto the unsubmitted queue, the call in from
380 * the resolver will it to the submitted thread when appropriate.
382 KAUTH_RESOLVER_LOCK();
383 workp
->kr_seqno
= workp
->kr_work
.el_seqno
= kauth_resolver_sequence
++;
384 workp
->kr_work
.el_result
= KAUTH_EXTLOOKUP_INPROG
;
387 * XXX We *MUST NOT* attempt to coalesce identical work items due to
388 * XXX the inability to ensure order of update of the request item
389 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
390 * XXX for each item repeat the update when they wake up.
392 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted
, workp
, kr_link
);
395 * Wake up an external resolver thread to deal with the new work; one
396 * may not be available, and if not, then the request will be grabbed
397 * when a resolver thread comes back into the kernel to request new
400 wakeup_one((caddr_t
)&kauth_resolver_unsubmitted
);
402 /* we could compute a better timeout here */
403 ts
.tv_sec
= kauth_resolver_timeout
;
405 error
= msleep(workp
, kauth_resolver_mtx
, PCATCH
, "kr_submit", &ts
);
406 /* request has been completed? */
407 if ((error
== 0) && (workp
->kr_flags
& KAUTH_REQUEST_DONE
))
409 /* woken because the resolver has died? */
410 if (kauth_resolver_identity
== 0) {
419 /* if the request was processed, copy the result */
421 *lkp
= workp
->kr_work
;
423 if (error
== EWOULDBLOCK
) {
424 if ((kauth_resolver_timeout_cnt
++ % KAUTH_COMPLAINT_INTERVAL
) == 0) {
425 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
426 kauth_resolver_timeout_cnt
, kauth_resolver_timeout
);
429 if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
431 * If the request timed out and was never collected, the resolver
432 * is dead and probably not coming back anytime soon. In this
433 * case we revert to no-resolver behaviour, and punt all the other
434 * sleeping requests to clear the backlog.
436 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
439 * Make the current resolver non-authoritative, and mark it as
440 * no longer registered to prevent kauth_cred_ismember_gid()
441 * enqueueing more work until a new one is registered. This
442 * mitigates the damage a crashing resolver may inflict.
444 kauth_resolver_identity
= 0;
445 kauth_resolver_registered
= 0;
447 /* kill all the other requestes that are waiting as well */
448 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
450 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
452 /* Cause all waiting-for-work threads to return EIO */
453 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
458 * drop our reference on the work item, and note whether we should
461 if (--workp
->kr_refs
<= 0) {
462 /* work out which list we have to remove it from */
463 if (workp
->kr_flags
& KAUTH_REQUEST_DONE
) {
464 TAILQ_REMOVE(&kauth_resolver_done
, workp
, kr_link
);
465 } else if (workp
->kr_flags
& KAUTH_REQUEST_SUBMITTED
) {
466 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
467 } else if (workp
->kr_flags
& KAUTH_REQUEST_UNSUBMITTED
) {
468 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
470 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
474 /* someone else still has a reference on this request */
478 /* collect request result */
480 error
= workp
->kr_result
;
482 KAUTH_RESOLVER_UNLOCK();
485 * If we dropped the last reference, free the request.
488 FREE(workp
, M_KAUTH
);
491 KAUTH_DEBUG("RESOLVER - returning %d", error
);
499 * Description: System call interface for the external identity resolver.
501 * Parameters: uap->message Message from daemon to kernel
503 * Returns: 0 Successfully became resolver
504 * EPERM Not the resolver process
505 * kauth_authorize_generic:EPERM Not root user
506 * kauth_resolver_complete:EIO
507 * kauth_resolver_complete:EFAULT
508 * kauth_resolver_getwork:EINTR
509 * kauth_resolver_getwork:EFAULT
511 * Notes: This system call blocks until there is work enqueued, at
512 * which time the kernel wakes it up, and a message from the
513 * kernel is copied out to the identity resolution daemon, which
514 * proceed to attempt to resolve it. When the resolution has
515 * completed (successfully or not), the daemon called back into
516 * this system call to give the result to the kernel, and wait
517 * for the next request.
520 identitysvc(__unused
struct proc
*p
, struct identitysvc_args
*uap
, __unused
int32_t *retval
)
522 int opcode
= uap
->opcode
;
523 user_addr_t message
= uap
->message
;
524 struct kauth_resolver_work
*workp
;
525 struct kauth_cache_sizes sz_arg
;
530 * New server registering itself.
532 if (opcode
== KAUTH_EXTLOOKUP_REGISTER
) {
533 new_id
= current_proc()->p_pid
;
534 if ((error
= kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER
)) != 0) {
535 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id
);
538 KAUTH_RESOLVER_LOCK();
539 if (kauth_resolver_identity
!= new_id
) {
540 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id
, kauth_resolver_identity
);
542 * We have a new server, so assume that all the old requests have been lost.
544 while ((workp
= TAILQ_LAST(&kauth_resolver_submitted
, kauth_resolver_submitted_head
)) != NULL
) {
545 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
546 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
547 workp
->kr_flags
|= KAUTH_REQUEST_UNSUBMITTED
;
548 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted
, workp
, kr_link
);
551 * Allow user space resolver to override the
552 * external resolution timeout
554 if (message
> 30 && message
< 10000) {
555 kauth_resolver_timeout
= message
;
556 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message
);
558 kauth_resolver_identity
= new_id
;
559 kauth_resolver_registered
= 1;
560 wakeup(&kauth_resolver_unsubmitted
);
562 KAUTH_RESOLVER_UNLOCK();
567 * Beyond this point, we must be the resolver process.
569 if (current_proc()->p_pid
!= kauth_resolver_identity
) {
570 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid
);
574 if (opcode
== KAUTH_GET_CACHE_SIZES
) {
575 KAUTH_IDENTITY_LOCK();
576 sz_arg
.kcs_id_size
= kauth_identity_cachemax
;
577 KAUTH_IDENTITY_UNLOCK();
580 sz_arg
.kcs_group_size
= kauth_groups_cachemax
;
581 KAUTH_GROUPS_UNLOCK();
583 if ((error
= copyout(&sz_arg
, uap
->message
, sizeof (sz_arg
))) != 0) {
588 } else if (opcode
== KAUTH_SET_CACHE_SIZES
) {
589 if ((error
= copyin(uap
->message
, &sz_arg
, sizeof (sz_arg
))) != 0) {
593 if ((sz_arg
.kcs_group_size
> KAUTH_CACHES_MAX_SIZE
) ||
594 (sz_arg
.kcs_id_size
> KAUTH_CACHES_MAX_SIZE
)) {
598 KAUTH_IDENTITY_LOCK();
599 kauth_identity_cachemax
= sz_arg
.kcs_id_size
;
600 kauth_identity_trimcache(kauth_identity_cachemax
);
601 KAUTH_IDENTITY_UNLOCK();
604 kauth_groups_cachemax
= sz_arg
.kcs_group_size
;
605 kauth_groups_trimcache(kauth_groups_cachemax
);
606 KAUTH_GROUPS_UNLOCK();
609 } else if (opcode
== KAUTH_CLEAR_CACHES
) {
610 KAUTH_IDENTITY_LOCK();
611 kauth_identity_trimcache(0);
612 KAUTH_IDENTITY_UNLOCK();
615 kauth_groups_trimcache(0);
616 KAUTH_GROUPS_UNLOCK();
617 } else if (opcode
== KAUTH_EXTLOOKUP_DEREGISTER
) {
619 * Terminate outstanding requests; without an authoritative
620 * resolver, we are now back on our own authority.
622 struct kauth_resolver_work
*killp
;
624 KAUTH_RESOLVER_LOCK();
627 * Clear the identity, but also mark it as unregistered so
628 * there is no explicit future expectation of us getting a
629 * new resolver any time soon.
631 kauth_resolver_identity
= 0;
632 kauth_resolver_registered
= 0;
634 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
636 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
638 /* Cause all waiting-for-work threads to return EIO */
639 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
640 KAUTH_RESOLVER_UNLOCK();
644 * Got a result returning?
646 if (opcode
& KAUTH_EXTLOOKUP_RESULT
) {
647 if ((error
= kauth_resolver_complete(message
)) != 0)
652 * Caller wants to take more work?
654 if (opcode
& KAUTH_EXTLOOKUP_WORKER
) {
655 if ((error
= kauth_resolver_getwork(message
)) != 0)
664 * kauth_resolver_getwork_continue
666 * Description: Continuation for kauth_resolver_getwork
668 * Parameters: result Error code or 0 for the sleep
669 * that got us to this function
672 * EINTR Interrupted (e.g. by signal)
673 * kauth_resolver_getwork2:EFAULT
675 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
679 kauth_resolver_getwork_continue(int result
)
686 KAUTH_RESOLVER_UNLOCK();
691 * If we lost a race with another thread/memberd restarting, then we
692 * need to go back to sleep to look for more work. If it was memberd
693 * restarting, then the msleep0() will error out here, as our thread
694 * will already be "dead".
696 if (TAILQ_FIRST(&kauth_resolver_unsubmitted
) == NULL
) {
699 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
701 * If this is a wakeup from another thread in the resolver
702 * deregistering it, error out the request-for-work thread
704 if (!kauth_resolver_identity
)
706 KAUTH_RESOLVER_UNLOCK();
710 thread
= current_thread();
711 ut
= get_bsdthread_info(thread
);
712 message
= ut
->uu_kauth
.message
;
713 return(kauth_resolver_getwork2(message
));
718 * kauth_resolver_getwork2
720 * Decription: Common utility function to copy out a identity resolver work
721 * item from the kernel to user space as part of the user space
722 * identity resolver requesting work.
724 * Parameters: message message to user space
727 * EFAULT Bad user space message address
729 * Notes: This common function exists to permit the use of continuations
730 * in the identity resolution process. This frees up the stack
731 * while we are waiting for the user space resolver to complete
732 * a request. This is specifically used so that our per thread
733 * cost can be small, and we will therefore be willing to run a
734 * larger number of threads in the user space identity resolver.
737 kauth_resolver_getwork2(user_addr_t message
)
739 struct kauth_resolver_work
*workp
;
743 * Note: We depend on the caller protecting us from a NULL work item
744 * queue, since we must have the kauth resolver lock on entry to this
747 workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
);
750 * Copy out the external lookup structure for the request, not
751 * including the el_extend field, which contains the address of the
752 * external buffer provided by the external resolver into which we
753 * copy the extension request information.
756 if ((error
= copyout(&workp
->kr_work
, message
, offsetof(struct kauth_identity_extlookup
, el_extend
))) != 0) {
757 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
761 if ((error
= copyout(&workp
->kr_work
.el_info_reserved_1
,
762 message
+ offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
),
763 sizeof(struct kauth_identity_extlookup
) - offsetof(struct kauth_identity_extlookup
, el_info_reserved_1
))) != 0) {
764 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
769 * Handle extended requests here; if we have a request of a type where
770 * the kernel wants a translation of extended information, then we need
771 * to copy it out into the extended buffer, assuming the buffer is
772 * valid; we only attempt to get the buffer address if we have request
773 * data to copy into it.
777 * translate a user@domain string into a uid/gid/whatever
779 if (workp
->kr_work
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
782 error
= copyin(message
+ offsetof(struct kauth_identity_extlookup
, el_extend
), &uaddr
, sizeof(uaddr
));
784 size_t actual
; /* not used */
786 * Use copyoutstr() to reduce the copy size; we let
787 * this catch a NULL uaddr because we shouldn't be
788 * asking in that case anyway.
790 error
= copyoutstr(CAST_DOWN(void *,workp
->kr_extend
), uaddr
, MAXPATHLEN
, &actual
);
793 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
797 TAILQ_REMOVE(&kauth_resolver_unsubmitted
, workp
, kr_link
);
798 workp
->kr_flags
&= ~KAUTH_REQUEST_UNSUBMITTED
;
799 workp
->kr_flags
|= KAUTH_REQUEST_SUBMITTED
;
800 TAILQ_INSERT_TAIL(&kauth_resolver_submitted
, workp
, kr_link
);
803 KAUTH_RESOLVER_UNLOCK();
809 * kauth_resolver_getwork
811 * Description: Get a work item from the enqueued requests from the kernel and
812 * give it to the user space daemon.
814 * Parameters: message message to user space
817 * EINTR Interrupted (e.g. by signal)
818 * kauth_resolver_getwork2:EFAULT
820 * Notes: This function blocks in a continuation if there are no work
821 * items available for processing at the time the user space
822 * identity resolution daemon makes a request for work. This
823 * permits a large number of threads to be used by the daemon,
824 * without using a lot of wired kernel memory when there are no
825 * actual request outstanding.
828 kauth_resolver_getwork(user_addr_t message
)
830 struct kauth_resolver_work
*workp
;
833 KAUTH_RESOLVER_LOCK();
835 while ((workp
= TAILQ_FIRST(&kauth_resolver_unsubmitted
)) == NULL
) {
836 thread_t thread
= current_thread();
837 struct uthread
*ut
= get_bsdthread_info(thread
);
839 ut
->uu_kauth
.message
= message
;
840 error
= msleep0(&kauth_resolver_unsubmitted
, kauth_resolver_mtx
, PCATCH
, "GRGetWork", 0, kauth_resolver_getwork_continue
);
841 KAUTH_RESOLVER_UNLOCK();
843 * If this is a wakeup from another thread in the resolver
844 * deregistering it, error out the request-for-work thread
846 if (!kauth_resolver_identity
)
850 return kauth_resolver_getwork2(message
);
855 * kauth_resolver_complete
857 * Description: Return a result from userspace.
859 * Parameters: message message from user space
862 * EIO The resolver is dead
863 * copyin:EFAULT Bad message from user space
866 kauth_resolver_complete(user_addr_t message
)
868 struct kauth_identity_extlookup extl
;
869 struct kauth_resolver_work
*workp
;
870 struct kauth_resolver_work
*killp
;
874 * Copy in the mesage, including the extension field, since we are
875 * copying into a local variable.
877 if ((error
= copyin(message
, &extl
, sizeof(extl
))) != 0) {
878 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
882 KAUTH_RESOLVER_LOCK();
886 switch (extl
.el_result
) {
887 case KAUTH_EXTLOOKUP_INPROG
:
891 /* XXX this should go away once memberd is updated */
893 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
899 case KAUTH_EXTLOOKUP_SUCCESS
:
902 case KAUTH_EXTLOOKUP_FATAL
:
903 /* fatal error means the resolver is dead */
904 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity
);
906 * Terminate outstanding requests; without an authoritative
907 * resolver, we are now back on our own authority. Tag the
908 * resolver unregistered to prevent kauth_cred_ismember_gid()
909 * enqueueing more work until a new one is registered. This
910 * mitigates the damage a crashing resolver may inflict.
912 kauth_resolver_identity
= 0;
913 kauth_resolver_registered
= 0;
915 TAILQ_FOREACH(killp
, &kauth_resolver_submitted
, kr_link
)
917 TAILQ_FOREACH(killp
, &kauth_resolver_unsubmitted
, kr_link
)
919 /* Cause all waiting-for-work threads to return EIO */
920 wakeup((caddr_t
)&kauth_resolver_unsubmitted
);
921 /* and return EIO to the caller */
925 case KAUTH_EXTLOOKUP_BADRQ
:
926 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl
.el_seqno
);
930 case KAUTH_EXTLOOKUP_FAILURE
:
931 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl
.el_seqno
);
936 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl
.el_result
);
942 * In the case of a fatal error, we assume that the resolver will
943 * restart quickly and re-collect all of the outstanding requests.
944 * Thus, we don't complete the request which returned the fatal
947 if (extl
.el_result
!= KAUTH_EXTLOOKUP_FATAL
) {
948 /* scan our list for this request */
949 TAILQ_FOREACH(workp
, &kauth_resolver_submitted
, kr_link
) {
951 if (workp
->kr_seqno
== extl
.el_seqno
) {
954 * Get the request of the submitted queue so
955 * that it is not cleaned up out from under
958 TAILQ_REMOVE(&kauth_resolver_submitted
, workp
, kr_link
);
959 workp
->kr_flags
&= ~KAUTH_REQUEST_SUBMITTED
;
960 workp
->kr_flags
|= KAUTH_REQUEST_DONE
;
961 workp
->kr_result
= result
;
963 /* Copy the result message to the work item. */
964 memcpy(&workp
->kr_work
, &extl
, sizeof(struct kauth_identity_extlookup
));
967 * Check if we have a result in the extension
968 * field; if we do, then we need to separately
969 * copy the data from the message el_extend
970 * into the request buffer that's in the work
971 * item. We have to do it here because we do
972 * not want to wake up the waiter until the
973 * data is in their buffer, and because the
974 * actual request response may be destroyed
975 * by the time the requester wakes up, and they
976 * do not have access to the user space buffer
979 * It is safe to drop and reacquire the lock
980 * here because we've already removed the item
981 * from the submission queue, but have not yet
982 * moved it to the completion queue. Note that
983 * near simultaneous requests may result in
984 * duplication of requests for items in this
985 * window. This should not be a performance
986 * issue and is easily detectable by comparing
987 * time to live on last response vs. time of
988 * next request in the resolver logs.
990 if (extl
.el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
|KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
991 size_t actual
; /* notused */
993 KAUTH_RESOLVER_UNLOCK();
994 error
= copyinstr(extl
.el_extend
, CAST_DOWN(void *, workp
->kr_extend
), MAXPATHLEN
, &actual
);
995 KAUTH_RESOLVER_LOCK();
999 * Move the completed work item to the
1000 * completion queue and wake up requester(s)
1002 TAILQ_INSERT_TAIL(&kauth_resolver_done
, workp
, kr_link
);
1009 * Note that it's OK for us not to find anything; if the request has
1010 * timed out the work record will be gone.
1012 KAUTH_RESOLVER_UNLOCK();
1016 #endif /* CONFIG_EXT_RESOLVER */
1023 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
1024 #define KI_VALID_GID (1<<1)
1025 #define KI_VALID_GUID (1<<2)
1026 #define KI_VALID_NTSID (1<<3)
1027 #define KI_VALID_PWNAM (1<<4) /* Used for translation */
1028 #define KI_VALID_GRNAM (1<<5) /* Used for translation */
1030 #if CONFIG_EXT_RESOLVER
1032 * kauth_identity_init
1034 * Description: Initialize the kernel side of the credential identity resolver
1036 * Parameters: (void)
1040 * Notes: Initialize the credential identity resolver for use; the
1041 * credential identity resolver is the KPI used to communicate
1042 * with a user space credential identity resolver daemon.
1044 * This function is called from kauth_init() in the file
1045 * kern_authorization.c.
1048 kauth_identity_init(void)
1050 TAILQ_INIT(&kauth_identities
);
1051 kauth_identity_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
1056 * kauth_identity_alloc
1058 * Description: Allocate and fill out a kauth_identity structure for
1059 * translation between {UID|GID}/GUID/NTSID
1063 * Returns: NULL Insufficient memory to satisfy
1065 * !NULL A pointer to the allocated
1066 * structure, filled in
1068 * Notes: It is illegal to translate between UID and GID; any given UUID
1069 * or NTSID can only refer to an NTSID or UUID (respectively),
1070 * and *either* a UID *or* a GID, but not both.
1072 static struct kauth_identity
*
1073 kauth_identity_alloc(uid_t uid
, gid_t gid
, guid_t
*guidp
, time_t guid_expiry
, ntsid_t
*ntsidp
, time_t ntsid_expiry
, const char *name
, int nametype
)
1075 struct kauth_identity
*kip
;
1077 /* get and fill in a new identity */
1078 MALLOC(kip
, struct kauth_identity
*, sizeof(*kip
), M_KAUTH
, M_WAITOK
| M_ZERO
);
1080 if (gid
!= KAUTH_GID_NONE
) {
1082 kip
->ki_valid
= KI_VALID_GID
;
1084 if (uid
!= KAUTH_UID_NONE
) {
1085 if (kip
->ki_valid
& KI_VALID_GID
)
1086 panic("can't allocate kauth identity with both uid and gid");
1088 kip
->ki_valid
= KI_VALID_UID
;
1090 if (guidp
!= NULL
) {
1091 kip
->ki_guid
= *guidp
;
1092 kip
->ki_valid
|= KI_VALID_GUID
;
1094 kip
->ki_guid_expiry
= guid_expiry
;
1095 if (ntsidp
!= NULL
) {
1096 kip
->ki_ntsid
= *ntsidp
;
1097 kip
->ki_valid
|= KI_VALID_NTSID
;
1099 kip
->ki_ntsid_expiry
= ntsid_expiry
;
1101 kip
->ki_name
= name
;
1102 kip
->ki_valid
|= nametype
;
1110 * kauth_identity_register_and_free
1112 * Description: Register an association between identity tokens. The passed
1113 * 'kip' is consumed by this function.
1115 * Parameters: kip Pointer to kauth_identity
1116 * structure to register
1120 * Notes: The memory pointer to by 'kip' is assumed to have been
1121 * previously allocated via kauth_identity_alloc().
1124 kauth_identity_register_and_free(struct kauth_identity
*kip
)
1126 struct kauth_identity
*ip
;
1129 * We search the cache for the UID listed in the incoming association.
1130 * If we already have an entry, the new information is merged.
1133 KAUTH_IDENTITY_LOCK();
1134 if (kip
->ki_valid
& KI_VALID_UID
) {
1135 if (kip
->ki_valid
& KI_VALID_GID
)
1136 panic("kauth_identity: can't insert record with both UID and GID as key");
1137 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1138 if ((ip
->ki_valid
& KI_VALID_UID
) && (ip
->ki_uid
== kip
->ki_uid
))
1140 } else if (kip
->ki_valid
& KI_VALID_GID
) {
1141 TAILQ_FOREACH(ip
, &kauth_identities
, ki_link
)
1142 if ((ip
->ki_valid
& KI_VALID_GID
) && (ip
->ki_gid
== kip
->ki_gid
))
1145 panic("kauth_identity: can't insert record without UID or GID as key");
1149 /* we already have an entry, merge/overwrite */
1150 if (kip
->ki_valid
& KI_VALID_GUID
) {
1151 ip
->ki_guid
= kip
->ki_guid
;
1152 ip
->ki_valid
|= KI_VALID_GUID
;
1154 ip
->ki_guid_expiry
= kip
->ki_guid_expiry
;
1155 if (kip
->ki_valid
& KI_VALID_NTSID
) {
1156 ip
->ki_ntsid
= kip
->ki_ntsid
;
1157 ip
->ki_valid
|= KI_VALID_NTSID
;
1159 ip
->ki_ntsid_expiry
= kip
->ki_ntsid_expiry
;
1160 /* a valid ki_name field overwrites the previous name field */
1161 if (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)) {
1162 /* if there's an old one, discard it */
1163 const char *oname
= NULL
;
1164 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1165 oname
= ip
->ki_name
;
1166 ip
->ki_name
= kip
->ki_name
;
1167 kip
->ki_name
= oname
;
1169 /* and discard the incoming entry */
1173 * if we don't have any information on this identity, add it;
1174 * if it pushes us over our limit, discard the oldest one.
1176 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1177 if (++kauth_identity_count
> kauth_identity_cachemax
) {
1178 ip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1179 TAILQ_REMOVE(&kauth_identities
, ip
, ki_link
);
1180 kauth_identity_count
--;
1183 KAUTH_IDENTITY_UNLOCK();
1184 /* have to drop lock before freeing expired entry (it may be in use) */
1186 /* if the ki_name field is used, clear it first */
1187 if (ip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
))
1188 vfs_removename(ip
->ki_name
);
1189 /* free the expired entry */
1196 * kauth_identity_updatecache
1198 * Description: Given a lookup result, add any associations that we don't
1199 * currently have; replace ones which have changed.
1201 * Parameters: elp External lookup result from
1202 * user space daemon to kernel
1203 * rkip pointer to returned kauth
1205 * extend_data Extended data (can vary)
1210 * *rkip Modified (if non-NULL)
1212 * Notes: For extended information requests, this code relies on the fact
1213 * that elp->el_flags is never used as an rvalue, and is only
1214 * ever bit-tested for valid lookup information we are willing
1217 * XXX: We may have to do the same in the case that extended data was
1218 * passed out to user space to ensure that the request string
1219 * gets cached; we may also be able to use the rkip as an
1220 * input to avoid this. The jury is still out.
1222 * XXX: This codes performance could be improved for multiple valid
1223 * results by combining the loop iteration in a single loop.
1226 kauth_identity_updatecache(struct kauth_identity_extlookup
*elp
, struct kauth_identity
*rkip
, uint64_t extend_data
)
1229 struct kauth_identity
*kip
;
1230 const char *speculative_name
= NULL
;
1235 * If there is extended data, and that data represents a name rather
1236 * than something else, speculatively create an entry for it in the
1237 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK
1238 * over the allocation later.
1240 if (elp
->el_flags
& (KAUTH_EXTLOOKUP_VALID_PWNAM
| KAUTH_EXTLOOKUP_VALID_GRNAM
)) {
1241 const char *tmp
= CAST_DOWN(const char *,extend_data
);
1242 speculative_name
= vfs_addname(tmp
, strnlen(tmp
, MAXPATHLEN
- 1), 0, 0);
1245 /* user identity? */
1246 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
) {
1247 KAUTH_IDENTITY_LOCK();
1248 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1249 /* matching record */
1250 if ((kip
->ki_valid
& KI_VALID_UID
) && (kip
->ki_uid
== elp
->el_uid
)) {
1251 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) {
1252 kip
->ki_guid
= elp
->el_uguid
;
1253 kip
->ki_valid
|= KI_VALID_GUID
;
1255 kip
->ki_guid_expiry
= (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0;
1256 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) {
1257 kip
->ki_ntsid
= elp
->el_usid
;
1258 kip
->ki_valid
|= KI_VALID_NTSID
;
1260 kip
->ki_ntsid_expiry
= (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0;
1261 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) {
1262 const char *oname
= kip
->ki_name
;
1263 kip
->ki_name
= speculative_name
;
1264 speculative_name
= NULL
;
1265 kip
->ki_valid
|= KI_VALID_PWNAM
;
1268 * free oname (if any) outside
1271 speculative_name
= oname
;
1274 kauth_identity_lru(kip
);
1277 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1281 KAUTH_IDENTITY_UNLOCK();
1282 /* not found in cache, add new record */
1284 kip
= kauth_identity_alloc(elp
->el_uid
, KAUTH_GID_NONE
,
1285 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UGUID
) ? &elp
->el_uguid
: NULL
,
1286 (elp
->el_uguid_valid
) ? tv
.tv_sec
+ elp
->el_uguid_valid
: 0,
1287 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_USID
) ? &elp
->el_usid
: NULL
,
1288 (elp
->el_usid_valid
) ? tv
.tv_sec
+ elp
->el_usid_valid
: 0,
1289 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
) ? speculative_name
: NULL
,
1294 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_PWNAM
)
1295 speculative_name
= NULL
;
1296 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1297 kauth_identity_register_and_free(kip
);
1302 /* group identity? (ignore, if we already processed it as a user) */
1303 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GID
&& !(elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_UID
)) {
1304 KAUTH_IDENTITY_LOCK();
1305 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1306 /* matching record */
1307 if ((kip
->ki_valid
& KI_VALID_GID
) && (kip
->ki_gid
== elp
->el_gid
)) {
1308 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) {
1309 kip
->ki_guid
= elp
->el_gguid
;
1310 kip
->ki_valid
|= KI_VALID_GUID
;
1312 kip
->ki_guid_expiry
= (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0;
1313 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) {
1314 kip
->ki_ntsid
= elp
->el_gsid
;
1315 kip
->ki_valid
|= KI_VALID_NTSID
;
1317 kip
->ki_ntsid_expiry
= (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0;
1318 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) {
1319 const char *oname
= kip
->ki_name
;
1320 kip
->ki_name
= speculative_name
;
1321 speculative_name
= NULL
;
1322 kip
->ki_valid
|= KI_VALID_GRNAM
;
1325 * free oname (if any) outside
1328 speculative_name
= oname
;
1331 kauth_identity_lru(kip
);
1334 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1338 KAUTH_IDENTITY_UNLOCK();
1339 /* not found in cache, add new record */
1341 kip
= kauth_identity_alloc(KAUTH_UID_NONE
, elp
->el_gid
,
1342 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GGUID
) ? &elp
->el_gguid
: NULL
,
1343 (elp
->el_gguid_valid
) ? tv
.tv_sec
+ elp
->el_gguid_valid
: 0,
1344 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GSID
) ? &elp
->el_gsid
: NULL
,
1345 (elp
->el_gsid_valid
) ? tv
.tv_sec
+ elp
->el_gsid_valid
: 0,
1346 (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
) ? speculative_name
: NULL
,
1351 if (elp
->el_flags
& KAUTH_EXTLOOKUP_VALID_GRNAM
)
1352 speculative_name
= NULL
;
1353 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT
, kip
->ki_uid
, K_UUID_ARG(kip
->ki_guid
));
1354 kauth_identity_register_and_free(kip
);
1359 /* If we have a name reference to drop, drop it here */
1360 if (speculative_name
!= NULL
) {
1361 vfs_removename(speculative_name
);
1367 * Trim older entries from the identity cache.
1369 * Must be called with the identity cache lock held.
1372 kauth_identity_trimcache(int newsize
) {
1373 struct kauth_identity
*kip
;
1375 lck_mtx_assert(kauth_identity_mtx
, LCK_MTX_ASSERT_OWNED
);
1377 while (kauth_identity_count
> newsize
) {
1378 kip
= TAILQ_LAST(&kauth_identities
, kauth_identity_head
);
1379 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1380 kauth_identity_count
--;
1386 * kauth_identity_lru
1388 * Description: Promote the entry to the head of the LRU, assumes the cache
1391 * Parameters: kip kauth identity to move to the
1392 * head of the LRU list, if it's
1397 * Notes: This is called even if the entry has expired; typically an
1398 * expired entry that's been looked up is about to be revalidated,
1399 * and having it closer to the head of the LRU means finding it
1400 * quickly again when the revalidation comes through.
1403 kauth_identity_lru(struct kauth_identity
*kip
)
1405 if (kip
!= TAILQ_FIRST(&kauth_identities
)) {
1406 TAILQ_REMOVE(&kauth_identities
, kip
, ki_link
);
1407 TAILQ_INSERT_HEAD(&kauth_identities
, kip
, ki_link
);
1413 * kauth_identity_guid_expired
1415 * Description: Handle lazy expiration of GUID translations.
1417 * Parameters: kip kauth identity to check for
1420 * Returns: 1 Expired
1424 kauth_identity_guid_expired(struct kauth_identity
*kip
)
1429 * Expiration time of 0 means this entry is persistent.
1431 if (kip
->ki_guid_expiry
== 0)
1435 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip
->ki_guid_expiry
, tv
.tv_sec
);
1437 return((kip
->ki_guid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1442 * kauth_identity_ntsid_expired
1444 * Description: Handle lazy expiration of NTSID translations.
1446 * Parameters: kip kauth identity to check for
1449 * Returns: 1 Expired
1453 kauth_identity_ntsid_expired(struct kauth_identity
*kip
)
1458 * Expiration time of 0 means this entry is persistent.
1460 if (kip
->ki_ntsid_expiry
== 0)
1464 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip
->ki_ntsid_expiry
, tv
.tv_sec
);
1466 return((kip
->ki_ntsid_expiry
<= tv
.tv_sec
) ? 1 : 0);
1471 * kauth_identity_find_uid
1473 * Description: Search for an entry by UID
1475 * Parameters: uid UID to find
1476 * kir Pointer to return area
1477 * getname Name buffer, if ki_name wanted
1483 * *klr Modified, if found
1486 kauth_identity_find_uid(uid_t uid
, struct kauth_identity
*kir
, char *getname
)
1488 struct kauth_identity
*kip
;
1490 KAUTH_IDENTITY_LOCK();
1491 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1492 if ((kip
->ki_valid
& KI_VALID_UID
) && (uid
== kip
->ki_uid
)) {
1493 kauth_identity_lru(kip
);
1494 /* Copy via structure assignment */
1496 /* If a name is wanted and one exists, copy it out */
1497 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1498 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1502 KAUTH_IDENTITY_UNLOCK();
1503 return((kip
== NULL
) ? ENOENT
: 0);
1508 * kauth_identity_find_gid
1510 * Description: Search for an entry by GID
1512 * Parameters: gid GID to find
1513 * kir Pointer to return area
1514 * getname Name buffer, if ki_name wanted
1520 * *klr Modified, if found
1523 kauth_identity_find_gid(uid_t gid
, struct kauth_identity
*kir
, char *getname
)
1525 struct kauth_identity
*kip
;
1527 KAUTH_IDENTITY_LOCK();
1528 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1529 if ((kip
->ki_valid
& KI_VALID_GID
) && (gid
== kip
->ki_gid
)) {
1530 kauth_identity_lru(kip
);
1531 /* Copy via structure assignment */
1533 /* If a name is wanted and one exists, copy it out */
1534 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1535 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1539 KAUTH_IDENTITY_UNLOCK();
1540 return((kip
== NULL
) ? ENOENT
: 0);
1545 * kauth_identity_find_guid
1547 * Description: Search for an entry by GUID
1549 * Parameters: guidp Pointer to GUID to find
1550 * kir Pointer to return area
1551 * getname Name buffer, if ki_name wanted
1557 * *klr Modified, if found
1559 * Note: The association may be expired, in which case the caller
1560 * may elect to call out to userland to revalidate.
1563 kauth_identity_find_guid(guid_t
*guidp
, struct kauth_identity
*kir
, char *getname
)
1565 struct kauth_identity
*kip
;
1567 KAUTH_IDENTITY_LOCK();
1568 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1569 if ((kip
->ki_valid
& KI_VALID_GUID
) && (kauth_guid_equal(guidp
, &kip
->ki_guid
))) {
1570 kauth_identity_lru(kip
);
1571 /* Copy via structure assignment */
1573 /* If a name is wanted and one exists, copy it out */
1574 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1575 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1579 KAUTH_IDENTITY_UNLOCK();
1580 return((kip
== NULL
) ? ENOENT
: 0);
1584 * kauth_identity_find_nam
1586 * Description: Search for an entry by name
1588 * Parameters: name Pointer to name to find
1589 * valid KI_VALID_PWNAM or KI_VALID_GRNAM
1590 * kir Pointer to return area
1596 * *klr Modified, if found
1599 kauth_identity_find_nam(char *name
, int valid
, struct kauth_identity
*kir
)
1601 struct kauth_identity
*kip
;
1603 KAUTH_IDENTITY_LOCK();
1604 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1605 if ((kip
->ki_valid
& valid
) && !strcmp(name
, kip
->ki_name
)) {
1606 kauth_identity_lru(kip
);
1607 /* Copy via structure assignment */
1612 KAUTH_IDENTITY_UNLOCK();
1613 return((kip
== NULL
) ? ENOENT
: 0);
1618 * kauth_identity_find_ntsid
1620 * Description: Search for an entry by NTSID
1622 * Parameters: ntsid Pointer to NTSID to find
1623 * kir Pointer to return area
1624 * getname Name buffer, if ki_name wanted
1630 * *klr Modified, if found
1632 * Note: The association may be expired, in which case the caller
1633 * may elect to call out to userland to revalidate.
1636 kauth_identity_find_ntsid(ntsid_t
*ntsid
, struct kauth_identity
*kir
, char *getname
)
1638 struct kauth_identity
*kip
;
1640 KAUTH_IDENTITY_LOCK();
1641 TAILQ_FOREACH(kip
, &kauth_identities
, ki_link
) {
1642 if ((kip
->ki_valid
& KI_VALID_NTSID
) && (kauth_ntsid_equal(ntsid
, &kip
->ki_ntsid
))) {
1643 kauth_identity_lru(kip
);
1644 /* Copy via structure assignment */
1646 /* If a name is wanted and one exists, copy it out */
1647 if (getname
!= NULL
&& (kip
->ki_valid
& (KI_VALID_PWNAM
| KI_VALID_GRNAM
)))
1648 strlcpy(getname
, kip
->ki_name
, MAXPATHLEN
);
1652 KAUTH_IDENTITY_UNLOCK();
1653 return((kip
== NULL
) ? ENOENT
: 0);
1655 #endif /* CONFIG_EXT_RESOLVER */
1661 guid_t kauth_null_guid
;
1667 * Description: Determine the equality of two GUIDs
1669 * Parameters: guid1 Pointer to first GUID
1670 * guid2 Pointer to second GUID
1672 * Returns: 0 If GUIDs are unequal
1673 * !0 If GUIDs are equal
1676 kauth_guid_equal(guid_t
*guid1
, guid_t
*guid2
)
1678 return(bcmp(guid1
, guid2
, sizeof(*guid1
)) == 0);
1683 * kauth_wellknown_guid
1685 * Description: Determine if a GUID is a well-known GUID
1687 * Parameters: guid Pointer to GUID to check
1689 * Returns: KAUTH_WKG_NOT Not a well known GUID
1690 * KAUTH_WKG_EVERYBODY "Everybody"
1691 * KAUTH_WKG_NOBODY "Nobody"
1692 * KAUTH_WKG_OWNER "Other"
1693 * KAUTH_WKG_GROUP "Group"
1696 kauth_wellknown_guid(guid_t
*guid
)
1698 static char fingerprint
[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1701 * All WKGs begin with the same 12 bytes.
1703 if (bcmp((void *)guid
, fingerprint
, 12) == 0) {
1705 * The final 4 bytes are our code (in network byte order).
1707 code
= OSSwapHostToBigInt32(*(uint32_t *)&guid
->g_guid
[12]);
1710 return(KAUTH_WKG_EVERYBODY
);
1712 return(KAUTH_WKG_NOBODY
);
1714 return(KAUTH_WKG_OWNER
);
1716 return(KAUTH_WKG_GROUP
);
1719 return(KAUTH_WKG_NOT
);
1726 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1728 * Parameters: sid1 Pointer to first NTSID
1729 * sid2 Pointer to second NTSID
1731 * Returns: 0 If GUIDs are unequal
1732 * !0 If GUIDs are equal
1735 kauth_ntsid_equal(ntsid_t
*sid1
, ntsid_t
*sid2
)
1737 /* check sizes for equality, also sanity-check size while we're at it */
1738 if ((KAUTH_NTSID_SIZE(sid1
) == KAUTH_NTSID_SIZE(sid2
)) &&
1739 (KAUTH_NTSID_SIZE(sid1
) <= sizeof(*sid1
)) &&
1740 bcmp(sid1
, sid2
, KAUTH_NTSID_SIZE(sid1
)) == 0)
1749 * We support four tokens representing identity:
1750 * - Credential reference
1753 * - NT security identifier
1755 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1762 * kauth_cred_change_egid
1764 * Description: Set EGID by changing the first element of cr_groups for the
1765 * passed credential; if the new EGID exists in the list of
1766 * groups already, then rotate the old EGID into its position,
1767 * otherwise replace it
1769 * Parameters: cred Pointer to the credential to modify
1770 * new_egid The new EGID to set
1772 * Returns: 0 The egid did not displace a member of
1773 * the supplementary group list
1774 * 1 The egid being set displaced a member
1775 * of the supplementary groups list
1777 * Note: Utility function; internal use only because of locking.
1779 * This function operates on the credential passed; the caller
1780 * must operate either on a newly allocated credential (one for
1781 * which there is no hash cache reference and no externally
1782 * visible pointer reference), or a template credential.
1785 kauth_cred_change_egid(kauth_cred_t cred
, gid_t new_egid
)
1791 #endif /* radar_4600026 */
1792 gid_t old_egid
= kauth_cred_getgid(cred
);
1793 posix_cred_t pcred
= posix_cred_get(cred
);
1795 /* Ignoring the first entry, scan for a match for the new egid */
1796 for (i
= 1; i
< pcred
->cr_ngroups
; i
++) {
1798 * If we find a match, swap them so we don't lose overall
1801 if (pcred
->cr_groups
[i
] == new_egid
) {
1802 pcred
->cr_groups
[i
] = old_egid
;
1803 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1810 #error Fix radar 4600026 first!!!
1813 This is correct for memberd behaviour, but incorrect for POSIX; to address
1814 this, we would need to automatically opt-out any SUID/SGID binary, and force
1815 it to use initgroups to opt back in. We take the approach of considering it
1816 opt'ed out in any group of 16 displacement instead, since it's a much more
1817 conservative approach (i.e. less likely to cause things to break).
1821 * If we displaced a member of the supplementary groups list of the
1822 * credential, and we have not opted out of memberd, then if memberd
1823 * says that the credential is a member of the group, then it has not
1824 * actually been displaced.
1826 * NB: This is typically a cold code path.
1828 if (displaced
&& !(pcred
->cr_flags
& CRF_NOMEMBERD
) &&
1829 kauth_cred_ismember_gid(cred
, new_egid
, &is_member
) == 0 &&
1832 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1834 #endif /* radar_4600026 */
1836 /* set the new EGID into the old spot */
1837 pcred
->cr_groups
[0] = new_egid
;
1846 * Description: Fetch UID from credential
1848 * Parameters: cred Credential to examine
1850 * Returns: (uid_t) UID associated with credential
1853 kauth_cred_getuid(kauth_cred_t cred
)
1855 NULLCRED_CHECK(cred
);
1856 return(posix_cred_get(cred
)->cr_uid
);
1861 * kauth_cred_getruid
1863 * Description: Fetch RUID from credential
1865 * Parameters: cred Credential to examine
1867 * Returns: (uid_t) RUID associated with credential
1870 kauth_cred_getruid(kauth_cred_t cred
)
1872 NULLCRED_CHECK(cred
);
1873 return(posix_cred_get(cred
)->cr_ruid
);
1878 * kauth_cred_getsvuid
1880 * Description: Fetch SVUID from credential
1882 * Parameters: cred Credential to examine
1884 * Returns: (uid_t) SVUID associated with credential
1887 kauth_cred_getsvuid(kauth_cred_t cred
)
1889 NULLCRED_CHECK(cred
);
1890 return(posix_cred_get(cred
)->cr_svuid
);
1897 * Description: Fetch GID from credential
1899 * Parameters: cred Credential to examine
1901 * Returns: (gid_t) GID associated with credential
1904 kauth_cred_getgid(kauth_cred_t cred
)
1906 NULLCRED_CHECK(cred
);
1907 return(posix_cred_get(cred
)->cr_gid
);
1912 * kauth_cred_getrgid
1914 * Description: Fetch RGID from credential
1916 * Parameters: cred Credential to examine
1918 * Returns: (gid_t) RGID associated with credential
1921 kauth_cred_getrgid(kauth_cred_t cred
)
1923 NULLCRED_CHECK(cred
);
1924 return(posix_cred_get(cred
)->cr_rgid
);
1929 * kauth_cred_getsvgid
1931 * Description: Fetch SVGID from credential
1933 * Parameters: cred Credential to examine
1935 * Returns: (gid_t) SVGID associated with credential
1938 kauth_cred_getsvgid(kauth_cred_t cred
)
1940 NULLCRED_CHECK(cred
);
1941 return(posix_cred_get(cred
)->cr_svgid
);
1945 static int kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
);
1947 #if CONFIG_EXT_RESOLVER == 0
1949 * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
1952 kauth_cred_cache_lookup(__unused
int from
, __unused
int to
,
1953 __unused
void *src
, __unused
void *dst
)
1955 return (EWOULDBLOCK
);
1961 * kauth_cred_guid2pwnam
1963 * Description: Fetch PWNAM from GUID
1965 * Parameters: guidp Pointer to GUID to examine
1966 * pwnam Pointer to user@domain buffer
1968 * Returns: 0 Success
1969 * kauth_cred_cache_lookup:EINVAL
1972 * *pwnam Modified, if successful
1974 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
1977 kauth_cred_guid2pwnam(guid_t
*guidp
, char *pwnam
)
1979 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_PWNAM
, guidp
, pwnam
));
1984 * kauth_cred_guid2grnam
1986 * Description: Fetch GRNAM from GUID
1988 * Parameters: guidp Pointer to GUID to examine
1989 * grnam Pointer to group@domain buffer
1991 * Returns: 0 Success
1992 * kauth_cred_cache_lookup:EINVAL
1995 * *grnam Modified, if successful
1997 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2000 kauth_cred_guid2grnam(guid_t
*guidp
, char *grnam
)
2002 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GRNAM
, guidp
, grnam
));
2007 * kauth_cred_pwnam2guid
2009 * Description: Fetch PWNAM from GUID
2011 * Parameters: pwnam String containing user@domain
2012 * guidp Pointer to buffer for GUID
2014 * Returns: 0 Success
2015 * kauth_cred_cache_lookup:EINVAL
2018 * *guidp Modified, if successful
2020 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2021 * bytes in size, including the NUL termination of the string.
2024 kauth_cred_pwnam2guid(char *pwnam
, guid_t
*guidp
)
2026 return(kauth_cred_cache_lookup(KI_VALID_PWNAM
, KI_VALID_GUID
, pwnam
, guidp
));
2031 * kauth_cred_grnam2guid
2033 * Description: Fetch GRNAM from GUID
2035 * Parameters: grnam String containing group@domain
2036 * guidp Pointer to buffer for GUID
2038 * Returns: 0 Success
2039 * kauth_cred_cache_lookup:EINVAL
2042 * *guidp Modified, if successful
2044 * Notes: grnam should not point to a request larger than MAXPATHLEN
2045 * bytes in size, including the NUL termination of the string.
2048 kauth_cred_grnam2guid(char *grnam
, guid_t
*guidp
)
2050 return(kauth_cred_cache_lookup(KI_VALID_GRNAM
, KI_VALID_GUID
, grnam
, guidp
));
2055 * kauth_cred_guid2uid
2057 * Description: Fetch UID from GUID
2059 * Parameters: guidp Pointer to GUID to examine
2060 * uidp Pointer to buffer for UID
2062 * Returns: 0 Success
2063 * kauth_cred_cache_lookup:EINVAL
2066 * *uidp Modified, if successful
2069 kauth_cred_guid2uid(guid_t
*guidp
, uid_t
*uidp
)
2071 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_UID
, guidp
, uidp
));
2076 * kauth_cred_guid2gid
2078 * Description: Fetch GID from GUID
2080 * Parameters: guidp Pointer to GUID to examine
2081 * gidp Pointer to buffer for GID
2083 * Returns: 0 Success
2084 * kauth_cred_cache_lookup:EINVAL
2087 * *gidp Modified, if successful
2090 kauth_cred_guid2gid(guid_t
*guidp
, gid_t
*gidp
)
2092 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_GID
, guidp
, gidp
));
2097 * kauth_cred_ntsid2uid
2099 * Description: Fetch UID from NTSID
2101 * Parameters: sidp Pointer to NTSID to examine
2102 * uidp Pointer to buffer for UID
2104 * Returns: 0 Success
2105 * kauth_cred_cache_lookup:EINVAL
2108 * *uidp Modified, if successful
2111 kauth_cred_ntsid2uid(ntsid_t
*sidp
, uid_t
*uidp
)
2113 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_UID
, sidp
, uidp
));
2118 * kauth_cred_ntsid2gid
2120 * Description: Fetch GID from NTSID
2122 * Parameters: sidp Pointer to NTSID to examine
2123 * gidp Pointer to buffer for GID
2125 * Returns: 0 Success
2126 * kauth_cred_cache_lookup:EINVAL
2129 * *gidp Modified, if successful
2132 kauth_cred_ntsid2gid(ntsid_t
*sidp
, gid_t
*gidp
)
2134 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GID
, sidp
, gidp
));
2139 * kauth_cred_ntsid2guid
2141 * Description: Fetch GUID from NTSID
2143 * Parameters: sidp Pointer to NTSID to examine
2144 * guidp Pointer to buffer for GUID
2146 * Returns: 0 Success
2147 * kauth_cred_cache_lookup:EINVAL
2150 * *guidp Modified, if successful
2153 kauth_cred_ntsid2guid(ntsid_t
*sidp
, guid_t
*guidp
)
2155 return(kauth_cred_cache_lookup(KI_VALID_NTSID
, KI_VALID_GUID
, sidp
, guidp
));
2160 * kauth_cred_uid2guid
2162 * Description: Fetch GUID from UID
2164 * Parameters: uid UID to examine
2165 * guidp Pointer to buffer for GUID
2167 * Returns: 0 Success
2168 * kauth_cred_cache_lookup:EINVAL
2171 * *guidp Modified, if successful
2174 kauth_cred_uid2guid(uid_t uid
, guid_t
*guidp
)
2176 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_GUID
, &uid
, guidp
));
2181 * kauth_cred_getguid
2183 * Description: Fetch GUID from credential
2185 * Parameters: cred Credential to examine
2186 * guidp Pointer to buffer for GUID
2188 * Returns: 0 Success
2189 * kauth_cred_cache_lookup:EINVAL
2192 * *guidp Modified, if successful
2195 kauth_cred_getguid(kauth_cred_t cred
, guid_t
*guidp
)
2197 NULLCRED_CHECK(cred
);
2198 return(kauth_cred_uid2guid(kauth_cred_getuid(cred
), guidp
));
2203 * kauth_cred_getguid
2205 * Description: Fetch GUID from GID
2207 * Parameters: gid GID to examine
2208 * guidp Pointer to buffer for GUID
2210 * Returns: 0 Success
2211 * kauth_cred_cache_lookup:EINVAL
2214 * *guidp Modified, if successful
2217 kauth_cred_gid2guid(gid_t gid
, guid_t
*guidp
)
2219 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_GUID
, &gid
, guidp
));
2224 * kauth_cred_uid2ntsid
2226 * Description: Fetch NTSID from UID
2228 * Parameters: uid UID to examine
2229 * sidp Pointer to buffer for NTSID
2231 * Returns: 0 Success
2232 * kauth_cred_cache_lookup:EINVAL
2235 * *sidp Modified, if successful
2238 kauth_cred_uid2ntsid(uid_t uid
, ntsid_t
*sidp
)
2240 return(kauth_cred_cache_lookup(KI_VALID_UID
, KI_VALID_NTSID
, &uid
, sidp
));
2245 * kauth_cred_getntsid
2247 * Description: Fetch NTSID from credential
2249 * Parameters: cred Credential to examine
2250 * sidp Pointer to buffer for NTSID
2252 * Returns: 0 Success
2253 * kauth_cred_cache_lookup:EINVAL
2256 * *sidp Modified, if successful
2259 kauth_cred_getntsid(kauth_cred_t cred
, ntsid_t
*sidp
)
2261 NULLCRED_CHECK(cred
);
2262 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred
), sidp
));
2267 * kauth_cred_gid2ntsid
2269 * Description: Fetch NTSID from GID
2271 * Parameters: gid GID to examine
2272 * sidp Pointer to buffer for NTSID
2274 * Returns: 0 Success
2275 * kauth_cred_cache_lookup:EINVAL
2278 * *sidp Modified, if successful
2281 kauth_cred_gid2ntsid(gid_t gid
, ntsid_t
*sidp
)
2283 return(kauth_cred_cache_lookup(KI_VALID_GID
, KI_VALID_NTSID
, &gid
, sidp
));
2288 * kauth_cred_guid2ntsid
2290 * Description: Fetch NTSID from GUID
2292 * Parameters: guidp Pointer to GUID to examine
2293 * sidp Pointer to buffer for NTSID
2295 * Returns: 0 Success
2296 * kauth_cred_cache_lookup:EINVAL
2299 * *sidp Modified, if successful
2302 kauth_cred_guid2ntsid(guid_t
*guidp
, ntsid_t
*sidp
)
2304 return(kauth_cred_cache_lookup(KI_VALID_GUID
, KI_VALID_NTSID
, guidp
, sidp
));
2309 * kauth_cred_cache_lookup
2311 * Description: Lookup a translation in the cache; if one is not found, and
2312 * the attempt was not fatal, submit the request to the resolver
2313 * instead, and wait for it to complete or be aborted.
2315 * Parameters: from Identity information we have
2316 * to Identity information we want
2317 * src Pointer to buffer containing
2318 * the source identity
2319 * dst Pointer to buffer to receive
2320 * the target identity
2322 * Returns: 0 Success
2323 * EINVAL Unknown source identity type
2325 #if CONFIG_EXT_RESOLVER
2327 kauth_cred_cache_lookup(int from
, int to
, void *src
, void *dst
)
2329 struct kauth_identity ki
;
2330 struct kauth_identity_extlookup el
;
2332 uint64_t extend_data
= 0ULL;
2333 int (* expired
)(struct kauth_identity
*kip
);
2334 char *namebuf
= NULL
;
2336 KAUTH_DEBUG("CACHE - translate %d to %d", from
, to
);
2339 * Look for an existing cache entry for this association.
2340 * If the entry has not expired, return the cached information.
2341 * We do not cache user@domain translations here; they use too
2342 * much memory to hold onto forever, and can not be updated
2345 if (to
== KI_VALID_PWNAM
|| to
== KI_VALID_GRNAM
) {
2351 error
= kauth_identity_find_uid(*(uid_t
*)src
, &ki
, namebuf
);
2354 error
= kauth_identity_find_gid(*(gid_t
*)src
, &ki
, namebuf
);
2357 error
= kauth_identity_find_guid((guid_t
*)src
, &ki
, namebuf
);
2359 case KI_VALID_NTSID
:
2360 error
= kauth_identity_find_ntsid((ntsid_t
*)src
, &ki
, namebuf
);
2362 case KI_VALID_PWNAM
:
2363 case KI_VALID_GRNAM
:
2364 /* Names are unique in their 'from' space */
2365 error
= kauth_identity_find_nam((char *)src
, from
, &ki
);
2370 /* lookup failure or error */
2372 /* any other error is fatal */
2373 if (error
!= ENOENT
) {
2374 /* XXX bogus check - this is not possible */
2375 KAUTH_DEBUG("CACHE - cache search error %d", error
);
2379 /* found a valid cached entry, check expiry */
2382 expired
= kauth_identity_guid_expired
;
2384 case KI_VALID_NTSID
:
2385 expired
= kauth_identity_ntsid_expired
;
2390 expired
= kauth_identity_guid_expired
;
2392 case KI_VALID_NTSID
:
2393 expired
= kauth_identity_ntsid_expired
;
2401 * If no expiry function, or not expired, we have found
2405 if (!expired(&ki
)) {
2406 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2407 expired
= NULL
; /* must clear it is used as a flag */
2410 * We leave ki_valid set here; it contains a
2411 * translation but the TTL has expired. If we can't
2412 * get a result from the resolver, we will use it as
2413 * a better-than nothing alternative.
2416 KAUTH_DEBUG("CACHE - expired entry found");
2419 KAUTH_DEBUG("CACHE - no expiry function");
2423 /* do we have a translation? */
2424 if (ki
.ki_valid
& to
) {
2425 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki
.ki_valid
);
2426 DTRACE_PROC4(kauth__identity__cache__hit
, int, from
, int, to
, void *, src
, void *, dst
);
2430 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2431 * If we went looking for a translation from GUID or NTSID and
2432 * found a translation that wasn't for our desired type, then
2433 * don't bother calling the resolver. We know that this
2434 * GUID/NTSID can't translate to our desired type.
2438 case KI_VALID_NTSID
:
2441 if ((ki
.ki_valid
& KI_VALID_UID
)) {
2442 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_GID
);
2447 if ((ki
.ki_valid
& KI_VALID_GID
)) {
2448 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki
.ki_valid
, KI_VALID_UID
);
2460 * We failed to find a cache entry; call the resolver.
2462 * Note: We ask for as much non-extended data as we can get,
2463 * and only provide (or ask for) extended information if
2464 * we have a 'from' (or 'to') which requires it. This
2465 * way we don't pay for the extra transfer overhead for
2466 * data we don't need.
2468 bzero(&el
, sizeof(el
));
2469 el
.el_info_pid
= current_proc()->p_pid
;
2472 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
;
2473 el
.el_uid
= *(uid_t
*)src
;
2476 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GID
;
2477 el
.el_gid
= *(gid_t
*)src
;
2480 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UGUID
| KAUTH_EXTLOOKUP_VALID_GGUID
;
2481 el
.el_uguid
= *(guid_t
*)src
;
2482 el
.el_gguid
= *(guid_t
*)src
;
2484 case KI_VALID_NTSID
:
2485 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_USID
| KAUTH_EXTLOOKUP_VALID_GSID
;
2486 el
.el_usid
= *(ntsid_t
*)src
;
2487 el
.el_gsid
= *(ntsid_t
*)src
;
2489 case KI_VALID_PWNAM
:
2490 /* extra overhead */
2491 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2492 extend_data
= CAST_USER_ADDR_T(src
);
2494 case KI_VALID_GRNAM
:
2495 /* extra overhead */
2496 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2497 extend_data
= CAST_USER_ADDR_T(src
);
2503 * Here we ask for everything all at once, to avoid having to work
2504 * out what we really want now, or might want soon.
2506 * Asking for SID translations when we don't know we need them right
2507 * now is going to cause excess work to be done if we're connected
2508 * to a network that thinks it can translate them. This list needs
2509 * to get smaller/smarter.
2511 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_UID
| KAUTH_EXTLOOKUP_WANT_GID
|
2512 KAUTH_EXTLOOKUP_WANT_UGUID
| KAUTH_EXTLOOKUP_WANT_GGUID
|
2513 KAUTH_EXTLOOKUP_WANT_USID
| KAUTH_EXTLOOKUP_WANT_GSID
;
2514 if (to
== KI_VALID_PWNAM
) {
2515 /* extra overhead */
2516 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_PWNAM
;
2517 extend_data
= CAST_USER_ADDR_T(dst
);
2519 if (to
== KI_VALID_GRNAM
) {
2520 /* extra overhead */
2521 el
.el_flags
|= KAUTH_EXTLOOKUP_WANT_GRNAM
;
2522 extend_data
= CAST_USER_ADDR_T(dst
);
2526 KAUTH_DEBUG("CACHE - calling resolver for %x", el
.el_flags
);
2528 DTRACE_PROC3(kauth__id__resolver__submitted
, int, from
, int, to
, uintptr_t, src
);
2530 error
= kauth_resolver_submit(&el
, extend_data
);
2532 DTRACE_PROC2(kauth__id__resolver__returned
, int, error
, struct kauth_identity_extlookup
*, &el
)
2534 KAUTH_DEBUG("CACHE - resolver returned %d", error
);
2536 /* was the external lookup successful? */
2539 * Save the results from the lookup - we may have other
2540 * information, even if we didn't get a guid or the
2543 * If we came from a name, we know the extend_data is valid.
2545 if (from
== KI_VALID_PWNAM
)
2546 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_PWNAM
;
2547 else if (from
== KI_VALID_GRNAM
)
2548 el
.el_flags
|= KAUTH_EXTLOOKUP_VALID_GRNAM
;
2550 kauth_identity_updatecache(&el
, &ki
, extend_data
);
2553 * Check to see if we have a valid cache entry
2554 * originating from the result.
2556 if (!(ki
.ki_valid
& to
)) {
2564 * Copy from the appropriate struct kauth_identity cache entry
2565 * structure into the destination buffer area.
2569 *(uid_t
*)dst
= ki
.ki_uid
;
2572 *(gid_t
*)dst
= ki
.ki_gid
;
2575 *(guid_t
*)dst
= ki
.ki_guid
;
2577 case KI_VALID_NTSID
:
2578 *(ntsid_t
*)dst
= ki
.ki_ntsid
;
2580 case KI_VALID_PWNAM
:
2581 case KI_VALID_GRNAM
:
2582 /* handled in kauth_resolver_complete() */
2587 KAUTH_DEBUG("CACHE - returned successfully");
2593 * Group membership cache.
2595 * XXX the linked-list implementation here needs to be optimized.
2601 * Description: Initialize the groups cache
2603 * Parameters: (void)
2607 * Notes: Initialize the groups cache for use; the group cache is used
2608 * to avoid unnecessary calls out to user space.
2610 * This function is called from kauth_init() in the file
2611 * kern_authorization.c.
2614 kauth_groups_init(void)
2616 TAILQ_INIT(&kauth_groups
);
2617 kauth_groups_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
2622 * kauth_groups_expired
2624 * Description: Handle lazy expiration of group membership cache entries
2626 * Parameters: gm group membership entry to
2627 * check for expiration
2629 * Returns: 1 Expired
2633 kauth_groups_expired(struct kauth_group_membership
*gm
)
2638 * Expiration time of 0 means this entry is persistent.
2640 if (gm
->gm_expiry
== 0)
2645 return((gm
->gm_expiry
<= tv
.tv_sec
) ? 1 : 0);
2652 * Description: Promote the entry to the head of the LRU, assumes the cache
2655 * Parameters: kip group membership entry to move
2656 * to the head of the LRU list,
2657 * if it's not already there
2661 * Notes: This is called even if the entry has expired; typically an
2662 * expired entry that's been looked up is about to be revalidated,
2663 * and having it closer to the head of the LRU means finding it
2664 * quickly again when the revalidation comes through.
2667 kauth_groups_lru(struct kauth_group_membership
*gm
)
2669 if (gm
!= TAILQ_FIRST(&kauth_groups
)) {
2670 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2671 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2677 * kauth_groups_updatecache
2679 * Description: Given a lookup result, add any group cache associations that
2680 * we don't currently have.
2682 * Parameters: elp External lookup result from
2683 * user space daemon to kernel
2684 * rkip pointer to returned kauth
2690 kauth_groups_updatecache(struct kauth_identity_extlookup
*el
)
2692 struct kauth_group_membership
*gm
;
2695 /* need a valid response if we are to cache anything */
2697 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
)) !=
2698 (KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
))
2704 * Search for an existing record for this association before inserting
2705 * a new one; if we find one, update it instead of creating a new one
2707 KAUTH_GROUPS_LOCK();
2708 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2709 if ((el
->el_uid
== gm
->gm_uid
) &&
2710 (el
->el_gid
== gm
->gm_gid
)) {
2711 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2712 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2714 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2716 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2717 kauth_groups_lru(gm
);
2721 KAUTH_GROUPS_UNLOCK();
2723 /* if we found an entry to update, stop here */
2727 /* allocate a new record */
2728 MALLOC(gm
, struct kauth_group_membership
*, sizeof(*gm
), M_KAUTH
, M_WAITOK
);
2730 gm
->gm_uid
= el
->el_uid
;
2731 gm
->gm_gid
= el
->el_gid
;
2732 if (el
->el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) {
2733 gm
->gm_flags
|= KAUTH_GROUP_ISMEMBER
;
2735 gm
->gm_flags
&= ~KAUTH_GROUP_ISMEMBER
;
2737 gm
->gm_expiry
= (el
->el_member_valid
) ? el
->el_member_valid
+ tv
.tv_sec
: 0;
2741 * Insert the new entry. Note that it's possible to race ourselves
2742 * here and end up with duplicate entries in the list. Wasteful, but
2743 * harmless since the first into the list will never be looked up,
2744 * and thus will eventually just fall off the end.
2746 KAUTH_GROUPS_LOCK();
2747 TAILQ_INSERT_HEAD(&kauth_groups
, gm
, gm_link
);
2748 if (++kauth_groups_count
> kauth_groups_cachemax
) {
2749 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2750 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2751 kauth_groups_count
--;
2755 KAUTH_GROUPS_UNLOCK();
2757 /* free expired cache entry */
2763 * Trim older entries from the group membership cache.
2765 * Must be called with the group cache lock held.
2768 kauth_groups_trimcache(int new_size
) {
2769 struct kauth_group_membership
*gm
;
2771 lck_mtx_assert(kauth_groups_mtx
, LCK_MTX_ASSERT_OWNED
);
2773 while (kauth_groups_count
> new_size
) {
2774 gm
= TAILQ_LAST(&kauth_groups
, kauth_groups_head
);
2775 TAILQ_REMOVE(&kauth_groups
, gm
, gm_link
);
2776 kauth_groups_count
--;
2780 #endif /* CONFIG_EXT_RESOLVER */
2783 * Group membership KPI
2787 * kauth_cred_ismember_gid
2789 * Description: Given a credential and a GID, determine if the GID is a member
2790 * of one of the supplementary groups associated with the given
2793 * Parameters: cred Credential to check in
2794 * gid GID to check for membership
2795 * resultp Pointer to int to contain the
2796 * result of the call
2798 * Returns: 0 Success
2799 * ENOENT Could not perform lookup
2800 * kauth_resolver_submit:EWOULDBLOCK
2801 * kauth_resolver_submit:EINTR
2802 * kauth_resolver_submit:ENOMEM
2803 * kauth_resolver_submit:ENOENT User space daemon did not vend
2805 * kauth_resolver_submit:??? Unlikely error from user space
2808 * *resultp (modified) 1 Is member
2811 * Notes: This function guarantees not to modify resultp when returning
2814 * This function effectively checks the EGID as well, since the
2815 * EGID is cr_groups[0] as an implementation detail.
2818 kauth_cred_ismember_gid(kauth_cred_t cred
, gid_t gid
, int *resultp
)
2820 posix_cred_t pcred
= posix_cred_get(cred
);
2824 * Check the per-credential list of override groups.
2826 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2827 * the cache should be used for that case.
2829 for (i
= 0; i
< pcred
->cr_ngroups
; i
++) {
2830 if (gid
== pcred
->cr_groups
[i
]) {
2837 * If we don't have a UID for group membership checks, the in-cred list
2838 * was authoritative and we can stop here.
2840 if (pcred
->cr_gmuid
== KAUTH_UID_NONE
) {
2845 #if CONFIG_EXT_RESOLVER
2846 struct kauth_group_membership
*gm
;
2847 struct kauth_identity_extlookup el
;
2851 * If the resolver hasn't checked in yet, we are early in the boot
2852 * phase and the local group list is complete and authoritative.
2854 if (!kauth_resolver_registered
) {
2860 /* XXX check supplementary groups */
2861 /* XXX check whiteout groups */
2862 /* XXX nesting of supplementary/whiteout groups? */
2865 * Check the group cache.
2867 KAUTH_GROUPS_LOCK();
2868 TAILQ_FOREACH(gm
, &kauth_groups
, gm_link
) {
2869 if ((gm
->gm_uid
== pcred
->cr_gmuid
) && (gm
->gm_gid
== gid
) && !kauth_groups_expired(gm
)) {
2870 kauth_groups_lru(gm
);
2875 /* did we find a membership entry? */
2877 *resultp
= (gm
->gm_flags
& KAUTH_GROUP_ISMEMBER
) ? 1 : 0;
2878 KAUTH_GROUPS_UNLOCK();
2880 /* if we did, we can return now */
2882 DTRACE_PROC2(kauth__group__cache__hit
, int, pcred
->cr_gmuid
, int, gid
);
2886 /* nothing in the cache, need to go to userland */
2887 bzero(&el
, sizeof(el
));
2888 el
.el_info_pid
= current_proc()->p_pid
;
2889 el
.el_flags
= KAUTH_EXTLOOKUP_VALID_UID
| KAUTH_EXTLOOKUP_VALID_GID
| KAUTH_EXTLOOKUP_WANT_MEMBERSHIP
;
2890 el
.el_uid
= pcred
->cr_gmuid
;
2892 el
.el_member_valid
= 0; /* XXX set by resolver? */
2894 DTRACE_PROC2(kauth__group__resolver__submitted
, int, el
.el_uid
, int, el
.el_gid
);
2896 error
= kauth_resolver_submit(&el
, 0ULL);
2898 DTRACE_PROC2(kauth__group__resolver__returned
, int, error
, int, el
.el_flags
);
2902 /* save the results from the lookup */
2903 kauth_groups_updatecache(&el
);
2905 /* if we successfully ascertained membership, report */
2906 if (el
.el_flags
& KAUTH_EXTLOOKUP_VALID_MEMBERSHIP
) {
2907 *resultp
= (el
.el_flags
& KAUTH_EXTLOOKUP_ISMEMBER
) ? 1 : 0;
2919 * kauth_cred_ismember_guid
2921 * Description: Determine whether the supplied credential is a member of the
2922 * group nominated by GUID.
2924 * Parameters: cred Credential to check in
2925 * guidp Pointer to GUID whose group
2926 * we are testing for membership
2927 * resultp Pointer to int to contain the
2928 * result of the call
2930 * Returns: 0 Success
2931 * kauth_cred_guid2gid:EINVAL
2932 * kauth_cred_ismember_gid:ENOENT
2933 * kauth_resolver_submit:ENOENT User space daemon did not vend
2935 * kauth_cred_ismember_gid:EWOULDBLOCK
2936 * kauth_cred_ismember_gid:EINTR
2937 * kauth_cred_ismember_gid:ENOMEM
2938 * kauth_cred_ismember_gid:??? Unlikely error from user space
2941 * *resultp (modified) 1 Is member
2945 kauth_cred_ismember_guid(__unused kauth_cred_t cred
, guid_t
*guidp
, int *resultp
)
2949 switch (kauth_wellknown_guid(guidp
)) {
2950 case KAUTH_WKG_NOBODY
:
2953 case KAUTH_WKG_EVERYBODY
:
2957 #if CONFIG_EXT_RESOLVER
2959 struct kauth_identity ki
;
2963 * Grovel the identity cache looking for this GUID.
2964 * If we find it, and it is for a user record, return
2965 * false because it's not a group.
2967 * This is necessary because we don't have -ve caching
2968 * of group memberships, and we really want to avoid
2969 * calling out to the resolver if at all possible.
2971 * Because we're called by the ACL evaluator, and the
2972 * ACL evaluator is likely to encounter ACEs for users,
2973 * this is expected to be a common case.
2976 if ((error
= kauth_identity_find_guid(guidp
, &ki
, NULL
)) == 0 &&
2977 !kauth_identity_guid_expired(&ki
)) {
2978 if (ki
.ki_valid
& KI_VALID_GID
) {
2979 /* It's a group after all... */
2983 if (ki
.ki_valid
& KI_VALID_UID
) {
2988 #endif /* 6603280 */
2990 * Attempt to translate the GUID to a GID. Even if
2991 * this fails, we will have primed the cache if it is
2992 * a user record and we'll see it above the next time
2995 if ((error
= kauth_cred_guid2gid(guidp
, &gid
)) != 0) {
2997 * If we have no guid -> gid translation, it's not a group and
2998 * thus the cred can't be a member.
3000 if (error
== ENOENT
) {
3006 error
= kauth_cred_ismember_gid(cred
, gid
, resultp
);
3009 #else /* CONFIG_EXT_RESOLVER */
3011 #endif /* CONFIG_EXT_RESOLVER */
3018 * kauth_cred_gid_subset
3020 * Description: Given two credentials, determine if all GIDs associated with
3021 * the first are also associated with the second
3023 * Parameters: cred1 Credential to check for
3024 * cred2 Credential to check in
3025 * resultp Pointer to int to contain the
3026 * result of the call
3028 * Returns: 0 Success
3029 * non-zero See kauth_cred_ismember_gid for
3033 * *resultp (modified) 1 Is subset
3036 * Notes: This function guarantees not to modify resultp when returning
3040 kauth_cred_gid_subset(kauth_cred_t cred1
, kauth_cred_t cred2
, int *resultp
)
3042 int i
, err
, res
= 1;
3044 posix_cred_t pcred1
= posix_cred_get(cred1
);
3045 posix_cred_t pcred2
= posix_cred_get(cred2
);
3047 /* First, check the local list of groups */
3048 for (i
= 0; i
< pcred1
->cr_ngroups
; i
++) {
3049 gid
= pcred1
->cr_groups
[i
];
3050 if ((err
= kauth_cred_ismember_gid(cred2
, gid
, &res
)) != 0) {
3054 if (!res
&& gid
!= pcred2
->cr_rgid
&& gid
!= pcred2
->cr_svgid
) {
3060 /* Check real gid */
3061 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_rgid
, &res
)) != 0) {
3065 if (!res
&& pcred1
->cr_rgid
!= pcred2
->cr_rgid
&&
3066 pcred1
->cr_rgid
!= pcred2
->cr_svgid
) {
3071 /* Finally, check saved gid */
3072 if ((err
= kauth_cred_ismember_gid(cred2
, pcred1
->cr_svgid
, &res
)) != 0){
3076 if (!res
&& pcred1
->cr_svgid
!= pcred2
->cr_rgid
&&
3077 pcred1
->cr_svgid
!= pcred2
->cr_svgid
) {
3088 * kauth_cred_issuser
3090 * Description: Fast replacement for issuser()
3092 * Parameters: cred Credential to check for super
3095 * Returns: 0 Not super user
3098 * Notes: This function uses a magic number which is not a manifest
3099 * constant; this is bad practice.
3102 kauth_cred_issuser(kauth_cred_t cred
)
3104 return(kauth_cred_getuid(cred
) == 0);
3112 /* lock protecting credential hash table */
3113 static lck_mtx_t
*kauth_cred_hash_mtx
;
3114 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3115 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
3116 #if KAUTH_CRED_HASH_DEBUG
3117 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3118 #else /* !KAUTH_CRED_HASH_DEBUG */
3119 #define KAUTH_CRED_HASH_LOCK_ASSERT()
3120 #endif /* !KAUTH_CRED_HASH_DEBUG */
3126 * Description: Initialize the credential hash cache
3128 * Parameters: (void)
3132 * Notes: Intialize the credential hash cache for use; the credential
3133 * hash cache is used convert duplicate credentials into a
3134 * single reference counted credential in order to save wired
3135 * kernel memory. In practice, this generally means a desktop
3136 * system runs with a few tens of credentials, instead of one
3137 * per process, one per thread, one per vnode cache entry, and
3138 * so on. This generally results in savings of 200K or more
3139 * (potentially much more on server systems).
3141 * The hash cache internally has a reference on the credential
3142 * for itself as a means of avoiding a reclaim race for a
3143 * credential in the process of having it's last non-hash
3144 * reference released. This would otherwise result in the
3145 * possibility of a freed credential that was still in uses due
3146 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3148 * On final release, the hash reference is droped, and the
3149 * credential is freed back to the system.
3151 * This function is called from kauth_init() in the file
3152 * kern_authorization.c.
3155 kauth_cred_init(void)
3159 kauth_cred_hash_mtx
= lck_mtx_alloc_init(kauth_lck_grp
, 0/*LCK_ATTR_NULL*/);
3160 kauth_cred_table_size
= kauth_cred_primes
[kauth_cred_primes_index
];
3162 /*allocate credential hash table */
3163 MALLOC(kauth_cred_table_anchor
, struct kauth_cred_entry_head
*,
3164 (sizeof(struct kauth_cred_entry_head
) * kauth_cred_table_size
),
3165 M_KAUTH
, M_WAITOK
| M_ZERO
);
3166 if (kauth_cred_table_anchor
== NULL
)
3167 panic("startup: kauth_cred_init");
3168 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
3169 TAILQ_INIT(&kauth_cred_table_anchor
[i
]);
3177 * Description: Get the current thread's effective UID.
3179 * Parameters: (void)
3181 * Returns: (uid_t) The effective UID of the
3187 return(kauth_cred_getuid(kauth_cred_get()));
3194 * Description: Get the current thread's real UID.
3196 * Parameters: (void)
3198 * Returns: (uid_t) The real UID of the current
3204 return(kauth_cred_getruid(kauth_cred_get()));
3211 * Description: Get the current thread's effective GID.
3213 * Parameters: (void)
3215 * Returns: (gid_t) The effective GID of the
3221 return(kauth_cred_getgid(kauth_cred_get()));
3228 * Description: Get the current thread's real GID.
3230 * Parameters: (void)
3232 * Returns: (gid_t) The real GID of the current
3238 return(kauth_cred_getrgid(kauth_cred_get()));
3245 * Description: Returns a pointer to the current thread's credential
3247 * Parameters: (void)
3249 * Returns: (kauth_cred_t) Pointer to the current thread's
3252 * Notes: This function does not take a reference; because of this, the
3253 * caller MUST NOT do anything that would let the thread's
3254 * credential change while using the returned value, without
3255 * first explicitly taking their own reference.
3257 * If a caller intends to take a reference on the resulting
3258 * credential pointer from calling this function, it is strongly
3259 * recommended that the caller use kauth_cred_get_with_ref()
3260 * instead, to protect against any future changes to the cred
3261 * locking protocols; such changes could otherwise potentially
3262 * introduce race windows in the callers code.
3265 kauth_cred_get(void)
3268 struct uthread
*uthread
;
3270 uthread
= get_bsdthread_info(current_thread());
3272 if (uthread
== NULL
)
3273 panic("thread wants credential but has no BSD thread info");
3275 * We can lazy-bind credentials to threads, as long as their processes
3278 * XXX If we later inline this function, the code in this block
3279 * XXX should probably be called out in a function.
3281 if (uthread
->uu_ucred
== NOCRED
) {
3282 if ((p
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3283 panic("thread wants credential but has no BSD process");
3284 uthread
->uu_ucred
= kauth_cred_proc_ref(p
);
3286 return(uthread
->uu_ucred
);
3290 mach_kauth_cred_uthread_update(void)
3295 uthread
= get_bsdthread_info(current_thread());
3296 proc
= current_proc();
3298 kauth_cred_uthread_update(uthread
, proc
);
3302 * kauth_cred_uthread_update
3304 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3305 * late-bind the uthread cred to the proc cred.
3307 * Parameters: uthread_t The uthread to update
3308 * proc_t The process to update to
3312 * Notes: This code is common code called from system call or trap entry
3313 * in the case that the process thread may have been changed
3314 * since the last time the thread entered the kernel. It is
3315 * generally only called with the current uthread and process as
3319 kauth_cred_uthread_update(uthread_t uthread
, proc_t proc
)
3321 if (uthread
->uu_ucred
!= proc
->p_ucred
&&
3322 (uthread
->uu_flag
& UT_SETUID
) == 0) {
3323 kauth_cred_t old
= uthread
->uu_ucred
;
3324 uthread
->uu_ucred
= kauth_cred_proc_ref(proc
);
3325 if (IS_VALID_CRED(old
))
3326 kauth_cred_unref(&old
);
3332 * kauth_cred_get_with_ref
3334 * Description: Takes a reference on the current thread's credential, and then
3335 * returns a pointer to it to the caller.
3337 * Parameters: (void)
3339 * Returns: (kauth_cred_t) Pointer to the current thread's
3340 * newly referenced credential
3342 * Notes: This function takes a reference on the credential before
3343 * returning it to the caller.
3345 * It is the responsibility of the calling code to release this
3346 * reference when the credential is no longer in use.
3348 * Since the returned reference may be a persistent reference
3349 * (e.g. one cached in another data structure with a lifetime
3350 * longer than the calling function), this release may be delayed
3351 * until such time as the persistent reference is to be destroyed.
3352 * An example of this would be the per vnode credential cache used
3353 * to accelerate lookup operations.
3356 kauth_cred_get_with_ref(void)
3359 struct uthread
*uthread
;
3361 uthread
= get_bsdthread_info(current_thread());
3363 if (uthread
== NULL
)
3364 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__
);
3365 if ((procp
= (proc_t
) get_bsdtask_info(get_threadtask(current_thread()))) == NULL
)
3366 panic("%s - thread wants credential but has no BSD process", __FUNCTION__
);
3369 * We can lazy-bind credentials to threads, as long as their processes
3372 * XXX If we later inline this function, the code in this block
3373 * XXX should probably be called out in a function.
3375 if (uthread
->uu_ucred
== NOCRED
) {
3376 /* take reference for new cred in thread */
3377 uthread
->uu_ucred
= kauth_cred_proc_ref(procp
);
3379 /* take a reference for our caller */
3380 kauth_cred_ref(uthread
->uu_ucred
);
3381 return(uthread
->uu_ucred
);
3386 * kauth_cred_proc_ref
3388 * Description: Takes a reference on the current process's credential, and
3389 * then returns a pointer to it to the caller.
3391 * Parameters: procp Process whose credential we
3392 * intend to take a reference on
3394 * Returns: (kauth_cred_t) Pointer to the process's
3395 * newly referenced credential
3397 * Locks: PROC_LOCK is held before taking the reference and released
3398 * after the refeence is taken to protect the p_ucred field of
3399 * the process referred to by procp.
3401 * Notes: This function takes a reference on the credential before
3402 * returning it to the caller.
3404 * It is the responsibility of the calling code to release this
3405 * reference when the credential is no longer in use.
3407 * Since the returned reference may be a persistent reference
3408 * (e.g. one cached in another data structure with a lifetime
3409 * longer than the calling function), this release may be delayed
3410 * until such time as the persistent reference is to be destroyed.
3411 * An example of this would be the per vnode credential cache used
3412 * to accelerate lookup operations.
3415 kauth_cred_proc_ref(proc_t procp
)
3420 cred
= proc_ucred(procp
);
3421 kauth_cred_ref(cred
);
3430 * Description: Allocate a new credential
3432 * Parameters: (void)
3434 * Returns: !NULL Newly allocated credential
3435 * NULL Insufficient memory
3437 * Notes: The newly allocated credential is zero'ed as part of the
3438 * allocation process, with the exception of the reference
3439 * count, which is set to 1 to indicate a single reference
3440 * held by the caller.
3442 * Since newly allocated credentials have no external pointers
3443 * referencing them, prior to making them visible in an externally
3444 * visible pointer (e.g. by adding them to the credential hash
3445 * cache) is the only legal time in which an existing credential
3446 * can be safely iinitialized or modified directly.
3448 * After initialization, the caller is expected to call the
3449 * function kauth_cred_add() to add the credential to the hash
3450 * cache, after which time it's frozen and becomes publically
3453 * The release protocol depends on kauth_hash_add() being called
3454 * before kauth_cred_rele() (there is a diagnostic panic which
3455 * will trigger if this protocol is not observed).
3457 * XXX: This function really ought to be static, rather than being
3458 * exported as KPI, since a failure of kauth_cred_add() can only
3459 * be handled by an explicit free of the credential; such frees
3460 * depend on knowlegdge of the allocation method used, which is
3461 * permitted to change between kernel revisions.
3463 * XXX: In the insufficient resource case, this code panic's rather
3464 * than returning a NULL pointer; the code that calls this
3465 * function needs to be audited before this can be changed.
3468 kauth_cred_alloc(void)
3470 kauth_cred_t newcred
;
3472 MALLOC_ZONE(newcred
, kauth_cred_t
, sizeof(*newcred
), M_CRED
, M_WAITOK
);
3474 posix_cred_t newpcred
= posix_cred_get(newcred
);
3475 bzero(newcred
, sizeof(*newcred
));
3476 newcred
->cr_ref
= 1;
3477 newcred
->cr_audit
.as_aia_p
= audit_default_aia_p
;
3478 /* must do this, or cred has same group membership as uid 0 */
3479 newpcred
->cr_gmuid
= KAUTH_UID_NONE
;
3482 panic("kauth_cred_alloc: couldn't allocate credential");
3486 #if KAUTH_CRED_HASH_DEBUG
3491 mac_cred_label_init(newcred
);
3501 * Description: Look to see if we already have a known credential in the hash
3502 * cache; if one is found, bump the reference count and return
3503 * it. If there are no credentials that match the given
3504 * credential, then allocate a new credential.
3506 * Parameters: cred Template for credential to
3509 * Returns: (kauth_cred_t) The credential that was found
3510 * in the hash or created
3511 * NULL kauth_cred_add() failed, or
3512 * there was not an egid specified
3514 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3515 * maintain this field, we can't expect callers to know how it
3516 * needs to be set. Callers should be prepared for this field
3517 * to be overwritten.
3519 * XXX: This code will tight-loop if memory for a new credential is
3520 * persistently unavailable; this is perhaps not the wisest way
3521 * to handle this condition, but current callers do not expect
3525 kauth_cred_create(kauth_cred_t cred
)
3527 kauth_cred_t found_cred
, new_cred
= NULL
;
3528 posix_cred_t pcred
= posix_cred_get(cred
);
3531 KAUTH_CRED_HASH_LOCK_ASSERT();
3533 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3534 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3537 * If the template credential is not opting out of external
3538 * group membership resolution, then we need to check that
3539 * the UID we will be using is resolvable by the external
3540 * resolver. If it's not, then we opt it out anyway, since
3541 * all future external resolution requests will be failing
3542 * anyway, and potentially taking a long time to do it. We
3543 * use gid 0 because we always know it will exist and not
3544 * trigger additional lookups. This is OK, because we end up
3545 * precatching the information here as a result.
3547 if (!kauth_cred_ismember_gid(cred
, 0, &is_member
)) {
3549 * It's a recognized value; we don't really care about
3550 * the answer, so long as it's something the external
3551 * resolver could have vended.
3553 pcred
->cr_gmuid
= pcred
->cr_uid
;
3556 * It's not something the external resolver could
3557 * have vended, so we don't want to ask it more
3558 * questions about the credential in the future. This
3559 * speeds up future lookups, as long as the caller
3560 * caches results; otherwise, it the same recurring
3561 * cost. Since most credentials are used multiple
3562 * times, we still get some performance win from this.
3564 pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3565 pcred
->cr_flags
|= CRF_NOMEMBERD
;
3569 /* Caller *must* specify at least the egid in cr_groups[0] */
3570 if (pcred
->cr_ngroups
< 1)
3574 KAUTH_CRED_HASH_LOCK();
3575 found_cred
= kauth_cred_find(cred
);
3576 if (found_cred
!= NULL
) {
3578 * Found an existing credential so we'll bump
3579 * reference count and return
3581 kauth_cred_ref(found_cred
);
3582 KAUTH_CRED_HASH_UNLOCK();
3585 KAUTH_CRED_HASH_UNLOCK();
3588 * No existing credential found. Create one and add it to
3591 new_cred
= kauth_cred_alloc();
3592 if (new_cred
!= NULL
) {
3594 posix_cred_t new_pcred
= posix_cred_get(new_cred
);
3595 new_pcred
->cr_uid
= pcred
->cr_uid
;
3596 new_pcred
->cr_ruid
= pcred
->cr_ruid
;
3597 new_pcred
->cr_svuid
= pcred
->cr_svuid
;
3598 new_pcred
->cr_rgid
= pcred
->cr_rgid
;
3599 new_pcred
->cr_svgid
= pcred
->cr_svgid
;
3600 new_pcred
->cr_gmuid
= pcred
->cr_gmuid
;
3601 new_pcred
->cr_ngroups
= pcred
->cr_ngroups
;
3602 bcopy(&pcred
->cr_groups
[0], &new_pcred
->cr_groups
[0], sizeof(new_pcred
->cr_groups
));
3604 bcopy(&cred
->cr_audit
, &new_cred
->cr_audit
,
3605 sizeof(new_cred
->cr_audit
));
3607 new_pcred
->cr_flags
= pcred
->cr_flags
;
3609 KAUTH_CRED_HASH_LOCK();
3610 err
= kauth_cred_add(new_cred
);
3611 KAUTH_CRED_HASH_UNLOCK();
3613 /* Retry if kauth_cred_add returns non zero value */
3617 mac_cred_label_destroy(new_cred
);
3619 AUDIT_SESSION_UNREF(new_cred
);
3621 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
3631 * kauth_cred_setresuid
3633 * Description: Update the given credential using the UID arguments. The given
3634 * UIDs are used to set the effective UID, real UID, saved UID,
3635 * and GMUID (used for group membership checking).
3637 * Parameters: cred The original credential
3638 * ruid The new real UID
3639 * euid The new effective UID
3640 * svuid The new saved UID
3641 * gmuid KAUTH_UID_NONE -or- the new
3642 * group membership UID
3644 * Returns: (kauth_cred_t) The updated credential
3646 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3647 * setting, so if you don't want it to change, pass it the
3648 * previous value, explicitly.
3650 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3651 * if it returns a credential other than the one it is passed,
3652 * will have dropped the reference on the passed credential. All
3653 * callers should be aware of this, and treat this function as an
3654 * unref + ref, potentially on different credentials.
3656 * Because of this, the caller is expected to take its own
3657 * reference on the credential passed as the first parameter,
3658 * and be prepared to release the reference on the credential
3659 * that is returned to them, if it is not intended to be a
3660 * persistent reference.
3663 kauth_cred_setresuid(kauth_cred_t cred
, uid_t ruid
, uid_t euid
, uid_t svuid
, uid_t gmuid
)
3665 struct ucred temp_cred
;
3666 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3667 posix_cred_t pcred
= posix_cred_get(cred
);
3669 NULLCRED_CHECK(cred
);
3672 * We don't need to do anything if the UIDs we are changing are
3673 * already the same as the UIDs passed in
3675 if ((euid
== KAUTH_UID_NONE
|| pcred
->cr_uid
== euid
) &&
3676 (ruid
== KAUTH_UID_NONE
|| pcred
->cr_ruid
== ruid
) &&
3677 (svuid
== KAUTH_UID_NONE
|| pcred
->cr_svuid
== svuid
) &&
3678 (pcred
->cr_gmuid
== gmuid
)) {
3679 /* no change needed */
3684 * Look up in cred hash table to see if we have a matching credential
3685 * with the new values; this is done by calling kauth_cred_update().
3687 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3688 if (euid
!= KAUTH_UID_NONE
) {
3689 temp_pcred
->cr_uid
= euid
;
3691 if (ruid
!= KAUTH_UID_NONE
) {
3692 temp_pcred
->cr_ruid
= ruid
;
3694 if (svuid
!= KAUTH_UID_NONE
) {
3695 temp_pcred
->cr_svuid
= svuid
;
3699 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3700 * opt out of participation in external group resolution, unless we
3701 * unless we explicitly opt back in later.
3703 if ((temp_pcred
->cr_gmuid
= gmuid
) == KAUTH_UID_NONE
) {
3704 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3707 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3712 * kauth_cred_setresgid
3714 * Description: Update the given credential using the GID arguments. The given
3715 * GIDs are used to set the effective GID, real GID, and saved
3718 * Parameters: cred The original credential
3719 * rgid The new real GID
3720 * egid The new effective GID
3721 * svgid The new saved GID
3723 * Returns: (kauth_cred_t) The updated credential
3725 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3726 * if it returns a credential other than the one it is passed,
3727 * will have dropped the reference on the passed credential. All
3728 * callers should be aware of this, and treat this function as an
3729 * unref + ref, potentially on different credentials.
3731 * Because of this, the caller is expected to take its own
3732 * reference on the credential passed as the first parameter,
3733 * and be prepared to release the reference on the credential
3734 * that is returned to them, if it is not intended to be a
3735 * persistent reference.
3738 kauth_cred_setresgid(kauth_cred_t cred
, gid_t rgid
, gid_t egid
, gid_t svgid
)
3740 struct ucred temp_cred
;
3741 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3742 posix_cred_t pcred
= posix_cred_get(cred
);
3744 NULLCRED_CHECK(cred
);
3745 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred
, rgid
, egid
, svgid
);
3748 * We don't need to do anything if the given GID are already the
3749 * same as the GIDs in the credential.
3751 if (pcred
->cr_groups
[0] == egid
&&
3752 pcred
->cr_rgid
== rgid
&&
3753 pcred
->cr_svgid
== svgid
) {
3754 /* no change needed */
3759 * Look up in cred hash table to see if we have a matching credential
3760 * with the new values; this is done by calling kauth_cred_update().
3762 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3763 if (egid
!= KAUTH_GID_NONE
) {
3764 /* displacing a supplementary group opts us out of memberd */
3765 if (kauth_cred_change_egid(&temp_cred
, egid
)) {
3766 DEBUG_CRED_CHANGE("displaced!\n");
3767 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3768 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3770 DEBUG_CRED_CHANGE("not displaced\n");
3773 if (rgid
!= KAUTH_GID_NONE
) {
3774 temp_pcred
->cr_rgid
= rgid
;
3776 if (svgid
!= KAUTH_GID_NONE
) {
3777 temp_pcred
->cr_svgid
= svgid
;
3780 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3785 * Update the given credential with the given groups. We only allocate a new
3786 * credential when the given gid actually results in changes to the existing
3788 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3789 * which will be used for group membership checking.
3792 * kauth_cred_setgroups
3794 * Description: Update the given credential using the provide supplementary
3795 * group list and group membership UID
3797 * Parameters: cred The original credential
3798 * groups Pointer to gid_t array which
3799 * contains the new group list
3800 * groupcount The count of valid groups which
3801 * are contained in 'groups'
3802 * gmuid KAUTH_UID_NONE -or- the new
3803 * group membership UID
3805 * Returns: (kauth_cred_t) The updated credential
3807 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3808 * setting, so if you don't want it to change, pass it the
3809 * previous value, explicitly.
3811 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3812 * if it returns a credential other than the one it is passed,
3813 * will have dropped the reference on the passed credential. All
3814 * callers should be aware of this, and treat this function as an
3815 * unref + ref, potentially on different credentials.
3817 * Because of this, the caller is expected to take its own
3818 * reference on the credential passed as the first parameter,
3819 * and be prepared to release the reference on the credential
3820 * that is returned to them, if it is not intended to be a
3821 * persistent reference.
3823 * XXX: Changes are determined in ordinal order - if the caller passes
3824 * in the same groups list that is already present in the
3825 * credential, but the members are in a different order, even if
3826 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3827 * is considered a modification to the credential, and a new
3828 * credential is created.
3830 * This should perhaps be better optimized, but it is considered
3831 * to be the caller's problem.
3834 kauth_cred_setgroups(kauth_cred_t cred
, gid_t
*groups
, int groupcount
, uid_t gmuid
)
3837 struct ucred temp_cred
;
3838 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3841 NULLCRED_CHECK(cred
);
3843 pcred
= posix_cred_get(cred
);
3846 * We don't need to do anything if the given list of groups does not
3849 if ((pcred
->cr_gmuid
== gmuid
) && (pcred
->cr_ngroups
== groupcount
)) {
3850 for (i
= 0; i
< groupcount
; i
++) {
3851 if (pcred
->cr_groups
[i
] != groups
[i
])
3854 if (i
== groupcount
) {
3855 /* no change needed */
3861 * Look up in cred hash table to see if we have a matching credential
3862 * with new values. If we are setting or clearing the gmuid, then
3863 * update the cr_flags, since clearing it is sticky. This permits an
3864 * opt-out of memberd processing using setgroups(), and an opt-in
3865 * using initgroups(). This is required for POSIX conformance.
3867 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
3868 temp_pcred
->cr_ngroups
= groupcount
;
3869 bcopy(groups
, temp_pcred
->cr_groups
, sizeof(temp_pcred
->cr_groups
));
3870 temp_pcred
->cr_gmuid
= gmuid
;
3871 if (gmuid
== KAUTH_UID_NONE
)
3872 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3874 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3876 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3880 * XXX temporary, for NFS support until we can come up with a better
3881 * XXX enumeration/comparison mechanism
3883 * Notes: The return value exists to account for the possibility of a
3884 * kauth_cred_t without a POSIX label. This will be the case in
3885 * the future (see posix_cred_get() below, for more details).
3888 kauth_cred_getgroups(kauth_cred_t cred
, gid_t
*grouplist
, int *countp
)
3890 int limit
= NGROUPS
;
3893 * If they just want a copy of the groups list, they may not care
3894 * about the actual count. If they specify an input count, however,
3895 * treat it as an indicator of the buffer size available in grouplist,
3896 * and limit the returned list to that size.
3899 limit
= MIN(*countp
, cred
->cr_posix
.cr_ngroups
);
3903 memcpy(grouplist
, cred
->cr_posix
.cr_groups
, sizeof(gid_t
) * limit
);
3910 * kauth_cred_setuidgid
3912 * Description: Update the given credential using the UID and GID arguments.
3913 * The given UID is used to set the effective UID, real UID, and
3914 * saved UID. The given GID is used to set the effective GID,
3915 * real GID, and saved GID.
3917 * Parameters: cred The original credential
3918 * uid The new UID to use
3919 * gid The new GID to use
3921 * Returns: (kauth_cred_t) The updated credential
3923 * Notes: We set the gmuid to uid if the credential we are inheriting
3924 * from has not opted out of memberd participation; otherwise
3925 * we set it to KAUTH_UID_NONE
3927 * This code is only ever called from the per-thread credential
3928 * code path in the "set per thread credential" case; and in
3929 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3932 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3933 * if it returns a credential other than the one it is passed,
3934 * will have dropped the reference on the passed credential. All
3935 * callers should be aware of this, and treat this function as an
3936 * unref + ref, potentially on different credentials.
3938 * Because of this, the caller is expected to take its own
3939 * reference on the credential passed as the first parameter,
3940 * and be prepared to release the reference on the credential
3941 * that is returned to them, if it is not intended to be a
3942 * persistent reference.
3945 kauth_cred_setuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
3947 struct ucred temp_cred
;
3948 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
3951 NULLCRED_CHECK(cred
);
3953 pcred
= posix_cred_get(cred
);
3956 * We don't need to do anything if the effective, real and saved
3957 * user IDs are already the same as the user ID passed into us.
3959 if (pcred
->cr_uid
== uid
&& pcred
->cr_ruid
== uid
&& pcred
->cr_svuid
== uid
&&
3960 pcred
->cr_gid
== gid
&& pcred
->cr_rgid
== gid
&& pcred
->cr_svgid
== gid
) {
3961 /* no change needed */
3966 * Look up in cred hash table to see if we have a matching credential
3967 * with the new values.
3969 bzero(&temp_cred
, sizeof(temp_cred
));
3970 temp_pcred
->cr_uid
= uid
;
3971 temp_pcred
->cr_ruid
= uid
;
3972 temp_pcred
->cr_svuid
= uid
;
3973 temp_pcred
->cr_flags
= pcred
->cr_flags
;
3974 /* inherit the opt-out of memberd */
3975 if (pcred
->cr_flags
& CRF_NOMEMBERD
) {
3976 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3977 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3979 temp_pcred
->cr_gmuid
= uid
;
3980 temp_pcred
->cr_flags
&= ~CRF_NOMEMBERD
;
3982 temp_pcred
->cr_ngroups
= 1;
3983 /* displacing a supplementary group opts us out of memberd */
3984 if (kauth_cred_change_egid(&temp_cred
, gid
)) {
3985 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
3986 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
3988 temp_pcred
->cr_rgid
= gid
;
3989 temp_pcred
->cr_svgid
= gid
;
3991 temp_cred
.cr_label
= cred
->cr_label
;
3994 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
3999 * kauth_cred_setsvuidgid
4001 * Description: Function used by execve to set the saved uid and gid values
4002 * for suid/sgid programs
4004 * Parameters: cred The credential to update
4005 * uid The saved uid to set
4006 * gid The saved gid to set
4008 * Returns: (kauth_cred_t) The updated credential
4010 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4011 * if it returns a credential other than the one it is passed,
4012 * will have dropped the reference on the passed credential. All
4013 * callers should be aware of this, and treat this function as an
4014 * unref + ref, potentially on different credentials.
4016 * Because of this, the caller is expected to take its own
4017 * reference on the credential passed as the first parameter,
4018 * and be prepared to release the reference on the credential
4019 * that is returned to them, if it is not intended to be a
4020 * persistent reference.
4023 kauth_cred_setsvuidgid(kauth_cred_t cred
, uid_t uid
, gid_t gid
)
4025 struct ucred temp_cred
;
4026 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4029 NULLCRED_CHECK(cred
);
4031 pcred
= posix_cred_get(cred
);
4033 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred
, cred
->cr_svuid
, uid
, cred
->cr_svgid
, gid
);
4036 * We don't need to do anything if the effective, real and saved
4037 * uids are already the same as the uid provided. This check is
4038 * likely insufficient.
4040 if (pcred
->cr_svuid
== uid
&& pcred
->cr_svgid
== gid
) {
4041 /* no change needed */
4044 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4046 /* look up in cred hash table to see if we have a matching credential
4049 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4050 temp_pcred
->cr_svuid
= uid
;
4051 temp_pcred
->cr_svgid
= gid
;
4053 return(kauth_cred_update(cred
, &temp_cred
, TRUE
));
4058 * kauth_cred_setauditinfo
4060 * Description: Update the given credential using the given au_session_t.
4062 * Parameters: cred The original credential
4063 * auditinfo_p Pointer to ne audit information
4065 * Returns: (kauth_cred_t) The updated credential
4067 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4068 * if it returns a credential other than the one it is passed,
4069 * will have dropped the reference on the passed credential. All
4070 * callers should be aware of this, and treat this function as an
4071 * unref + ref, potentially on different credentials.
4073 * Because of this, the caller is expected to take its own
4074 * reference on the credential passed as the first parameter,
4075 * and be prepared to release the reference on the credential
4076 * that is returned to them, if it is not intended to be a
4077 * persistent reference.
4080 kauth_cred_setauditinfo(kauth_cred_t cred
, au_session_t
*auditinfo_p
)
4082 struct ucred temp_cred
;
4084 NULLCRED_CHECK(cred
);
4087 * We don't need to do anything if the audit info is already the
4088 * same as the audit info in the credential provided.
4090 if (bcmp(&cred
->cr_audit
, auditinfo_p
, sizeof(cred
->cr_audit
)) == 0) {
4091 /* no change needed */
4095 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4096 bcopy(auditinfo_p
, &temp_cred
.cr_audit
, sizeof(temp_cred
.cr_audit
));
4098 return(kauth_cred_update(cred
, &temp_cred
, FALSE
));
4103 * kauth_cred_label_update
4105 * Description: Update the MAC label associated with a credential
4107 * Parameters: cred The original credential
4108 * label The MAC label to set
4110 * Returns: (kauth_cred_t) The updated credential
4112 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4113 * if it returns a credential other than the one it is passed,
4114 * will have dropped the reference on the passed credential. All
4115 * callers should be aware of this, and treat this function as an
4116 * unref + ref, potentially on different credentials.
4118 * Because of this, the caller is expected to take its own
4119 * reference on the credential passed as the first parameter,
4120 * and be prepared to release the reference on the credential
4121 * that is returned to them, if it is not intended to be a
4122 * persistent reference.
4125 kauth_cred_label_update(kauth_cred_t cred
, struct label
*label
)
4127 kauth_cred_t newcred
;
4128 struct ucred temp_cred
;
4130 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4132 mac_cred_label_init(&temp_cred
);
4133 mac_cred_label_associate(cred
, &temp_cred
);
4134 mac_cred_label_update(&temp_cred
, label
);
4136 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4137 mac_cred_label_destroy(&temp_cred
);
4142 * kauth_cred_label_update_execve
4144 * Description: Update the MAC label associated with a credential as
4147 * Parameters: cred The original credential
4149 * scriptl The script MAC label
4150 * execl The executable MAC label
4151 * disjointp Pointer to flag to set if old
4152 * and returned credentials are
4155 * Returns: (kauth_cred_t) The updated credential
4158 * *disjointp Set to 1 for disjoint creds
4160 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4161 * if it returns a credential other than the one it is passed,
4162 * will have dropped the reference on the passed credential. All
4163 * callers should be aware of this, and treat this function as an
4164 * unref + ref, potentially on different credentials.
4166 * Because of this, the caller is expected to take its own
4167 * reference on the credential passed as the first parameter,
4168 * and be prepared to release the reference on the credential
4169 * that is returned to them, if it is not intended to be a
4170 * persistent reference.
4174 kauth_cred_label_update_execve(kauth_cred_t cred
, vfs_context_t ctx
,
4175 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
,
4178 kauth_cred_t newcred
;
4179 struct ucred temp_cred
;
4181 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4183 mac_cred_label_init(&temp_cred
);
4184 mac_cred_label_associate(cred
, &temp_cred
);
4185 *disjointp
= mac_cred_label_update_execve(ctx
, &temp_cred
,
4186 vp
, scriptl
, execl
);
4188 newcred
= kauth_cred_update(cred
, &temp_cred
, TRUE
);
4189 mac_cred_label_destroy(&temp_cred
);
4194 * kauth_proc_label_update
4196 * Description: Update the label inside the credential associated with the process.
4198 * Parameters: p The process to modify
4199 * label The label to place in the process credential
4201 * Notes: The credential associated with the process may change as a result
4202 * of this call. The caller should not assume the process reference to
4203 * the old credential still exists.
4205 int kauth_proc_label_update(struct proc
*p
, struct label
*label
)
4207 kauth_cred_t my_cred
, my_new_cred
;
4209 my_cred
= kauth_cred_proc_ref(p
);
4211 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred
);
4213 /* get current credential and take a reference while we muck with it */
4217 * Set the credential with new info. If there is no change,
4218 * we get back the same credential we passed in; if there is
4219 * a change, we drop the reference on the credential we
4220 * passed in. The subsequent compare is safe, because it is
4221 * a pointer compare rather than a contents compare.
4223 my_new_cred
= kauth_cred_label_update(my_cred
, label
);
4224 if (my_cred
!= my_new_cred
) {
4226 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
);
4230 * We need to protect for a race where another thread
4231 * also changed the credential after we took our
4232 * reference. If p_ucred has changed then we should
4233 * restart this again with the new cred.
4235 if (p
->p_ucred
!= my_cred
) {
4237 kauth_cred_unref(&my_new_cred
);
4238 my_cred
= kauth_cred_proc_ref(p
);
4242 p
->p_ucred
= my_new_cred
;
4243 /* update cred on proc */
4244 PROC_UPDATE_CREDS_ONPROC(p
);
4246 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4251 /* Drop old proc reference or our extra reference */
4252 kauth_cred_unref(&my_cred
);
4258 * kauth_proc_label_update_execve
4260 * Description: Update the label inside the credential associated with the
4261 * process as part of a transitioning execve. The label will
4262 * be updated by the policies as part of this processing, not
4263 * provided up front.
4265 * Parameters: p The process to modify
4266 * ctx The context of the exec
4267 * vp The vnode being exec'ed
4268 * scriptl The script MAC label
4269 * execl The executable MAC label
4271 * Returns: 0 Label update did not make credential
4273 * 1 Label update caused credential to be
4276 * Notes: The credential associated with the process WILL change as a
4277 * result of this call. The caller should not assume the process
4278 * reference to the old credential still exists.
4281 kauth_proc_label_update_execve(struct proc
*p
, vfs_context_t ctx
,
4282 struct vnode
*vp
, struct label
*scriptl
, struct label
*execl
)
4284 kauth_cred_t my_cred
, my_new_cred
;
4287 my_cred
= kauth_cred_proc_ref(p
);
4289 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred
);
4291 /* get current credential and take a reference while we muck with it */
4295 * Set the credential with new info. If there is no change,
4296 * we get back the same credential we passed in; if there is
4297 * a change, we drop the reference on the credential we
4298 * passed in. The subsequent compare is safe, because it is
4299 * a pointer compare rather than a contents compare.
4301 my_new_cred
= kauth_cred_label_update_execve(my_cred
, ctx
, vp
, scriptl
, execl
, &disjoint
);
4302 if (my_cred
!= my_new_cred
) {
4304 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
);
4308 * We need to protect for a race where another thread
4309 * also changed the credential after we took our
4310 * reference. If p_ucred has changed then we should
4311 * restart this again with the new cred.
4313 if (p
->p_ucred
!= my_cred
) {
4315 kauth_cred_unref(&my_new_cred
);
4316 my_cred
= kauth_cred_proc_ref(p
);
4320 p
->p_ucred
= my_new_cred
;
4321 /* update cred on proc */
4322 PROC_UPDATE_CREDS_ONPROC(p
);
4323 mac_proc_set_enforce(p
, MAC_ALL_ENFORCE
);
4328 /* Drop old proc reference or our extra reference */
4329 kauth_cred_unref(&my_cred
);
4336 * for temporary binary compatibility
4338 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
);
4340 kauth_cred_setlabel(kauth_cred_t cred
, struct label
*label
)
4342 return kauth_cred_label_update(cred
, label
);
4345 int kauth_proc_setlabel(struct proc
*p
, struct label
*label
);
4347 kauth_proc_setlabel(struct proc
*p
, struct label
*label
)
4349 return kauth_proc_label_update(p
, label
);
4355 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4356 * Since we cannot build our export lists based on the kernel configuration we need
4360 kauth_cred_label_update(__unused kauth_cred_t cred
, __unused
void *label
)
4366 kauth_proc_label_update(__unused
struct proc
*p
, __unused
void *label
)
4373 * for temporary binary compatibility
4375 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred
, void *label
);
4377 kauth_cred_setlabel(__unused kauth_cred_t cred
, __unused
void *label
)
4382 int kauth_proc_setlabel(struct proc
*p
, void *label
);
4384 kauth_proc_setlabel(__unused
struct proc
*p
, __unused
void *label
)
4394 * Description: Add a reference to the passed credential
4396 * Parameters: cred The credential to reference
4400 * Notes: This function adds a reference to the provided credential;
4401 * the existing reference on the credential is assumed to be
4402 * held stable over this operation by taking the appropriate
4403 * lock to protect the pointer from which it is being referenced,
4404 * if necessary (e.g. the proc lock is held over the call if the
4405 * credential being referenced is from p_ucred, the vnode lock
4406 * if from the per vnode name cache cred cache, and so on).
4408 * This is safe from the kauth_cred_unref() path, since an atomic
4409 * add is used, and the unref path specifically checks to see that
4410 * the value has not been changed to add a reference between the
4411 * time the credential is unreferenced by another pointer and the
4412 * time it is unreferenced from the cred hash cache.
4415 kauth_cred_ref(kauth_cred_t cred
)
4419 NULLCRED_CHECK(cred
);
4421 old_value
= OSAddAtomicLong(1, (long*)&cred
->cr_ref
);
4424 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4426 #if 0 // use this to watch a specific credential
4427 if ( is_target_cred( cred
) != 0 ) {
4437 * kauth_cred_unref_hashlocked
4439 * Description: release a credential reference; when the last reference is
4440 * released, the credential will be freed.
4442 * Parameters: credp Pointer to address containing
4443 * credential to be freed
4448 * *credp Set to NOCRED
4450 * Notes: This function assumes the credential hash lock is held.
4452 * This function is internal use only, since the hash lock is
4453 * scoped to this compilation unit.
4455 * This function destroys the contents of the pointer passed by
4456 * the caller to prevent the caller accidentally attempting to
4457 * release a given reference twice in error.
4459 * The last reference is considered to be released when a release
4460 * of a credential of a reference count of 2 occurs; this is an
4461 * intended effect, to take into account the reference held by
4462 * the credential hash, which is released at the same time.
4465 kauth_cred_unref_hashlocked(kauth_cred_t
*credp
)
4469 KAUTH_CRED_HASH_LOCK_ASSERT();
4470 NULLCRED_CHECK(*credp
);
4472 old_value
= OSAddAtomicLong(-1, (long*)&(*credp
)->cr_ref
);
4476 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm
, *credp
);
4478 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm
, *credp
);
4481 #if 0 // use this to watch a specific credential
4482 if ( is_target_cred( *credp
) != 0 ) {
4488 * If the old_value is 2, then we have just released the last external
4489 * reference to this credential
4491 if (old_value
< 3) {
4492 /* The last absolute reference is our credential hash table */
4493 kauth_cred_remove(*credp
);
4502 * Description: Release a credential reference while holding the credential
4503 * hash lock; when the last reference is released, the credential
4506 * Parameters: credp Pointer to address containing
4507 * credential to be freed
4512 * *credp Set to NOCRED
4514 * Notes: See kauth_cred_unref_hashlocked() for more information.
4518 kauth_cred_unref(kauth_cred_t
*credp
)
4520 KAUTH_CRED_HASH_LOCK();
4521 kauth_cred_unref_hashlocked(credp
);
4522 KAUTH_CRED_HASH_UNLOCK();
4530 * Description: release a credential reference; when the last reference is
4531 * released, the credential will be freed
4533 * Parameters: cred Credential to release
4537 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4538 * clear the pointer in the caller to avoid multiple releases of
4539 * the same credential. The currently recommended interface is
4540 * kauth_cred_unref().
4543 kauth_cred_rele(kauth_cred_t cred
)
4545 kauth_cred_unref(&cred
);
4547 #endif /* !__LP64__ */
4553 * Description: Duplicate a credential via alloc and copy; the new credential
4556 * Parameters: cred The credential to duplicate
4558 * Returns: (kauth_cred_t) The duplicate credential
4560 * Notes: The typical value to calling this routine is if you are going
4561 * to modify an existing credential, and expect to need a new one
4562 * from the hash cache.
4564 * This should probably not be used in the majority of cases;
4565 * if you are using it instead of kauth_cred_create(), you are
4566 * likely making a mistake.
4568 * The newly allocated credential is copied as part of the
4569 * allocation process, with the exception of the reference
4570 * count, which is set to 1 to indicate a single reference
4571 * held by the caller.
4573 * Since newly allocated credentials have no external pointers
4574 * referencing them, prior to making them visible in an externally
4575 * visible pointer (e.g. by adding them to the credential hash
4576 * cache) is the only legal time in which an existing credential
4577 * can be safely initialized or modified directly.
4579 * After initialization, the caller is expected to call the
4580 * function kauth_cred_add() to add the credential to the hash
4581 * cache, after which time it's frozen and becomes publicly
4584 * The release protocol depends on kauth_hash_add() being called
4585 * before kauth_cred_rele() (there is a diagnostic panic which
4586 * will trigger if this protocol is not observed).
4590 kauth_cred_dup(kauth_cred_t cred
)
4592 kauth_cred_t newcred
;
4594 struct label
*temp_label
;
4598 if (cred
== NOCRED
|| cred
== FSCRED
)
4599 panic("kauth_cred_dup: bad credential");
4601 newcred
= kauth_cred_alloc();
4602 if (newcred
!= NULL
) {
4604 temp_label
= newcred
->cr_label
;
4606 bcopy(cred
, newcred
, sizeof(*newcred
));
4608 newcred
->cr_label
= temp_label
;
4609 mac_cred_label_associate(cred
, newcred
);
4611 AUDIT_SESSION_REF(cred
);
4612 newcred
->cr_ref
= 1;
4618 * kauth_cred_copy_real
4620 * Description: Returns a credential based on the passed credential but which
4621 * reflects the real rather than effective UID and GID.
4623 * Parameters: cred The credential from which to
4624 * derive the new credential
4626 * Returns: (kauth_cred_t) The copied credential
4628 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4629 * result, the caller is responsible for dropping BOTH the
4630 * additional reference on the passed cred (if any), and the
4631 * credential returned by this function. The drop should be
4632 * via the kauth_cred_unref() KPI.
4635 kauth_cred_copy_real(kauth_cred_t cred
)
4637 kauth_cred_t newcred
= NULL
, found_cred
;
4638 struct ucred temp_cred
;
4639 posix_cred_t temp_pcred
= posix_cred_get(&temp_cred
);
4640 posix_cred_t pcred
= posix_cred_get(cred
);
4642 /* if the credential is already 'real', just take a reference */
4643 if ((pcred
->cr_ruid
== pcred
->cr_uid
) &&
4644 (pcred
->cr_rgid
== pcred
->cr_gid
)) {
4645 kauth_cred_ref(cred
);
4650 * Look up in cred hash table to see if we have a matching credential
4651 * with the new values.
4653 bcopy(cred
, &temp_cred
, sizeof(temp_cred
));
4654 temp_pcred
->cr_uid
= pcred
->cr_ruid
;
4655 /* displacing a supplementary group opts us out of memberd */
4656 if (kauth_cred_change_egid(&temp_cred
, pcred
->cr_rgid
)) {
4657 temp_pcred
->cr_flags
|= CRF_NOMEMBERD
;
4658 temp_pcred
->cr_gmuid
= KAUTH_UID_NONE
;
4661 * If the cred is not opted out, make sure we are using the r/euid
4664 if (temp_pcred
->cr_gmuid
!= KAUTH_UID_NONE
)
4665 temp_pcred
->cr_gmuid
= pcred
->cr_ruid
;
4670 KAUTH_CRED_HASH_LOCK();
4671 found_cred
= kauth_cred_find(&temp_cred
);
4672 if (found_cred
== cred
) {
4673 /* same cred so just bail */
4674 KAUTH_CRED_HASH_UNLOCK();
4677 if (found_cred
!= NULL
) {
4679 * Found a match so we bump reference count on new
4680 * one. We leave the old one alone.
4682 kauth_cred_ref(found_cred
);
4683 KAUTH_CRED_HASH_UNLOCK();
4688 * Must allocate a new credential, copy in old credential
4689 * data and update the real user and group IDs.
4691 newcred
= kauth_cred_dup(&temp_cred
);
4692 err
= kauth_cred_add(newcred
);
4693 KAUTH_CRED_HASH_UNLOCK();
4695 /* Retry if kauth_cred_add() fails */
4699 mac_cred_label_destroy(newcred
);
4701 AUDIT_SESSION_UNREF(newcred
);
4703 FREE_ZONE(newcred
, sizeof(*newcred
), M_CRED
);
4714 * Description: Common code to update a credential
4716 * Parameters: old_cred Reference counted credential
4718 * model_cred Non-reference counted model
4719 * credential to apply to the
4720 * credential to be updated
4721 * retain_auditinfo Flag as to whether or not the
4722 * audit information should be
4723 * copied from the old_cred into
4726 * Returns: (kauth_cred_t) The updated credential
4728 * IMPORTANT: This function will potentially return a credential other than
4729 * the one it is passed, and if so, it will have dropped the
4730 * reference on the passed credential. All callers should be
4731 * aware of this, and treat this function as an unref + ref,
4732 * potentially on different credentials.
4734 * Because of this, the caller is expected to take its own
4735 * reference on the credential passed as the first parameter,
4736 * and be prepared to release the reference on the credential
4737 * that is returned to them, if it is not intended to be a
4738 * persistent reference.
4741 kauth_cred_update(kauth_cred_t old_cred
, kauth_cred_t model_cred
,
4742 boolean_t retain_auditinfo
)
4744 kauth_cred_t found_cred
, new_cred
= NULL
;
4747 * Make sure we carry the auditinfo forward to the new credential
4748 * unless we are actually updating the auditinfo.
4750 if (retain_auditinfo
) {
4751 bcopy(&old_cred
->cr_audit
, &model_cred
->cr_audit
,
4752 sizeof(model_cred
->cr_audit
));
4758 KAUTH_CRED_HASH_LOCK();
4759 found_cred
= kauth_cred_find(model_cred
);
4760 if (found_cred
== old_cred
) {
4761 /* same cred so just bail */
4762 KAUTH_CRED_HASH_UNLOCK();
4765 if (found_cred
!= NULL
) {
4766 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred
, found_cred
);
4768 * Found a match so we bump reference count on new
4769 * one and decrement reference count on the old one.
4771 kauth_cred_ref(found_cred
);
4772 kauth_cred_unref_hashlocked(&old_cred
);
4773 KAUTH_CRED_HASH_UNLOCK();
4778 * Must allocate a new credential using the model. also
4779 * adds the new credential to the credential hash table.
4781 new_cred
= kauth_cred_dup(model_cred
);
4782 err
= kauth_cred_add(new_cred
);
4783 KAUTH_CRED_HASH_UNLOCK();
4785 /* retry if kauth_cred_add returns non zero value */
4789 mac_cred_label_destroy(new_cred
);
4791 AUDIT_SESSION_UNREF(new_cred
);
4793 FREE_ZONE(new_cred
, sizeof(*new_cred
), M_CRED
);
4797 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred
, new_cred
);
4798 kauth_cred_unref(&old_cred
);
4806 * Description: Add the given credential to our credential hash table and
4807 * take an additional reference to account for our use of the
4808 * credential in the hash table
4810 * Parameters: new_cred Credential to insert into cred
4813 * Returns: 0 Success
4814 * -1 Hash insertion failed: caller
4817 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4819 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4822 kauth_cred_add(kauth_cred_t new_cred
)
4826 KAUTH_CRED_HASH_LOCK_ASSERT();
4828 hash_key
= kauth_cred_get_hashkey(new_cred
);
4829 hash_key
%= kauth_cred_table_size
;
4831 /* race fix - there is a window where another matching credential
4832 * could have been inserted between the time this one was created and we
4833 * got the hash lock. If we find a match return an error and have the
4836 if (kauth_cred_find(new_cred
) != NULL
) {
4840 /* take a reference for our use in credential hash table */
4841 kauth_cred_ref(new_cred
);
4843 /* insert the credential into the hash table */
4844 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor
[hash_key
], new_cred
, cr_link
);
4853 * Description: Remove the given credential from our credential hash table
4855 * Parameters: cred Credential to remove from cred
4860 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4862 * Notes: The check for the reference increment after entry is generally
4863 * agree to be safe, since we use atomic operations, and the
4864 * following code occurs with the hash lock held; in theory, this
4865 * protects us from the 2->1 reference that gets us here.
4868 kauth_cred_remove(kauth_cred_t cred
)
4871 kauth_cred_t found_cred
;
4873 hash_key
= kauth_cred_get_hashkey(cred
);
4874 hash_key
%= kauth_cred_table_size
;
4877 if (cred
->cr_ref
< 1)
4878 panic("cred reference underflow");
4879 if (cred
->cr_ref
> 1)
4880 return; /* someone else got a ref */
4882 /* Find cred in the credential hash table */
4883 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4884 if (found_cred
== cred
) {
4885 /* found a match, remove it from the hash table */
4886 TAILQ_REMOVE(&kauth_cred_table_anchor
[hash_key
], found_cred
, cr_link
);
4888 mac_cred_label_destroy(cred
);
4890 AUDIT_SESSION_UNREF(cred
);
4893 FREE_ZONE(cred
, sizeof(*cred
), M_CRED
);
4894 #if KAUTH_CRED_HASH_DEBUG
4901 /* Did not find a match... this should not happen! XXX Make panic? */
4902 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__
, __LINE__
, __FUNCTION__
, current_proc()->p_comm
, cred
);
4910 * Description: Using the given credential data, look for a match in our
4911 * credential hash table
4913 * Parameters: cred Credential to lookup in cred
4916 * Returns: NULL Not found
4917 * !NULL Matching credential already in
4920 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4923 kauth_cred_find(kauth_cred_t cred
)
4926 kauth_cred_t found_cred
;
4927 posix_cred_t pcred
= posix_cred_get(cred
);
4929 KAUTH_CRED_HASH_LOCK_ASSERT();
4931 #if KAUTH_CRED_HASH_DEBUG
4932 static int test_count
= 0;
4935 if ((test_count
% 200) == 0) {
4936 kauth_cred_hash_print();
4940 hash_key
= kauth_cred_get_hashkey(cred
);
4941 hash_key
%= kauth_cred_table_size
;
4943 /* Find cred in the credential hash table */
4944 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[hash_key
], cr_link
) {
4946 posix_cred_t found_pcred
= posix_cred_get(found_cred
);
4949 * don't worry about the label unless the flags in
4950 * either credential tell us to.
4952 match
= (bcmp(found_pcred
, pcred
, sizeof (*pcred
)) == 0) ? TRUE
: FALSE
;
4953 match
= match
&& ((bcmp(&found_cred
->cr_audit
, &cred
->cr_audit
,
4954 sizeof(cred
->cr_audit
)) == 0) ? TRUE
: FALSE
);
4955 if (((found_pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0) ||
4956 ((pcred
->cr_flags
& CRF_MAC_ENFORCE
) != 0)) {
4957 match
= match
&& mac_cred_label_compare(found_cred
->cr_label
,
4966 /* No match found */
4975 * Description: Generates a hash key using data that makes up a credential;
4978 * Parameters: datap Pointer to data to hash
4979 * data_len Count of bytes to hash
4980 * start_key Start key value
4982 * Returns: (u_long) Returned hash key
4984 static inline u_long
4985 kauth_cred_hash(const uint8_t *datap
, int data_len
, u_long start_key
)
4987 u_long hash_key
= start_key
;
4990 while (data_len
> 0) {
4991 hash_key
= (hash_key
<< 4) + *datap
++;
4992 temp
= hash_key
& 0xF0000000;
4994 hash_key
^= temp
>> 24;
5004 * kauth_cred_get_hashkey
5006 * Description: Generate a hash key using data that makes up a credential;
5007 * based on ElfHash. We hash on the entire credential data,
5008 * not including the ref count or the TAILQ, which are mutable;
5009 * everything else isn't.
5011 * Parameters: cred Credential for which hash is
5014 * Returns: (u_long) Returned hash key
5016 * Notes: When actually moving the POSIX credential into a real label,
5017 * remember to update this hash computation.
5020 kauth_cred_get_hashkey(kauth_cred_t cred
)
5022 posix_cred_t pcred
= posix_cred_get(cred
);
5023 u_long hash_key
= 0;
5025 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_posix
,
5026 sizeof (struct posix_cred
),
5028 hash_key
= kauth_cred_hash((uint8_t *)&cred
->cr_audit
,
5029 sizeof(struct au_session
),
5032 if (pcred
->cr_flags
& CRF_MAC_ENFORCE
) {
5033 hash_key
= kauth_cred_hash((uint8_t *)cred
->cr_label
,
5034 sizeof (struct label
),
5041 #if KAUTH_CRED_HASH_DEBUG
5043 * kauth_cred_hash_print
5045 * Description: Print out cred hash cache table information for debugging
5046 * purposes, including the credential contents
5048 * Parameters: (void)
5052 * Implicit returns: Results in console output
5055 kauth_cred_hash_print(void)
5058 kauth_cred_t found_cred
;
5060 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count
);
5061 /* count slot hits, misses, collisions, and max depth */
5062 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5063 printf("[%02d] ", i
);
5065 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5070 kauth_cred_print(found_cred
);
5074 printf("NOCRED \n");
5078 #endif /* KAUTH_CRED_HASH_DEBUG */
5081 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5085 * Description: Print out an individual credential's contents for debugging
5088 * Parameters: cred The credential to print out
5092 * Implicit returns: Results in console output
5095 kauth_cred_print(kauth_cred_t cred
)
5099 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
);
5100 printf("group count %d gids ", cred
->cr_ngroups
);
5101 for (i
= 0; i
< NGROUPS
; i
++) {
5104 printf("%d ", cred
->cr_groups
[i
]);
5106 printf("r%d sv%d ", cred
->cr_rgid
, cred
->cr_svgid
);
5107 printf("auditinfo_addr %d %d %d %d %d %d\n",
5108 cred
->cr_audit
.s_aia_p
->ai_auid
,
5109 cred
->cr_audit
.as_mask
.am_success
,
5110 cred
->cr_audit
.as_mask
.am_failure
,
5111 cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
,
5112 cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0],
5113 cred
->cr_audit
.as_aia_p
->ai_asid
);
5116 int is_target_cred( kauth_cred_t the_cred
)
5118 if ( the_cred
->cr_uid
!= 0 )
5120 if ( the_cred
->cr_ruid
!= 0 )
5122 if ( the_cred
->cr_svuid
!= 0 )
5124 if ( the_cred
->cr_ngroups
!= 11 )
5126 if ( the_cred
->cr_groups
[0] != 11 )
5128 if ( the_cred
->cr_groups
[1] != 81 )
5130 if ( the_cred
->cr_groups
[2] != 63947 )
5132 if ( the_cred
->cr_groups
[3] != 80288 )
5134 if ( the_cred
->cr_groups
[4] != 89006 )
5136 if ( the_cred
->cr_groups
[5] != 52173 )
5138 if ( the_cred
->cr_groups
[6] != 84524 )
5140 if ( the_cred
->cr_groups
[7] != 79 )
5142 if ( the_cred
->cr_groups
[8] != 80292 )
5144 if ( the_cred
->cr_groups
[9] != 80 )
5146 if ( the_cred
->cr_groups
[10] != 90824 )
5148 if ( the_cred
->cr_rgid
!= 11 )
5150 if ( the_cred
->cr_svgid
!= 11 )
5152 if ( the_cred
->cr_gmuid
!= 3475 )
5154 if ( the_cred
->cr_audit
.as_aia_p
->ai_auid
!= 3475 )
5157 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5159 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5161 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5163 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5165 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5167 if ( the_cred->cr_flags != 0 )
5170 return( -1 ); // found target cred
5173 void get_backtrace( void )
5176 void * my_stack
[ MAX_STACK_DEPTH
];
5179 if ( cred_debug_buf_p
== NULL
) {
5180 MALLOC(cred_debug_buf_p
, cred_debug_buffer
*, sizeof(*cred_debug_buf_p
), M_KAUTH
, M_WAITOK
);
5181 bzero(cred_debug_buf_p
, sizeof(*cred_debug_buf_p
));
5184 if ( cred_debug_buf_p
->next_slot
> (MAX_CRED_BUFFER_SLOTS
- 1) ) {
5185 /* buffer is full */
5189 my_depth
= OSBacktrace(&my_stack
[0], MAX_STACK_DEPTH
);
5190 if ( my_depth
== 0 ) {
5191 printf("%s - OSBacktrace failed \n", __FUNCTION__
);
5195 /* fill new backtrace */
5196 my_slot
= cred_debug_buf_p
->next_slot
;
5197 cred_debug_buf_p
->next_slot
++;
5198 cred_debug_buf_p
->stack_buffer
[ my_slot
].depth
= my_depth
;
5199 for ( i
= 0; i
< my_depth
; i
++ ) {
5200 cred_debug_buf_p
->stack_buffer
[ my_slot
].stack
[ i
] = my_stack
[ i
];
5207 /* subset of struct ucred for use in sysctl_dump_creds */
5208 struct debug_ucred
{
5210 u_long cr_ref
; /* reference count */
5211 uid_t cr_uid
; /* effective user id */
5212 uid_t cr_ruid
; /* real user id */
5213 uid_t cr_svuid
; /* saved user id */
5214 short cr_ngroups
; /* number of groups in advisory list */
5215 gid_t cr_groups
[NGROUPS
]; /* advisory group list */
5216 gid_t cr_rgid
; /* real group id */
5217 gid_t cr_svgid
; /* saved group id */
5218 uid_t cr_gmuid
; /* UID for group membership purposes */
5219 struct auditinfo_addr cr_audit
; /* user auditing data. */
5220 void *cr_label
; /* MACF label */
5221 int cr_flags
; /* flags on credential */
5223 typedef struct debug_ucred debug_ucred
;
5225 SYSCTL_PROC(_kern
, OID_AUTO
, dump_creds
, CTLFLAG_RD
,
5226 NULL
, 0, sysctl_dump_creds
, "S,debug_ucred", "List of credentials in the cred hash");
5229 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5233 sysctl_dump_creds( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5235 int i
, j
, counter
= 0;
5238 kauth_cred_t found_cred
;
5239 debug_ucred
* cred_listp
;
5240 debug_ucred
* nextp
;
5242 /* This is a readonly node. */
5243 if (req
->newptr
!= USER_ADDR_NULL
)
5246 /* calculate space needed */
5247 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5248 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5253 /* they are querying us so just return the space required. */
5254 if (req
->oldptr
== USER_ADDR_NULL
) {
5255 counter
+= 10; // add in some padding;
5256 req
->oldidx
= counter
* sizeof(debug_ucred
);
5260 MALLOC( cred_listp
, debug_ucred
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5261 if ( cred_listp
== NULL
) {
5265 /* fill in creds to send back */
5268 for (i
= 0; i
< kauth_cred_table_size
; i
++) {
5269 TAILQ_FOREACH(found_cred
, &kauth_cred_table_anchor
[i
], cr_link
) {
5270 nextp
->credp
= found_cred
;
5271 nextp
->cr_ref
= found_cred
->cr_ref
;
5272 nextp
->cr_uid
= found_cred
->cr_uid
;
5273 nextp
->cr_ruid
= found_cred
->cr_ruid
;
5274 nextp
->cr_svuid
= found_cred
->cr_svuid
;
5275 nextp
->cr_ngroups
= found_cred
->cr_ngroups
;
5276 for ( j
= 0; j
< nextp
->cr_ngroups
; j
++ ) {
5277 nextp
->cr_groups
[ j
] = found_cred
->cr_groups
[ j
];
5279 nextp
->cr_rgid
= found_cred
->cr_rgid
;
5280 nextp
->cr_svgid
= found_cred
->cr_svgid
;
5281 nextp
->cr_gmuid
= found_cred
->cr_gmuid
;
5282 nextp
->cr_audit
.ai_auid
=
5283 found_cred
->cr_audit
.as_aia_p
->ai_auid
;
5284 nextp
->cr_audit
.ai_mask
.am_success
=
5285 found_cred
->cr_audit
.as_mask
.am_success
;
5286 nextp
->cr_audit
.ai_mask
.am_failure
=
5287 found_cred
->cr_audit
.as_mask
.am_failure
;
5288 nextp
->cr_audit
.ai_termid
.at_port
=
5289 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_port
;
5290 nextp
->cr_audit
.ai_termid
.at_type
=
5291 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_type
;
5292 nextp
->cr_audit
.ai_termid
.at_addr
[0] =
5293 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[0];
5294 nextp
->cr_audit
.ai_termid
.at_addr
[1] =
5295 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[1];
5296 nextp
->cr_audit
.ai_termid
.at_addr
[2] =
5297 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[2];
5298 nextp
->cr_audit
.ai_termid
.at_addr
[3] =
5299 found_cred
->cr_audit
.as_aia_p
->ai_termid
.at_addr
[3];
5300 nextp
->cr_audit
.ai_asid
=
5301 found_cred
->cr_audit
.as_aia_p
->ai_asid
;
5302 nextp
->cr_audit
.ai_flags
=
5303 found_cred
->cr_audit
.as_aia_p
->ai_flags
;
5304 nextp
->cr_label
= found_cred
->cr_label
;
5305 nextp
->cr_flags
= found_cred
->cr_flags
;
5307 space
+= sizeof(debug_ucred
);
5308 if ( space
> req
->oldlen
) {
5309 FREE(cred_listp
, M_TEMP
);
5314 req
->oldlen
= space
;
5315 error
= SYSCTL_OUT(req
, cred_listp
, req
->oldlen
);
5316 FREE(cred_listp
, M_TEMP
);
5321 SYSCTL_PROC(_kern
, OID_AUTO
, cred_bt
, CTLFLAG_RD
,
5322 NULL
, 0, sysctl_dump_cred_backtraces
, "S,cred_debug_buffer", "dump credential backtrace");
5325 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5329 sysctl_dump_cred_backtraces( __unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
5334 cred_debug_buffer
* bt_bufp
;
5335 cred_backtrace
* nextp
;
5337 /* This is a readonly node. */
5338 if (req
->newptr
!= USER_ADDR_NULL
)
5341 if ( cred_debug_buf_p
== NULL
) {
5345 /* calculate space needed */
5346 space
= sizeof( cred_debug_buf_p
->next_slot
);
5347 space
+= (sizeof( cred_backtrace
) * cred_debug_buf_p
->next_slot
);
5349 /* they are querying us so just return the space required. */
5350 if (req
->oldptr
== USER_ADDR_NULL
) {
5351 req
->oldidx
= space
;
5355 if ( space
> req
->oldlen
) {
5359 MALLOC( bt_bufp
, cred_debug_buffer
*, req
->oldlen
, M_TEMP
, M_WAITOK
);
5360 if ( bt_bufp
== NULL
) {
5364 /* fill in backtrace info to send back */
5365 bt_bufp
->next_slot
= cred_debug_buf_p
->next_slot
;
5366 space
= sizeof(bt_bufp
->next_slot
);
5368 nextp
= &bt_bufp
->stack_buffer
[ 0 ];
5369 for (i
= 0; i
< cred_debug_buf_p
->next_slot
; i
++) {
5370 nextp
->depth
= cred_debug_buf_p
->stack_buffer
[ i
].depth
;
5371 for ( j
= 0; j
< nextp
->depth
; j
++ ) {
5372 nextp
->stack
[ j
] = cred_debug_buf_p
->stack_buffer
[ i
].stack
[ j
];
5374 space
+= sizeof(*nextp
);
5377 req
->oldlen
= space
;
5378 error
= SYSCTL_OUT(req
, bt_bufp
, req
->oldlen
);
5379 FREE(bt_bufp
, M_TEMP
);
5383 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5387 **********************************************************************
5388 * The following routines will be moved to a policy_posix.c module at
5389 * some future point.
5390 **********************************************************************
5396 * Description: Helper function to create a kauth_cred_t credential that is
5397 * initally labelled with a specific POSIX credential label
5399 * Parameters: pcred The posix_cred_t to use as the initial
5402 * Returns: (kauth_cred_t) The credential that was found in the
5404 * NULL kauth_cred_add() failed, or there was
5405 * no egid specified, or we failed to
5406 * attach a label to the new credential
5408 * Notes: This function currently wraps kauth_cred_create(), and is the
5409 * only consumer of that ill-fated function, apart from bsd_init().
5410 * It exists solely to support the NFS server code creation of
5411 * credentials based on the over-the-wire RPC calls containing
5412 * traditional POSIX credential information being tunneled to
5413 * the server host from the client machine.
5415 * In the future, we hope this function goes away.
5417 * In the short term, it creates a temporary credential, puts
5418 * the POSIX information from NFS into it, and then calls
5419 * kauth_cred_create(), as an internal implementation detail.
5421 * If we have to keep it around in the medium term, it will
5422 * create a new kauth_cred_t, then label it with a POSIX label
5423 * corresponding to the contents of the kauth_cred_t. If the
5424 * policy_posix MACF module is not loaded, it will instead
5425 * substitute a posix_cred_t which GRANTS all access (effectively
5426 * a "root" credential) in order to not prevent NFS from working
5427 * in the case that we are not supporting POSIX credentials.
5430 posix_cred_create(posix_cred_t pcred
)
5432 struct ucred temp_cred
;
5434 bzero(&temp_cred
, sizeof(temp_cred
));
5435 temp_cred
.cr_posix
= *pcred
;
5437 return kauth_cred_create(&temp_cred
);
5444 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5445 * any, which is associated with it.
5447 * Parameters: cred The credential to obtain the label from
5449 * Returns: posix_cred_t The POSIX credential label
5451 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5452 * this function will return a pointer to a posix_cred_t which
5453 * GRANTS all access (effectively, a "root" credential). This is
5454 * necessary to support legacy code which insists on tightly
5455 * integrating POSIX credentials into its APIs, including, but
5456 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5457 * NFSv3, signals, dtrace, and a large number of kauth routines
5458 * used to implement POSIX permissions related system calls.
5460 * In the event that the policy_posix MACF module IS loaded, and
5461 * there is no POSIX label on the kauth_cred_t credential, this
5462 * function will return a pointer to a posix_cred_t which DENIES
5463 * all access (effectively, a "deny rights granted by POSIX"
5464 * credential). This is necessary to support the concept of a
5465 * transiently loaded POSIX policy, or kauth_cred_t credentials
5466 * which can not be used in conjunctions with POSIX permissions
5469 * This function currently returns the address of the cr_posix
5470 * field of the supplied kauth_cred_t credential, and as such
5471 * currently can not fail. In the future, this will not be the
5475 posix_cred_get(kauth_cred_t cred
)
5477 return(&cred
->cr_posix
);
5484 * Description: Label a kauth_cred_t with a POSIX credential label
5486 * Parameters: cred The credential to label
5487 * pcred The POSIX credential t label it with
5491 * Notes: This function is currently void in order to permit it to fit
5492 * in with the current MACF framework label methods which allow
5493 * labeling to fail silently. This is like acceptable for
5494 * mandatory access controls, but not for POSIX, since those
5495 * access controls are advisory. We will need to consider a
5496 * return value in a future version of the MACF API.
5498 * This operation currently cannot fail, as currently the POSIX
5499 * credential is a subfield of the kauth_cred_t (ucred), which
5500 * MUST be valid. In the future, this will not be the case.
5503 posix_cred_label(kauth_cred_t cred
, posix_cred_t pcred
)
5505 cred
->cr_posix
= *pcred
; /* structure assign for now */
5512 * Description: Perform a POSIX access check for a protected object
5514 * Parameters: cred The credential to check
5515 * object_uid The POSIX UID of the protected object
5516 * object_gid The POSIX GID of the protected object
5517 * object_mode The POSIX mode of the protected object
5518 * mode_req The requested POSIX access rights
5520 * Returns 0 Access is granted
5521 * EACCES Access is denied
5523 * Notes: This code optimizes the case where the world and group rights
5524 * would both grant the requested rights to avoid making a group
5525 * membership query. This is a big performance win in the case
5526 * where this is true.
5529 posix_cred_access(kauth_cred_t cred
, id_t object_uid
, id_t object_gid
, mode_t object_mode
, mode_t mode_req
)
5532 mode_t mode_owner
= (object_mode
& S_IRWXU
);
5533 mode_t mode_group
= (object_mode
& S_IRWXG
) << 3;
5534 mode_t mode_world
= (object_mode
& S_IRWXO
) << 6;
5537 * Check first for owner rights
5539 if (kauth_cred_getuid(cred
) == object_uid
&& (mode_req
& mode_owner
) == mode_req
)
5543 * Combined group and world rights check, if we don't have owner rights
5545 * OPTIMIZED: If group and world rights would grant the same bits, and
5546 * they set of requested bits is in both, then we can simply check the
5547 * world rights, avoiding a group membership check, which is expensive.
5549 if ((mode_req
& mode_group
& mode_world
) == mode_req
) {
5553 * NON-OPTIMIZED: requires group membership check.
5555 if ((mode_req
& mode_group
) != mode_req
) {
5557 * exclusion group : treat errors as "is a member"
5559 * NON-OPTIMIZED: +group would deny; must check group
5561 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5563 * DENY: +group denies
5567 if ((mode_req
& mode_world
) != mode_req
) {
5569 * DENY: both -group & world would deny
5574 * ALLOW: allowed by -group and +world
5581 * inclusion group; treat errors as "not a member"
5583 * NON-OPTIMIZED: +group allows, world denies; must
5586 if (!kauth_cred_ismember_gid(cred
, object_gid
, &is_member
) && is_member
) {
5588 * ALLOW: allowed by +group
5592 if ((mode_req
& mode_world
) != mode_req
) {
5594 * DENY: both -group & world would deny
5599 * ALLOW: allowed by -group and +world