1/*
2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
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,
32 * Version 2.0.
33 */
34
35/*
36 * Kernel Authorization framework: Management of process/thread credentials
37 * and identity information.
38 */
39
40#include <sys/param.h>	/* XXX trim includes */
41#include <sys/acct.h>
42#include <sys/systm.h>
43#include <sys/ucred.h>
44#include <sys/proc_internal.h>
45#include <sys/user.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>
51#include <sys/sdt.h>
52
53#include <security/audit/audit.h>
54
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>
60
61#include <libkern/OSAtomic.h>
62
63#include <kern/task.h>
64#include <kern/locks.h>
65#ifdef MACH_ASSERT
66# undef MACH_ASSERT
67#endif
68#define MACH_ASSERT 1	/* XXX so bogus */
69#include <kern/assert.h>
70
71#if CONFIG_MACF
72#include <security/mac.h>
73#include <security/mac_framework.h>
74#include <security/_label.h>
75#endif
76
77void mach_kauth_cred_uthread_update( void );
78
79#define CRED_DIAGNOSTIC 0
80
81# define NULLCRED_CHECK(_c)	do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
82
83/* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */
84#if 0
85#ifdef KAUTH_DEBUG
86#undef KAUTH_DEBUG
87#endif
88
89#ifdef K_UUID_FMT
90#undef K_UUID_FMT
91#endif
92
93#ifdef K_UUID_ARG
94#undef K_UUID_ARG
95#endif
96
97# define K_UUID_FMT "%08x:%08x:%08x:%08x"
98# define K_UUID_ARG(_u) *(int *)&_u.g_guid[0],*(int *)&_u.g_guid[4],*(int *)&_u.g_guid[8],*(int *)&_u.g_guid[12]
99# define KAUTH_DEBUG(fmt, args...)      do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0)
100#endif
101
102/*
103 * Credential debugging; we can track entry into a function that might
104 * change a credential, and we can track actual credential changes that
105 * result.
106 *
107 * Note:	Does *NOT* currently include per-thread credential changes
108 */
109
110#if DEBUG_CRED
111#define	DEBUG_CRED_ENTER		printf
112#define	DEBUG_CRED_CHANGE		printf
113extern void kauth_cred_print(kauth_cred_t cred);
114
115#include <libkern/OSDebug.h>	/* needed for get_backtrace( ) */
116
117int is_target_cred( kauth_cred_t the_cred );
118void get_backtrace( void );
119
120static int sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1,
121							  __unused int arg2, struct sysctl_req *req );
122static int
123sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1,
124							 __unused int arg2, struct sysctl_req *req );
125
126#define MAX_STACK_DEPTH 8
127struct cred_backtrace {
128	int				depth;
129	void *			stack[ MAX_STACK_DEPTH ];
130};
131typedef struct cred_backtrace cred_backtrace;
132
133#define MAX_CRED_BUFFER_SLOTS 200
134struct cred_debug_buffer {
135	int				next_slot;
136	cred_backtrace	stack_buffer[ MAX_CRED_BUFFER_SLOTS ];
137};
138typedef struct cred_debug_buffer cred_debug_buffer;
139cred_debug_buffer * cred_debug_buf_p = NULL;
140
141#else	/* !DEBUG_CRED */
142
143#define	DEBUG_CRED_ENTER(fmt, ...)	do {} while (0)
144#define	DEBUG_CRED_CHANGE(fmt, ...)	do {} while (0)
145
146#endif	/* !DEBUG_CRED */
147
148#if CONFIG_EXT_RESOLVER
149/*
150 * Interface to external identity resolver.
151 *
152 * The architecture of the interface is simple; the external resolver calls
153 * in to get work, then calls back with completed work.  It also calls us
154 * to let us know that it's (re)started, so that we can resubmit work if it
155 * times out.
156 */
157
158static lck_mtx_t *kauth_resolver_mtx;
159#define KAUTH_RESOLVER_LOCK()	lck_mtx_lock(kauth_resolver_mtx);
160#define KAUTH_RESOLVER_UNLOCK()	lck_mtx_unlock(kauth_resolver_mtx);
161
162static volatile pid_t	kauth_resolver_identity;
163static int	kauth_identitysvc_has_registered;
164static int	kauth_resolver_registered;
165static uint32_t	kauth_resolver_sequence;
166static int	kauth_resolver_timeout = 30;	/* default: 30 seconds */
167
168struct kauth_resolver_work {
169	TAILQ_ENTRY(kauth_resolver_work) kr_link;
170	struct kauth_identity_extlookup kr_work;
171	uint64_t	kr_extend;
172	uint32_t	kr_seqno;
173	int		kr_refs;
174	int		kr_flags;
175#define KAUTH_REQUEST_UNSUBMITTED	(1<<0)
176#define KAUTH_REQUEST_SUBMITTED		(1<<1)
177#define KAUTH_REQUEST_DONE		(1<<2)
178	int		kr_result;
179};
180
181TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
182TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work)	kauth_resolver_submitted;
183TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work)	kauth_resolver_done;
184
185/* Number of resolver timeouts between logged complaints */
186#define KAUTH_COMPLAINT_INTERVAL 1000
187int kauth_resolver_timeout_cnt = 0;
188
189static int	kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data);
190static int	kauth_resolver_complete(user_addr_t message);
191static int	kauth_resolver_getwork(user_addr_t message);
192static int	kauth_resolver_getwork2(user_addr_t message);
193static __attribute__((noinline)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
194	struct kauth_resolver_work *);
195
196#define	KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */
197
198struct kauth_identity {
199	TAILQ_ENTRY(kauth_identity) ki_link;
200	int	ki_valid;
201	uid_t	ki_uid;
202	gid_t	ki_gid;
203	int	ki_supgrpcnt;
204	gid_t	ki_supgrps[NGROUPS];
205	guid_t	ki_guid;
206	ntsid_t ki_ntsid;
207	const char	*ki_name;	/* string name from string cache */
208	/*
209	 * Expiry times are the earliest time at which we will disregard the
210	 * cached state and go to userland.  Before then if the valid bit is
211	 * set, we will return the cached value.  If it's not set, we will
212	 * not go to userland to resolve, just assume that there is no answer
213	 * available.
214	 */
215	time_t	ki_groups_expiry;
216	time_t	ki_guid_expiry;
217	time_t	ki_ntsid_expiry;
218};
219
220static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
221static lck_mtx_t *kauth_identity_mtx;
222#define KAUTH_IDENTITY_LOCK()	lck_mtx_lock(kauth_identity_mtx);
223#define KAUTH_IDENTITY_UNLOCK()	lck_mtx_unlock(kauth_identity_mtx);
224#define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100	/* XXX default sizing? */
225static int kauth_identity_cachemax = KAUTH_IDENTITY_CACHEMAX_DEFAULT;
226static int kauth_identity_count;
227
228static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
229	ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry,
230	const char *name, int nametype);
231static void	kauth_identity_register_and_free(struct kauth_identity *kip);
232static void	kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip, uint64_t extend_data);
233static void	kauth_identity_trimcache(int newsize);
234static void	kauth_identity_lru(struct kauth_identity *kip);
235static int	kauth_identity_guid_expired(struct kauth_identity *kip);
236static int	kauth_identity_ntsid_expired(struct kauth_identity *kip);
237static int	kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname);
238static int	kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir, char *getname);
239static int	kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname);
240static int	kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname);
241static int	kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir);
242
243struct kauth_group_membership {
244	TAILQ_ENTRY(kauth_group_membership) gm_link;
245	uid_t	gm_uid;		/* the identity whose membership we're recording */
246	gid_t	gm_gid;		/* group of which they are a member */
247	time_t	gm_expiry;	/* TTL for the membership, or 0 for persistent entries */
248	int	gm_flags;
249#define KAUTH_GROUP_ISMEMBER	(1<<0)
250};
251
252TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
253static lck_mtx_t *kauth_groups_mtx;
254#define KAUTH_GROUPS_LOCK()	lck_mtx_lock(kauth_groups_mtx);
255#define KAUTH_GROUPS_UNLOCK()	lck_mtx_unlock(kauth_groups_mtx);
256#define KAUTH_GROUPS_CACHEMAX_DEFAULT 100	/* XXX default sizing? */
257static int kauth_groups_cachemax = KAUTH_GROUPS_CACHEMAX_DEFAULT;
258static int kauth_groups_count;
259
260static int	kauth_groups_expired(struct kauth_group_membership *gm);
261static void	kauth_groups_lru(struct kauth_group_membership *gm);
262static void	kauth_groups_updatecache(struct kauth_identity_extlookup *el);
263static void	kauth_groups_trimcache(int newsize);
264
265#endif	/* CONFIG_EXT_RESOLVER */
266
267static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = KAUTH_CRED_PRIMES;
268static int	kauth_cred_primes_index = 0;
269static int	kauth_cred_table_size = 0;
270
271TAILQ_HEAD(kauth_cred_entry_head, ucred);
272static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
273
274#define KAUTH_CRED_HASH_DEBUG	0
275
276static int kauth_cred_add(kauth_cred_t new_cred);
277static boolean_t kauth_cred_remove(kauth_cred_t cred);
278static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
279static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
280static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
281static boolean_t kauth_cred_unref_hashlocked(kauth_cred_t *credp);
282
283#if KAUTH_CRED_HASH_DEBUG
284static int	kauth_cred_count = 0;
285static void kauth_cred_hash_print(void);
286static void kauth_cred_print(kauth_cred_t cred);
287#endif
288
289#if CONFIG_EXT_RESOLVER
290
291/*
292 *  __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__
293 *
294 * Description:  Waits for the user space daemon to respond to the request
295 *               we made. Function declared non inline to be visible in
296 *               stackshots and spindumps as well as debugging.
297 *
298 * Parameters:   workp                     Work queue entry.
299 *
300 * Returns:      0                         on Success.
301 *               EIO                       if Resolver is dead.
302 *               EINTR                     thread interrupted in msleep
303 *               EWOULDBLOCK               thread timed out in msleep
304 *               ERESTART                  returned by msleep.
305 *
306 */
307static __attribute__((noinline)) int
308__KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(
309	struct kauth_resolver_work  *workp)
310{
311	int error = 0;
312	struct timespec ts;
313	for (;;) {
314		/* we could compute a better timeout here */
315		ts.tv_sec = kauth_resolver_timeout;
316		ts.tv_nsec = 0;
317		error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
318		/* request has been completed? */
319		if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
320			break;
321		/* woken because the resolver has died? */
322		if (kauth_resolver_identity == 0) {
323			error = EIO;
324			break;
325		}
326		/* an error? */
327		if (error != 0)
328			break;
329	}
330	return error;
331}
332
333
334/*
335 * kauth_resolver_init
336 *
337 * Description:	Initialize the daemon side of the credential identity resolver
338 *
339 * Parameters:	(void)
340 *
341 * Returns:	(void)
342 *
343 * Notes:	Initialize the credential identity resolver for use; the
344 *		credential identity resolver is the KPI used by the user
345 *		space credential identity resolver daemon to communicate
346 *		with the kernel via the identitysvc() system call..
347 *
348 *		This is how membership in more than 16 groups (1 effective
349 *		and 15 supplementary) is supported, and also how UID's,
350 *		UUID's, and so on, are translated to/from POSIX credential
351 *		values.
352 *
353 *		The credential identity resolver operates by attempting to
354 *		determine identity first from the credential, then from
355 *		the kernel credential identity cache, and finally by
356 *		enqueueing a request to a user space daemon.
357 *
358 *		This function is called from kauth_init() in the file
359 *		kern_authorization.c.
360 */
361void
362kauth_resolver_init(void)
363{
364	TAILQ_INIT(&kauth_resolver_unsubmitted);
365	TAILQ_INIT(&kauth_resolver_submitted);
366	TAILQ_INIT(&kauth_resolver_done);
367	kauth_resolver_sequence = 31337;
368	kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
369}
370
371
372/*
373 * kauth_resolver_submit
374 *
375 * Description:	Submit an external credential identity resolution request to
376 *		the user space daemon.
377 *
378 * Parameters:	lkp				A pointer to an external
379 *						lookup request
380 *		extend_data			extended data for kr_extend
381 *
382 * Returns:	0				Success
383 *		EWOULDBLOCK			No resolver registered
384 *		EINTR				Operation interrupted (e.g. by
385 *						a signal)
386 *		ENOMEM				Could not allocate work item
387 *	copyinstr:EFAULT			Bad message from user space
388 *	workp->kr_result:???			An error from the user space
389 *						daemon (includes ENOENT!)
390 *
391 * Implicit returns:
392 *		*lkp				Modified
393 *
394 * Notes:	Allocate a work queue entry, submit the work and wait for
395 *		the operation to either complete or time out.  Outstanding
396 *		operations may also be cancelled.
397 *
398 *		Submission is by means of placing the item on a work queue
399 *		which is serviced by an external resolver thread calling
400 *		into the kernel.  The caller then sleeps until timeout,
401 *		cancellation, or an external resolver thread calls in with
402 *		a result message to kauth_resolver_complete().  All of these
403 *		events wake the caller back up.
404 *
405 *		This code is called from either kauth_cred_ismember_gid()
406 *		for a group membership request, or it is called from
407 *		kauth_cred_cache_lookup() when we get a cache miss.
408 */
409static int
410kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data)
411{
412	struct kauth_resolver_work *workp, *killp;
413	struct timespec ts;
414	int	error, shouldfree;
415
416	/* no point actually blocking if the resolver isn't up yet */
417	if (kauth_resolver_identity == 0) {
418		/*
419		 * We've already waited an initial <kauth_resolver_timeout>
420		 * seconds with no result.
421		 *
422		 * Sleep on a stack address so no one wakes us before timeout;
423		 * we sleep a half a second in case we are a high priority
424		 * process, so that memberd doesn't starve while we are in a
425		 * tight loop between user and kernel, eating all the CPU.
426		 */
427		error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2);
428		if (kauth_resolver_identity == 0) {
429			/*
430			 * if things haven't changed while we were asleep,
431			 * tell the caller we couldn't get an authoritative
432			 * answer.
433			 */
434			return(EWOULDBLOCK);
435		}
436	}
437
438	MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK);
439	if (workp == NULL)
440		return(ENOMEM);
441
442	workp->kr_work = *lkp;
443	workp->kr_extend = extend_data;
444	workp->kr_refs = 1;
445	workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
446	workp->kr_result = 0;
447
448	/*
449	 * We insert the request onto the unsubmitted queue, the call in from
450	 * the resolver will it to the submitted thread when appropriate.
451	 */
452	KAUTH_RESOLVER_LOCK();
453	workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++;
454	workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
455
456	/*
457	 * XXX We *MUST NOT* attempt to coalesce identical work items due to
458	 * XXX the inability to ensure order of update of the request item
459	 * XXX extended data vs. the wakeup; instead, we let whoever is waiting
460	 * XXX for each item repeat the update when they wake up.
461	 */
462	TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
463
464	/*
465	 * Wake up an external resolver thread to deal with the new work; one
466	 * may not be available, and if not, then the request will be grabbed
467	 * when a resolver thread comes back into the kernel to request new
468	 * work.
469	 */
470	wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
471	error = __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp);
472
473	/* if the request was processed, copy the result */
474	if (error == 0)
475		*lkp = workp->kr_work;
476
477	if (error == EWOULDBLOCK) {
478	        if ((kauth_resolver_timeout_cnt++ % KAUTH_COMPLAINT_INTERVAL) == 0) {
479                        printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n",
480                                kauth_resolver_timeout_cnt, kauth_resolver_timeout);
481                }
482
483	        if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
484	                /*
485                	 * If the request timed out and was never collected, the resolver
486                	 * is dead and probably not coming back anytime soon.  In this
487                	 * case we revert to no-resolver behaviour, and punt all the other
488                	 * sleeping requests to clear the backlog.
489                	 */
490                        KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
491
492                        /*
493                        * Make the current resolver non-authoritative, and mark it as
494                        * no longer registered to prevent kauth_cred_ismember_gid()
495                        * enqueueing more work until a new one is registered.  This
496                        * mitigates the damage a crashing resolver may inflict.
497                        */
498                        kauth_resolver_identity = 0;
499                        kauth_resolver_registered = 0;
500
501                        /* kill all the other requestes that are waiting as well */
502                        TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
503                                wakeup(killp);
504                        TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
505                                wakeup(killp);
506                        /* Cause all waiting-for-work threads to return EIO */
507                        wakeup((caddr_t)&kauth_resolver_unsubmitted);
508                }
509        }
510
511	/*
512	 * drop our reference on the work item, and note whether we should
513	 * free it or not
514	 */
515	if (--workp->kr_refs <= 0) {
516		/* work out which list we have to remove it from */
517		if (workp->kr_flags & KAUTH_REQUEST_DONE) {
518			TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link);
519		} else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) {
520			TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
521		} else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
522			TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
523		} else {
524			KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
525		}
526		shouldfree = 1;
527	} else {
528		/* someone else still has a reference on this request */
529		shouldfree = 0;
530	}
531
532	/* collect request result */
533	if (error == 0) {
534		error = workp->kr_result;
535	}
536	KAUTH_RESOLVER_UNLOCK();
537
538	/*
539	 * If we dropped the last reference, free the request.
540	 */
541	if (shouldfree) {
542		FREE(workp, M_KAUTH);
543	}
544
545	KAUTH_DEBUG("RESOLVER - returning %d", error);
546	return(error);
547}
548
549
550/*
551 * identitysvc
552 *
553 * Description:	System call interface for the external identity resolver.
554 *
555 * Parameters:	uap->message			Message from daemon to kernel
556 *
557 * Returns:	0				Successfully became resolver
558 *		EPERM				Not the resolver process
559 *	kauth_authorize_generic:EPERM		Not root user
560 *	kauth_resolver_complete:EIO
561 *	kauth_resolver_complete:EFAULT
562 *	kauth_resolver_getwork:EINTR
563 *	kauth_resolver_getwork:EFAULT
564 *
565 * Notes:	This system call blocks until there is work enqueued, at
566 *		which time the kernel wakes it up, and a message from the
567 *		kernel is copied out to the identity resolution daemon, which
568 *		proceed to attempt to resolve it.  When the resolution has
569 *		completed (successfully or not), the daemon called back into
570 *		this system call to give the result to the kernel, and wait
571 *		for the next request.
572 */
573int
574identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int32_t *retval)
575{
576	int opcode = uap->opcode;
577	user_addr_t message = uap->message;
578	struct kauth_resolver_work *workp;
579	struct kauth_cache_sizes sz_arg;
580	int error;
581	pid_t new_id;
582
583	/*
584	 * New server registering itself.
585	 */
586	if (opcode == KAUTH_EXTLOOKUP_REGISTER) {
587		new_id = current_proc()->p_pid;
588		if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) {
589			KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id);
590			return(error);
591		}
592		KAUTH_RESOLVER_LOCK();
593		if (kauth_resolver_identity != new_id) {
594			KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity);
595			/*
596			 * We have a new server, so assume that all the old requests have been lost.
597			 */
598			while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) {
599				TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
600				workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
601				workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED;
602				TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link);
603			}
604			/*
605			 * Allow user space resolver to override the
606			 * external resolution timeout
607			 */
608			if (message > 30 && message < 10000) {
609				kauth_resolver_timeout = message;
610				KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message);
611			}
612			kauth_resolver_identity = new_id;
613			kauth_resolver_registered = 1;
614			kauth_identitysvc_has_registered = 1;
615			wakeup(&kauth_resolver_unsubmitted);
616		}
617		KAUTH_RESOLVER_UNLOCK();
618		return(0);
619	}
620
621	/*
622	 * Beyond this point, we must be the resolver process.
623	 */
624	if (current_proc()->p_pid != kauth_resolver_identity) {
625		KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
626		return(EPERM);
627	}
628
629	if (opcode == KAUTH_GET_CACHE_SIZES) {
630		KAUTH_IDENTITY_LOCK();
631		sz_arg.kcs_id_size = kauth_identity_cachemax;
632		KAUTH_IDENTITY_UNLOCK();
633
634		KAUTH_GROUPS_LOCK();
635		sz_arg.kcs_group_size = kauth_groups_cachemax;
636		KAUTH_GROUPS_UNLOCK();
637
638		if ((error = copyout(&sz_arg, uap->message, sizeof (sz_arg))) != 0) {
639			return (error);
640		}
641
642		return (0);
643	} else if (opcode == KAUTH_SET_CACHE_SIZES) {
644		if ((error = copyin(uap->message, &sz_arg, sizeof (sz_arg))) != 0) {
645			return (error);
646		}
647
648		if ((sz_arg.kcs_group_size > KAUTH_CACHES_MAX_SIZE) ||
649		    (sz_arg.kcs_id_size > KAUTH_CACHES_MAX_SIZE)) {
650			return (EINVAL);
651		}
652
653		KAUTH_IDENTITY_LOCK();
654		kauth_identity_cachemax = sz_arg.kcs_id_size;
655		kauth_identity_trimcache(kauth_identity_cachemax);
656		KAUTH_IDENTITY_UNLOCK();
657
658		KAUTH_GROUPS_LOCK();
659		kauth_groups_cachemax = sz_arg.kcs_group_size;
660		kauth_groups_trimcache(kauth_groups_cachemax);
661		KAUTH_GROUPS_UNLOCK();
662
663		return (0);
664	} else if (opcode == KAUTH_CLEAR_CACHES) {
665		KAUTH_IDENTITY_LOCK();
666		kauth_identity_trimcache(0);
667		KAUTH_IDENTITY_UNLOCK();
668
669		KAUTH_GROUPS_LOCK();
670		kauth_groups_trimcache(0);
671		KAUTH_GROUPS_UNLOCK();
672	} else if (opcode == KAUTH_EXTLOOKUP_DEREGISTER) {
673		/*
674		 * Terminate outstanding requests; without an authoritative
675		 * resolver, we are now back on our own authority.
676		 */
677		struct kauth_resolver_work *killp;
678
679		KAUTH_RESOLVER_LOCK();
680
681		/*
682		 * Clear the identity, but also mark it as unregistered so
683		 * there is no explicit future expectation of us getting a
684		 * new resolver any time soon.
685		 */
686		kauth_resolver_identity = 0;
687		kauth_resolver_registered = 0;
688
689		TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
690		    wakeup(killp);
691		TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
692		    wakeup(killp);
693		/* Cause all waiting-for-work threads to return EIO */
694		wakeup((caddr_t)&kauth_resolver_unsubmitted);
695		KAUTH_RESOLVER_UNLOCK();
696	}
697
698	/*
699	 * Got a result returning?
700	 */
701	if (opcode & KAUTH_EXTLOOKUP_RESULT) {
702		if ((error = kauth_resolver_complete(message)) != 0)
703			return(error);
704	}
705
706	/*
707	 * Caller wants to take more work?
708	 */
709	if (opcode & KAUTH_EXTLOOKUP_WORKER) {
710		if ((error = kauth_resolver_getwork(message)) != 0)
711			return(error);
712	}
713
714	return(0);
715}
716
717
718/*
719 * kauth_resolver_getwork_continue
720 *
721 * Description:	Continuation for kauth_resolver_getwork
722 *
723 * Parameters:	result				Error code or 0 for the sleep
724 *						that got us to this function
725 *
726 * Returns:	0				Success
727 *		EINTR				Interrupted (e.g. by signal)
728 *	kauth_resolver_getwork2:EFAULT
729 *
730 * Notes:	See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
731 *		more information.
732 */
733static int
734kauth_resolver_getwork_continue(int result)
735{
736	thread_t thread;
737	struct uthread *ut;
738	user_addr_t message;
739
740	if (result) {
741		KAUTH_RESOLVER_UNLOCK();
742		return(result);
743	}
744
745	/*
746	 * If we lost a race with another thread/memberd restarting, then we
747	 * need to go back to sleep to look for more work.  If it was memberd
748	 * restarting, then the msleep0() will error out here, as our thread
749	 * will already be "dead".
750	 */
751	if (TAILQ_FIRST(&kauth_resolver_unsubmitted) == NULL) {
752		int error;
753
754		error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue);
755		/*
756		 * If this is a wakeup from another thread in the resolver
757		 * deregistering it, error out the request-for-work thread
758		 */
759		if (!kauth_resolver_identity)
760			error = EIO;
761		KAUTH_RESOLVER_UNLOCK();
762		return(error);
763	}
764
765	thread = current_thread();
766	ut = get_bsdthread_info(thread);
767	message = ut->uu_kevent.uu_kauth.message;
768	return(kauth_resolver_getwork2(message));
769}
770
771
772/*
773 * kauth_resolver_getwork2
774 *
775 * Decription:	Common utility function to copy out a identity resolver work
776 *		item from the kernel to user space as part of the user space
777 *		identity resolver requesting work.
778 *
779 * Parameters:	message				message to user space
780 *
781 * Returns:	0				Success
782 *		EFAULT				Bad user space message address
783 *
784 * Notes:	This common function exists to permit the use of continuations
785 *		in the identity resolution process.  This frees up the stack
786 *		while we are waiting for the user space resolver to complete
787 *		a request.  This is specifically used so that our per thread
788 *		cost can be small, and we will therefore be willing to run a
789 *		larger number of threads in the user space identity resolver.
790 */
791static int
792kauth_resolver_getwork2(user_addr_t message)
793{
794	struct kauth_resolver_work *workp;
795	int		error;
796
797	/*
798	 * Note: We depend on the caller protecting us from a NULL work item
799	 * queue, since we must have the kauth resolver lock on entry to this
800	 * function.
801	 */
802	workp = TAILQ_FIRST(&kauth_resolver_unsubmitted);
803
804	/*
805	 * Copy out the external lookup structure for the request, not
806	 * including the el_extend field, which contains the address of the
807	 * external buffer provided by the external resolver into which we
808	 * copy the extension request information.
809	 */
810	/* BEFORE FIELD */
811	if ((error = copyout(&workp->kr_work, message, offsetof(struct kauth_identity_extlookup, el_extend))) != 0) {
812		KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
813		goto out;
814	}
815	/* AFTER FIELD */
816	if ((error = copyout(&workp->kr_work.el_info_reserved_1,
817			message + offsetof(struct kauth_identity_extlookup, el_info_reserved_1),
818		sizeof(struct kauth_identity_extlookup) - offsetof(struct kauth_identity_extlookup, el_info_reserved_1))) != 0) {
819		KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
820		goto out;
821	}
822
823	/*
824	 * Handle extended requests here; if we have a request of a type where
825	 * the kernel wants a translation of extended information, then we need
826	 * to copy it out into the extended buffer, assuming the buffer is
827	 * valid; we only attempt to get the buffer address if we have request
828	 * data to copy into it.
829	 */
830
831	/*
832	 * translate a user@domain string into a uid/gid/whatever
833	 */
834	if (workp->kr_work.el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) {
835		uint64_t uaddr;
836
837		error = copyin(message + offsetof(struct kauth_identity_extlookup, el_extend), &uaddr, sizeof(uaddr));
838		if (!error) {
839			size_t actual;	/* not used */
840			/*
841			 * Use copyoutstr() to reduce the copy size; we let
842			 * this catch a NULL uaddr because we shouldn't be
843			 * asking in that case anyway.
844			 */
845			error = copyoutstr(CAST_DOWN(void *,workp->kr_extend), uaddr, MAXPATHLEN, &actual);
846		}
847		if (error) {
848			KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
849			goto out;
850		}
851	}
852	TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
853	workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
854	workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
855	TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
856
857out:
858	KAUTH_RESOLVER_UNLOCK();
859	return(error);
860}
861
862
863/*
864 * kauth_resolver_getwork
865 *
866 * Description:	Get a work item from the enqueued requests from the kernel and
867 *		give it to the user space daemon.
868 *
869 * Parameters:	message				message to user space
870 *
871 * Returns:	0				Success
872 *		EINTR				Interrupted (e.g. by signal)
873 *	kauth_resolver_getwork2:EFAULT
874 *
875 * Notes:	This function blocks in a continuation if there are no work
876 *		items available for processing at the time the user space
877 *		identity resolution daemon makes a request for work.  This
878 *		permits a large number of threads to be used by the daemon,
879 *		without using a lot of wired kernel memory when there are no
880 *		actual request outstanding.
881 */
882static int
883kauth_resolver_getwork(user_addr_t message)
884{
885	struct kauth_resolver_work *workp;
886	int		error;
887
888	KAUTH_RESOLVER_LOCK();
889	error = 0;
890	while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) {
891		thread_t thread = current_thread();
892		struct uthread *ut = get_bsdthread_info(thread);
893
894		ut->uu_kevent.uu_kauth.message = message;
895		error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue);
896		KAUTH_RESOLVER_UNLOCK();
897		/*
898		 * If this is a wakeup from another thread in the resolver
899		 * deregistering it, error out the request-for-work thread
900		 */
901		if (!kauth_resolver_identity)
902			error = EIO;
903		return(error);
904	}
905	return kauth_resolver_getwork2(message);
906}
907
908
909/*
910 * kauth_resolver_complete
911 *
912 * Description:	Return a result from userspace.
913 *
914 * Parameters:	message				message from user space
915 *
916 * Returns:	0				Success
917 *		EIO				The resolver is dead
918 *	copyin:EFAULT				Bad message from user space
919 */
920static int
921kauth_resolver_complete(user_addr_t message)
922{
923	struct kauth_identity_extlookup	extl;
924	struct kauth_resolver_work *workp;
925	struct kauth_resolver_work *killp;
926	int error, result;
927
928	/*
929	 * Copy in the mesage, including the extension field, since we are
930	 * copying into a local variable.
931	 */
932	if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
933		KAUTH_DEBUG("RESOLVER - error getting completed work\n");
934		return(error);
935	}
936
937	KAUTH_RESOLVER_LOCK();
938
939	error = 0;
940	result = 0;
941	switch (extl.el_result) {
942	case KAUTH_EXTLOOKUP_INPROG:
943	{
944		static int once = 0;
945
946		/* XXX this should go away once memberd is updated */
947		if (!once) {
948			printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
949			once = 1;
950		}
951	}
952	/* FALLTHROUGH */
953
954	case KAUTH_EXTLOOKUP_SUCCESS:
955		break;
956
957	case KAUTH_EXTLOOKUP_FATAL:
958		/* fatal error means the resolver is dead */
959		KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity);
960		/*
961		 * Terminate outstanding requests; without an authoritative
962		 * resolver, we are now back on our own authority.  Tag the
963		 * resolver unregistered to prevent kauth_cred_ismember_gid()
964		 * enqueueing more work until a new one is registered.  This
965		 * mitigates the damage a crashing resolver may inflict.
966		 */
967		kauth_resolver_identity = 0;
968		kauth_resolver_registered = 0;
969
970		TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
971		    wakeup(killp);
972		TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
973		    wakeup(killp);
974		/* Cause all waiting-for-work threads to return EIO */
975		wakeup((caddr_t)&kauth_resolver_unsubmitted);
976		/* and return EIO to the caller */
977		error = EIO;
978		break;
979
980	case KAUTH_EXTLOOKUP_BADRQ:
981		KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno);
982		result = EINVAL;
983		break;
984
985	case KAUTH_EXTLOOKUP_FAILURE:
986		KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno);
987		result = EIO;
988		break;
989
990	default:
991		KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result);
992		result = EIO;
993		break;
994	}
995
996	/*
997	 * In the case of a fatal error, we assume that the resolver will
998	 * restart quickly and re-collect all of the outstanding requests.
999	 * Thus, we don't complete the request which returned the fatal
1000	 * error status.
1001	 */
1002	if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
1003		/* scan our list for this request */
1004		TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
1005			/* found it? */
1006			if (workp->kr_seqno == extl.el_seqno) {
1007
1008				/*
1009				 * Get the request of the submitted queue so
1010				 * that it is not cleaned up out from under
1011				 * us by a timeout.
1012				 */
1013				TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
1014				workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
1015				workp->kr_flags |= KAUTH_REQUEST_DONE;
1016				workp->kr_result = result;
1017
1018				/* Copy the result message to the work item. */
1019				memcpy(&workp->kr_work, &extl, sizeof(struct kauth_identity_extlookup));
1020
1021				/*
1022				 * Check if we have a result in the extension
1023				 * field; if we do, then we need to separately
1024				 * copy the data from the message el_extend
1025				 * into the request buffer that's in the work
1026				 * item.  We have to do it here because we do
1027				 * not want to wake up the waiter until the
1028				 * data is in their buffer, and because the
1029				 * actual request response may be destroyed
1030				 * by the time the requester wakes up, and they
1031				 * do not have access to the user space buffer
1032				 * address.
1033				 *
1034				 * It is safe to drop and reacquire the lock
1035				 * here because we've already removed the item
1036				 * from the submission queue, but have not yet
1037				 * moved it to the completion queue.  Note that
1038				 * near simultaneous requests may result in
1039				 * duplication of requests for items in this
1040				 * window. This should not be a performance
1041				 * issue and is easily detectable by comparing
1042				 * time to live on last response vs. time of
1043				 * next request in the resolver logs.
1044				 */
1045				if (extl.el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM|KAUTH_EXTLOOKUP_VALID_GRNAM)) {
1046					size_t actual;	/* notused */
1047
1048					KAUTH_RESOLVER_UNLOCK();
1049					error = copyinstr(extl.el_extend, CAST_DOWN(void *, workp->kr_extend), MAXPATHLEN, &actual);
1050					KAUTH_RESOLVER_LOCK();
1051				}
1052
1053				/*
1054				 * Move the completed work item to the
1055				 * completion queue and wake up requester(s)
1056				 */
1057				TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
1058				wakeup(workp);
1059				break;
1060			}
1061		}
1062	}
1063	/*
1064	 * Note that it's OK for us not to find anything; if the request has
1065	 * timed out the work record will be gone.
1066	 */
1067	KAUTH_RESOLVER_UNLOCK();
1068
1069	return(error);
1070}
1071#endif /* CONFIG_EXT_RESOLVER */
1072
1073
1074/*
1075 * Identity cache.
1076 */
1077
1078#define	KI_VALID_UID	(1<<0)		/* UID and GID are mutually exclusive */
1079#define KI_VALID_GID	(1<<1)
1080#define KI_VALID_GUID	(1<<2)
1081#define KI_VALID_NTSID	(1<<3)
1082#define KI_VALID_PWNAM	(1<<4)	/* Used for translation */
1083#define KI_VALID_GRNAM	(1<<5)	/* Used for translation */
1084#define KI_VALID_GROUPS (1<<6)
1085
1086#if CONFIG_EXT_RESOLVER
1087/*
1088 * kauth_identity_init
1089 *
1090 * Description:	Initialize the kernel side of the credential identity resolver
1091 *
1092 * Parameters:	(void)
1093 *
1094 * Returns:	(void)
1095 *
1096 * Notes:	Initialize the credential identity resolver for use; the
1097 *		credential identity resolver is the KPI used to communicate
1098 *		with a user space credential identity resolver daemon.
1099 *
1100 *		This function is called from kauth_init() in the file
1101 *		kern_authorization.c.
1102 */
1103void
1104kauth_identity_init(void)
1105{
1106	TAILQ_INIT(&kauth_identities);
1107	kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
1108}
1109
1110
1111/*
1112 * kauth_identity_alloc
1113 *
1114 * Description:	Allocate and fill out a kauth_identity structure for
1115 *		translation between {UID|GID}/GUID/NTSID
1116 *
1117 * Parameters:	uid
1118 *
1119 * Returns:	NULL				Insufficient memory to satisfy
1120 *						the request
1121 *		!NULL				A pointer to the allocated
1122 *						structure, filled in
1123 *
1124 * Notes:	It is illegal to translate between UID and GID; any given UUID
1125 *		or NTSID can only refer to an NTSID or UUID (respectively),
1126 *		and *either* a UID *or* a GID, but not both.
1127 */
1128static struct kauth_identity *
1129kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
1130	ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry,
1131	const char *name, int nametype)
1132{
1133	struct kauth_identity *kip;
1134
1135	/* get and fill in a new identity */
1136	MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO);
1137	if (kip != NULL) {
1138		if (gid != KAUTH_GID_NONE) {
1139			kip->ki_gid = gid;
1140			kip->ki_valid = KI_VALID_GID;
1141		}
1142		if (uid != KAUTH_UID_NONE) {
1143			if (kip->ki_valid & KI_VALID_GID)
1144				panic("can't allocate kauth identity with both uid and gid");
1145			kip->ki_uid = uid;
1146			kip->ki_valid = KI_VALID_UID;
1147		}
1148		if (supgrpcnt) {
1149			assert(supgrpcnt <= NGROUPS);
1150			assert(supgrps != NULL);
1151			if (kip->ki_valid & KI_VALID_GID)
1152				panic("can't allocate kauth identity with both gid and supplementary groups");
1153			kip->ki_supgrpcnt = supgrpcnt;
1154			memcpy(kip->ki_supgrps, supgrps, sizeof(supgrps[0]) * supgrpcnt);
1155			kip->ki_valid |= KI_VALID_GROUPS;
1156		}
1157		kip->ki_groups_expiry = groups_expiry;
1158		if (guidp != NULL) {
1159			kip->ki_guid = *guidp;
1160			kip->ki_valid |= KI_VALID_GUID;
1161		}
1162		kip->ki_guid_expiry = guid_expiry;
1163		if (ntsidp != NULL) {
1164			kip->ki_ntsid = *ntsidp;
1165			kip->ki_valid |= KI_VALID_NTSID;
1166		}
1167		kip->ki_ntsid_expiry = ntsid_expiry;
1168		if (name != NULL) {
1169			kip->ki_name = name;
1170			kip->ki_valid |= nametype;
1171		}
1172	}
1173	return(kip);
1174}
1175
1176
1177/*
1178 * kauth_identity_register_and_free
1179 *
1180 * Description:	Register an association between identity tokens.  The passed
1181 *		'kip' is consumed by this function.
1182 *
1183 * Parameters:	kip				Pointer to kauth_identity
1184 *						structure to register
1185 *
1186 * Returns:	(void)
1187 *
1188 * Notes:	The memory pointer to by 'kip' is assumed to have been
1189 *		previously allocated via kauth_identity_alloc().
1190 */
1191static void
1192kauth_identity_register_and_free(struct kauth_identity *kip)
1193{
1194	struct kauth_identity *ip;
1195
1196	/*
1197	 * We search the cache for the UID listed in the incoming association.
1198	 * If we already have an entry, the new information is merged.
1199	 */
1200	ip = NULL;
1201	KAUTH_IDENTITY_LOCK();
1202	if (kip->ki_valid & KI_VALID_UID) {
1203		if (kip->ki_valid & KI_VALID_GID)
1204			panic("kauth_identity: can't insert record with both UID and GID as key");
1205		TAILQ_FOREACH(ip, &kauth_identities, ki_link)
1206		    if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid))
1207				break;
1208	} else if (kip->ki_valid & KI_VALID_GID) {
1209		TAILQ_FOREACH(ip, &kauth_identities, ki_link)
1210		    if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid))
1211				break;
1212	} else {
1213		panic("kauth_identity: can't insert record without UID or GID as key");
1214	}
1215
1216	if (ip != NULL) {
1217		/* we already have an entry, merge/overwrite */
1218		if (kip->ki_valid & KI_VALID_GUID) {
1219			ip->ki_guid = kip->ki_guid;
1220			ip->ki_valid |= KI_VALID_GUID;
1221		}
1222		ip->ki_guid_expiry = kip->ki_guid_expiry;
1223		if (kip->ki_valid & KI_VALID_NTSID) {
1224			ip->ki_ntsid = kip->ki_ntsid;
1225			ip->ki_valid |= KI_VALID_NTSID;
1226		}
1227		ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
1228		/* a valid ki_name field overwrites the previous name field */
1229		if (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)) {
1230			/* if there's an old one, discard it */
1231			const char *oname = NULL;
1232			if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))
1233				oname = ip->ki_name;
1234			ip->ki_name = kip->ki_name;
1235			kip->ki_name = oname;
1236		}
1237		/* and discard the incoming entry */
1238		ip = kip;
1239	} else {
1240		/*
1241		 * if we don't have any information on this identity, add it;
1242		 * if it pushes us over our limit, discard the oldest one.
1243		 */
1244		TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
1245		if (++kauth_identity_count > kauth_identity_cachemax) {
1246			ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
1247			TAILQ_REMOVE(&kauth_identities, ip, ki_link);
1248			kauth_identity_count--;
1249		}
1250	}
1251	KAUTH_IDENTITY_UNLOCK();
1252	/* have to drop lock before freeing expired entry (it may be in use) */
1253	if (ip != NULL) {
1254		/* if the ki_name field is used, clear it first */
1255		if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))
1256			vfs_removename(ip->ki_name);
1257		/* free the expired entry */
1258		FREE(ip, M_KAUTH);
1259	}
1260}
1261
1262
1263/*
1264 * kauth_identity_updatecache
1265 *
1266 * Description:	Given a lookup result, add any associations that we don't
1267 *		currently have; replace ones which have changed.
1268 *
1269 * Parameters:	elp				External lookup result from
1270 *						user space daemon to kernel
1271 *		rkip				pointer to returned kauth
1272 *						identity, or NULL
1273 *		extend_data			Extended data (can vary)
1274 *
1275 * Returns:	(void)
1276 *
1277 * Implicit returns:
1278 *		*rkip				Modified (if non-NULL)
1279 *
1280 * Notes:	For extended information requests, this code relies on the fact
1281 *		that elp->el_flags is never used as an rvalue, and is only
1282 *		ever bit-tested for valid lookup information we are willing
1283 *		to cache.
1284 *
1285 * XXX:		We may have to do the same in the case that extended data was
1286 *		passed out to user space to ensure that the request string
1287 *		gets cached; we may also be able to use the rkip as an
1288 *		input to avoid this.  The jury is still out.
1289 *
1290 * XXX:		This codes performance could be improved for multiple valid
1291 *		results by combining the loop iteration in a single loop.
1292 */
1293static void
1294kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip, uint64_t extend_data)
1295{
1296	struct timeval tv;
1297	struct kauth_identity *kip;
1298	const char *speculative_name = NULL;
1299
1300	microuptime(&tv);
1301
1302	/*
1303	 * If there is extended data, and that data represents a name rather
1304	 * than something else, speculatively create an entry for it in the
1305	 * string cache.  We do this to avoid holding the KAUTH_IDENTITY_LOCK
1306	 * over the allocation later.
1307	 */
1308	if (elp->el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) {
1309		const char *tmp = CAST_DOWN(const char *,extend_data);
1310		speculative_name = vfs_addname(tmp, strnlen(tmp, MAXPATHLEN - 1), 0, 0);
1311	}
1312
1313	/* user identity? */
1314	if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
1315		KAUTH_IDENTITY_LOCK();
1316		TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1317			/* matching record */
1318			if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
1319				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) {
1320					assert(elp->el_sup_grp_cnt <= NGROUPS);
1321					kip->ki_supgrpcnt = elp->el_sup_grp_cnt;
1322					memcpy(kip->ki_supgrps, elp->el_sup_groups, sizeof(elp->el_sup_groups[0]) * kip->ki_supgrpcnt);
1323					kip->ki_valid |= KI_VALID_GROUPS;
1324					kip->ki_groups_expiry = (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0;
1325				}
1326				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
1327					kip->ki_guid = elp->el_uguid;
1328					kip->ki_valid |= KI_VALID_GUID;
1329				}
1330				kip->ki_guid_expiry = (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0;
1331				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
1332					kip->ki_ntsid = elp->el_usid;
1333					kip->ki_valid |= KI_VALID_NTSID;
1334				}
1335				kip->ki_ntsid_expiry = (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0;
1336				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) {
1337					const char *oname = kip->ki_name;
1338					kip->ki_name = speculative_name;
1339					speculative_name = NULL;
1340					kip->ki_valid |= KI_VALID_PWNAM;
1341					if (oname) {
1342						/*
1343						 * free oname (if any) outside
1344						 * the lock
1345						 */
1346						speculative_name = oname;
1347					}
1348				}
1349				kauth_identity_lru(kip);
1350				if (rkip != NULL)
1351					*rkip = *kip;
1352				KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1353				break;
1354			}
1355		}
1356		KAUTH_IDENTITY_UNLOCK();
1357		/* not found in cache, add new record */
1358		if (kip == NULL) {
1359			kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
1360			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
1361			    (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0,
1362			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
1363			    (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0,
1364			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0,
1365			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL,
1366			    (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0,
1367			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) ? speculative_name : NULL,
1368			    KI_VALID_PWNAM);
1369			if (kip != NULL) {
1370				if (rkip != NULL)
1371					*rkip = *kip;
1372				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM)
1373					speculative_name = NULL;
1374				KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1375				kauth_identity_register_and_free(kip);
1376			}
1377		}
1378	}
1379
1380	/* group identity? (ignore, if we already processed it as a user) */
1381	if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID && !(elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID)) {
1382		KAUTH_IDENTITY_LOCK();
1383		TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1384			/* matching record */
1385			if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) {
1386				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) {
1387					kip->ki_guid = elp->el_gguid;
1388					kip->ki_valid |= KI_VALID_GUID;
1389				}
1390				kip->ki_guid_expiry = (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0;
1391				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
1392					kip->ki_ntsid = elp->el_gsid;
1393					kip->ki_valid |= KI_VALID_NTSID;
1394				}
1395				kip->ki_ntsid_expiry = (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0;
1396				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) {
1397					const char *oname = kip->ki_name;
1398					kip->ki_name = speculative_name;
1399					speculative_name = NULL;
1400					kip->ki_valid |= KI_VALID_GRNAM;
1401					if (oname) {
1402						/*
1403						 * free oname (if any) outside
1404						 * the lock
1405						 */
1406						speculative_name = oname;
1407					}
1408				}
1409				kauth_identity_lru(kip);
1410				if (rkip != NULL)
1411					*rkip = *kip;
1412				KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1413				break;
1414			}
1415		}
1416		KAUTH_IDENTITY_UNLOCK();
1417		/* not found in cache, add new record */
1418		if (kip == NULL) {
1419			kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
1420			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
1421			    (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0,
1422			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
1423			    (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0,
1424			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0,
1425			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL,
1426			    (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0,
1427			    (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) ? speculative_name : NULL,
1428			    KI_VALID_GRNAM);
1429			if (kip != NULL) {
1430				if (rkip != NULL)
1431					*rkip = *kip;
1432				if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM)
1433					speculative_name = NULL;
1434				KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1435				kauth_identity_register_and_free(kip);
1436			}
1437		}
1438	}
1439
1440	/* If we have a name reference to drop, drop it here */
1441	if (speculative_name != NULL) {
1442		vfs_removename(speculative_name);
1443	}
1444}
1445
1446
1447/*
1448 * Trim older entries from the identity cache.
1449 *
1450 * Must be called with the identity cache lock held.
1451 */
1452static void
1453kauth_identity_trimcache(int newsize) {
1454	struct kauth_identity 		*kip;
1455
1456	lck_mtx_assert(kauth_identity_mtx, LCK_MTX_ASSERT_OWNED);
1457
1458	while (kauth_identity_count > newsize) {
1459		kip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
1460		TAILQ_REMOVE(&kauth_identities, kip, ki_link);
1461		kauth_identity_count--;
1462		FREE(kip, M_KAUTH);
1463	}
1464}
1465
1466/*
1467 * kauth_identity_lru
1468 *
1469 * Description:	Promote the entry to the head of the LRU, assumes the cache
1470 *		is locked.
1471 *
1472 * Parameters:	kip				kauth identity to move to the
1473 *						head of the LRU list, if it's
1474 *						not already there
1475 *
1476 * Returns:	(void)
1477 *
1478 * Notes:	This is called even if the entry has expired; typically an
1479 *		expired entry that's been looked up is about to be revalidated,
1480 *		and having it closer to the head of the LRU means finding it
1481 *		quickly again when the revalidation comes through.
1482 */
1483static void
1484kauth_identity_lru(struct kauth_identity *kip)
1485{
1486	if (kip != TAILQ_FIRST(&kauth_identities)) {
1487		TAILQ_REMOVE(&kauth_identities, kip, ki_link);
1488		TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
1489	}
1490}
1491
1492
1493/*
1494 * kauth_identity_guid_expired
1495 *
1496 * Description:	Handle lazy expiration of GUID translations.
1497 *
1498 * Parameters:	kip				kauth identity to check for
1499 *						GUID expiration
1500 *
1501 * Returns:	1				Expired
1502 *		0				Not expired
1503 */
1504static int
1505kauth_identity_guid_expired(struct kauth_identity *kip)
1506{
1507	struct timeval tv;
1508
1509	/*
1510	 * Expiration time of 0 means this entry is persistent.
1511	 */
1512	if (kip->ki_guid_expiry == 0)
1513		return (0);
1514
1515	microuptime(&tv);
1516	KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip->ki_guid_expiry, tv.tv_sec);
1517
1518	return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
1519}
1520
1521
1522/*
1523 * kauth_identity_ntsid_expired
1524 *
1525 * Description:	Handle lazy expiration of NTSID translations.
1526 *
1527 * Parameters:	kip				kauth identity to check for
1528 *						NTSID expiration
1529 *
1530 * Returns:	1				Expired
1531 *		0				Not expired
1532 */
1533static int
1534kauth_identity_ntsid_expired(struct kauth_identity *kip)
1535{
1536	struct timeval tv;
1537
1538	/*
1539	 * Expiration time of 0 means this entry is persistent.
1540	 */
1541	if (kip->ki_ntsid_expiry == 0)
1542		return (0);
1543
1544	microuptime(&tv);
1545	KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip->ki_ntsid_expiry, tv.tv_sec);
1546
1547	return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
1548}
1549
1550/*
1551 * kauth_identity_groups_expired
1552 *
1553 * Description:	Handle lazy expiration of supplemental group translations.
1554 *
1555 * Parameters:	kip				kauth identity to check for
1556 *						groups expiration
1557 *
1558 * Returns:	1				Expired
1559 *		0				Not expired
1560 */
1561static int
1562kauth_identity_groups_expired(struct kauth_identity *kip)
1563{
1564	struct timeval tv;
1565
1566	/*
1567	 * Expiration time of 0 means this entry is persistent.
1568	 */
1569	if (kip->ki_groups_expiry == 0)
1570		return (0);
1571
1572	microuptime(&tv);
1573	KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip->ki_groups_expiry, tv.tv_sec);
1574
1575	return((kip->ki_groups_expiry <= tv.tv_sec) ? 1 : 0);
1576}
1577
1578/*
1579 * kauth_identity_find_uid
1580 *
1581 * Description: Search for an entry by UID
1582 *
1583 * Parameters:	uid				UID to find
1584 *		kir				Pointer to return area
1585 *		getname				Name buffer, if ki_name wanted
1586 *
1587 * Returns:	0				Found
1588 *		ENOENT				Not found
1589 *
1590 * Implicit returns:
1591 *		*klr				Modified, if found
1592 */
1593static int
1594kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname)
1595{
1596	struct kauth_identity *kip;
1597
1598	KAUTH_IDENTITY_LOCK();
1599	TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1600		if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) {
1601			kauth_identity_lru(kip);
1602			/* Copy via structure assignment */
1603			*kir = *kip;
1604			/* If a name is wanted and one exists, copy it out */
1605			if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
1606				strlcpy(getname, kip->ki_name, MAXPATHLEN);
1607			break;
1608		}
1609	}
1610	KAUTH_IDENTITY_UNLOCK();
1611	return((kip == NULL) ? ENOENT : 0);
1612}
1613
1614
1615/*
1616 * kauth_identity_find_gid
1617 *
1618 * Description: Search for an entry by GID
1619 *
1620 * Parameters:	gid				GID to find
1621 *		kir				Pointer to return area
1622 *		getname				Name buffer, if ki_name wanted
1623 *
1624 * Returns:	0				Found
1625 *		ENOENT				Not found
1626 *
1627 * Implicit returns:
1628 *		*klr				Modified, if found
1629 */
1630static int
1631kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir, char *getname)
1632{
1633	struct kauth_identity *kip;
1634
1635	KAUTH_IDENTITY_LOCK();
1636	TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1637		if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) {
1638			kauth_identity_lru(kip);
1639			/* Copy via structure assignment */
1640			*kir = *kip;
1641			/* If a name is wanted and one exists, copy it out */
1642			if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
1643				strlcpy(getname, kip->ki_name, MAXPATHLEN);
1644			break;
1645		}
1646	}
1647	KAUTH_IDENTITY_UNLOCK();
1648	return((kip == NULL) ? ENOENT : 0);
1649}
1650
1651
1652/*
1653 * kauth_identity_find_guid
1654 *
1655 * Description: Search for an entry by GUID
1656 *
1657 * Parameters:	guidp				Pointer to GUID to find
1658 *		kir				Pointer to return area
1659 *		getname				Name buffer, if ki_name wanted
1660 *
1661 * Returns:	0				Found
1662 *		ENOENT				Not found
1663 *
1664 * Implicit returns:
1665 *		*klr				Modified, if found
1666 *
1667 * Note:	The association may be expired, in which case the caller
1668 *		may elect to call out to userland to revalidate.
1669 */
1670static int
1671kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname)
1672{
1673	struct kauth_identity *kip;
1674
1675	KAUTH_IDENTITY_LOCK();
1676	TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1677		if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
1678			kauth_identity_lru(kip);
1679			/* Copy via structure assignment */
1680			*kir = *kip;
1681			/* If a name is wanted and one exists, copy it out */
1682			if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
1683				strlcpy(getname, kip->ki_name, MAXPATHLEN);
1684			break;
1685		}
1686	}
1687	KAUTH_IDENTITY_UNLOCK();
1688	return((kip == NULL) ? ENOENT : 0);
1689}
1690
1691/*
1692 * kauth_identity_find_nam
1693 *
1694 * Description:	Search for an entry by name
1695 *
1696 * Parameters:	name				Pointer to name to find
1697 *		valid				KI_VALID_PWNAM or KI_VALID_GRNAM
1698 *		kir				Pointer to return area
1699 *
1700 * Returns:	0				Found
1701 *		ENOENT				Not found
1702 *
1703 * Implicit returns:
1704 *		*klr				Modified, if found
1705 */
1706static int
1707kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir)
1708{
1709	struct kauth_identity *kip;
1710
1711	KAUTH_IDENTITY_LOCK();
1712	TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1713		if ((kip->ki_valid & valid) && !strcmp(name, kip->ki_name)) {
1714			kauth_identity_lru(kip);
1715			/* Copy via structure assignment */
1716			*kir = *kip;
1717			break;
1718		}
1719	}
1720	KAUTH_IDENTITY_UNLOCK();
1721	return((kip == NULL) ? ENOENT : 0);
1722}
1723
1724
1725/*
1726 * kauth_identity_find_ntsid
1727 *
1728 * Description: Search for an entry by NTSID
1729 *
1730 * Parameters:	ntsid				Pointer to NTSID to find
1731 *		kir				Pointer to return area
1732 *		getname				Name buffer, if ki_name wanted
1733 *
1734 * Returns:	0				Found
1735 *		ENOENT				Not found
1736 *
1737 * Implicit returns:
1738 *		*klr				Modified, if found
1739 *
1740 * Note:	The association may be expired, in which case the caller
1741 *		may elect to call out to userland to revalidate.
1742 */
1743static int
1744kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname)
1745{
1746	struct kauth_identity *kip;
1747
1748	KAUTH_IDENTITY_LOCK();
1749	TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1750		if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) {
1751			kauth_identity_lru(kip);
1752			/* Copy via structure assignment */
1753			*kir = *kip;
1754			/* If a name is wanted and one exists, copy it out */
1755			if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)))
1756				strlcpy(getname, kip->ki_name, MAXPATHLEN);
1757			break;
1758		}
1759	}
1760	KAUTH_IDENTITY_UNLOCK();
1761	return((kip == NULL) ? ENOENT : 0);
1762}
1763#endif	/* CONFIG_EXT_RESOLVER */
1764
1765
1766/*
1767 * GUID handling.
1768 */
1769guid_t kauth_null_guid;
1770
1771
1772/*
1773 * kauth_guid_equal
1774 *
1775 * Description:	Determine the equality of two GUIDs
1776 *
1777 * Parameters:	guid1				Pointer to first GUID
1778 *		guid2				Pointer to second GUID
1779 *
1780 * Returns:	0				If GUIDs are unequal
1781 *		!0				If GUIDs are equal
1782 */
1783int
1784kauth_guid_equal(guid_t *guid1, guid_t *guid2)
1785{
1786	return(bcmp(guid1, guid2, sizeof(*guid1)) == 0);
1787}
1788
1789
1790/*
1791 * kauth_wellknown_guid
1792 *
1793 * Description:	Determine if a GUID is a well-known GUID
1794 *
1795 * Parameters:	guid				Pointer to GUID to check
1796 *
1797 * Returns:	KAUTH_WKG_NOT			Not a well known GUID
1798 *		KAUTH_WKG_EVERYBODY		"Everybody"
1799 *		KAUTH_WKG_NOBODY		"Nobody"
1800 *		KAUTH_WKG_OWNER			"Other"
1801 *		KAUTH_WKG_GROUP			"Group"
1802 */
1803int
1804kauth_wellknown_guid(guid_t *guid)
1805{
1806	static char	fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1807	uint32_t		code;
1808	/*
1809	 * All WKGs begin with the same 12 bytes.
1810	 */
1811	if (bcmp((void *)guid, fingerprint, 12) == 0) {
1812		/*
1813		 * The final 4 bytes are our code (in network byte order).
1814		 */
1815		code = OSSwapHostToBigInt32(*(uint32_t *)&guid->g_guid[12]);
1816		switch(code) {
1817		case 0x0000000c:
1818			return(KAUTH_WKG_EVERYBODY);
1819		case 0xfffffffe:
1820			return(KAUTH_WKG_NOBODY);
1821		case 0x0000000a:
1822			return(KAUTH_WKG_OWNER);
1823		case 0x00000010:
1824			return(KAUTH_WKG_GROUP);
1825		}
1826	}
1827	return(KAUTH_WKG_NOT);
1828}
1829
1830
1831/*
1832 * kauth_ntsid_equal
1833 *
1834 * Description:	Determine the equality of two NTSIDs (NT Security Identifiers)
1835 *
1836 * Parameters:	sid1				Pointer to first NTSID
1837 *		sid2				Pointer to second NTSID
1838 *
1839 * Returns:	0				If GUIDs are unequal
1840 *		!0				If GUIDs are equal
1841 */
1842int
1843kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
1844{
1845	/* check sizes for equality, also sanity-check size while we're at it */
1846	if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) &&
1847	    (KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) &&
1848	    bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)) == 0)
1849		return(1);
1850	return(0);
1851}
1852
1853
1854/*
1855 * Identity KPI
1856 *
1857 * We support four tokens representing identity:
1858 *  - Credential reference
1859 *  - UID
1860 *  - GUID
1861 *  - NT security identifier
1862 *
1863 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1864 * be done using it.
1865 */
1866
1867
1868
1869/*
1870 * kauth_cred_change_egid
1871 *
1872 * Description:	Set EGID by changing the first element of cr_groups for the
1873 *		passed credential; if the new EGID exists in the list of
1874 *		groups already, then rotate the old EGID into its position,
1875 *		otherwise replace it
1876 *
1877 * Parameters:	cred			Pointer to the credential to modify
1878 *		new_egid		The new EGID to set
1879 *
1880 * Returns:	0			The egid did not displace a member of
1881 *					the supplementary group list
1882 *		1			The egid being set displaced a member
1883 *					of the supplementary groups list
1884 *
1885 * Note:	Utility function; internal use only because of locking.
1886 *
1887 *		This function operates on the credential passed; the caller
1888 *		must operate either on a newly allocated credential (one for
1889 *		which there is no hash cache reference and no externally
1890 *		visible pointer reference), or a template credential.
1891 */
1892static int
1893kauth_cred_change_egid(kauth_cred_t cred, gid_t new_egid)
1894{
1895	int	i;
1896	int	displaced = 1;
1897#if radar_4600026
1898	int	is_member;
1899#endif	/* radar_4600026 */
1900	gid_t	old_egid = kauth_cred_getgid(cred);
1901	posix_cred_t pcred = posix_cred_get(cred);
1902
1903	/* Ignoring the first entry, scan for a match for the new egid */
1904	for (i = 1; i < pcred->cr_ngroups; i++) {
1905		/*
1906		 * If we find a match, swap them so we don't lose overall
1907		 * group information
1908		 */
1909		if (pcred->cr_groups[i] == new_egid) {
1910			pcred->cr_groups[i] = old_egid;
1911			DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1912			displaced = 0;
1913			break;
1914		}
1915	}
1916
1917#if radar_4600026
1918#error Fix radar 4600026 first!!!
1919
1920/*
1921This is correct for memberd behaviour, but incorrect for POSIX; to address
1922this, we would need to automatically opt-out any SUID/SGID binary, and force
1923it to use initgroups to opt back in.  We take the approach of considering it
1924opt'ed out in any group of 16 displacement instead, since it's a much more
1925conservative approach (i.e. less likely to cause things to break).
1926*/
1927
1928	/*
1929	 * If we displaced a member of the supplementary groups list of the
1930	 * credential, and we have not opted out of memberd, then if memberd
1931	 * says that the credential is a member of the group, then it has not
1932	 * actually been displaced.
1933	 *
1934	 * NB:	This is typically a cold code path.
1935	 */
1936	if (displaced && !(pcred->cr_flags & CRF_NOMEMBERD) &&
1937	    kauth_cred_ismember_gid(cred, new_egid, &is_member) == 0 &&
1938	    is_member) {
1939	    	displaced = 0;
1940		DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1941	}
1942#endif	/* radar_4600026 */
1943
1944	/* set the new EGID into the old spot */
1945	pcred->cr_groups[0] = new_egid;
1946
1947	return (displaced);
1948}
1949
1950
1951/*
1952 * kauth_cred_getuid
1953 *
1954 * Description:	Fetch UID from credential
1955 *
1956 * Parameters:	cred				Credential to examine
1957 *
1958 * Returns:	(uid_t)				UID associated with credential
1959 */
1960uid_t
1961kauth_cred_getuid(kauth_cred_t cred)
1962{
1963	NULLCRED_CHECK(cred);
1964	return(posix_cred_get(cred)->cr_uid);
1965}
1966
1967
1968/*
1969 * kauth_cred_getruid
1970 *
1971 * Description:	Fetch RUID from credential
1972 *
1973 * Parameters:	cred				Credential to examine
1974 *
1975 * Returns:	(uid_t)				RUID associated with credential
1976 */
1977uid_t
1978kauth_cred_getruid(kauth_cred_t cred)
1979{
1980	NULLCRED_CHECK(cred);
1981	return(posix_cred_get(cred)->cr_ruid);
1982}
1983
1984
1985/*
1986 * kauth_cred_getsvuid
1987 *
1988 * Description:	Fetch SVUID from credential
1989 *
1990 * Parameters:	cred				Credential to examine
1991 *
1992 * Returns:	(uid_t)				SVUID associated with credential
1993 */
1994uid_t
1995kauth_cred_getsvuid(kauth_cred_t cred)
1996{
1997	NULLCRED_CHECK(cred);
1998	return(posix_cred_get(cred)->cr_svuid);
1999}
2000
2001
2002/*
2003 * kauth_cred_getgid
2004 *
2005 * Description:	Fetch GID from credential
2006 *
2007 * Parameters:	cred				Credential to examine
2008 *
2009 * Returns:	(gid_t)				GID associated with credential
2010 */
2011gid_t
2012kauth_cred_getgid(kauth_cred_t cred)
2013{
2014	NULLCRED_CHECK(cred);
2015	return(posix_cred_get(cred)->cr_gid);
2016}
2017
2018
2019/*
2020 * kauth_cred_getrgid
2021 *
2022 * Description:	Fetch RGID from credential
2023 *
2024 * Parameters:	cred				Credential to examine
2025 *
2026 * Returns:	(gid_t)				RGID associated with credential
2027 */
2028gid_t
2029kauth_cred_getrgid(kauth_cred_t cred)
2030{
2031	NULLCRED_CHECK(cred);
2032	return(posix_cred_get(cred)->cr_rgid);
2033}
2034
2035
2036/*
2037 * kauth_cred_getsvgid
2038 *
2039 * Description:	Fetch SVGID from credential
2040 *
2041 * Parameters:	cred				Credential to examine
2042 *
2043 * Returns:	(gid_t)				SVGID associated with credential
2044 */
2045gid_t
2046kauth_cred_getsvgid(kauth_cred_t cred)
2047{
2048	NULLCRED_CHECK(cred);
2049	return(posix_cred_get(cred)->cr_svgid);
2050}
2051
2052
2053static int	kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
2054
2055#if CONFIG_EXT_RESOLVER == 0
2056/*
2057 * If there's no resolver, short-circuit the kauth_cred_x2y() lookups.
2058 */
2059static __inline int
2060kauth_cred_cache_lookup(__unused int from, __unused int to,
2061	__unused void *src, __unused void *dst)
2062{
2063	return (EWOULDBLOCK);
2064
2065}
2066#endif
2067
2068#if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2069/*
2070 * Structure to hold supplemental groups. Used for impedance matching with
2071 * kauth_cred_cache_lookup below.
2072 */
2073struct supgroups {
2074	int *count;
2075	gid_t *groups;
2076};
2077
2078/*
2079 * kauth_cred_uid2groups
2080 *
2081 * Description:	Fetch supplemental GROUPS from UID
2082 *
2083 * Parameters:	uid				UID to examine
2084 *		groups				pointer to an array of gid_ts
2085 *		gcount				pointer to the number of groups wanted/returned
2086 *
2087 * Returns:	0				Success
2088 *	kauth_cred_cache_lookup:EINVAL
2089 *
2090 * Implicit returns:
2091 *		*groups				Modified, if successful
2092 *		*gcount				Modified, if successful
2093 *
2094 */
2095static int
2096kauth_cred_uid2groups(uid_t *uid, gid_t *groups, int *gcount)
2097{
2098	int rv;
2099
2100	struct supgroups supgroups;
2101	supgroups.count = gcount;
2102	supgroups.groups = groups;
2103
2104	rv = kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GROUPS, uid, &supgroups);
2105
2106	return (rv);
2107}
2108#endif
2109
2110/*
2111 * kauth_cred_guid2pwnam
2112 *
2113 * Description:	Fetch PWNAM from GUID
2114 *
2115 * Parameters:	guidp				Pointer to GUID to examine
2116 *		pwnam				Pointer to user@domain buffer
2117 *
2118 * Returns:	0				Success
2119 *	kauth_cred_cache_lookup:EINVAL
2120 *
2121 * Implicit returns:
2122 *		*pwnam				Modified, if successful
2123 *
2124 * Notes:	pwnam is assumed to point to a buffer of MAXPATHLEN in size
2125 */
2126int
2127kauth_cred_guid2pwnam(guid_t *guidp, char *pwnam)
2128{
2129	return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_PWNAM, guidp, pwnam));
2130}
2131
2132
2133/*
2134 * kauth_cred_guid2grnam
2135 *
2136 * Description:	Fetch GRNAM from GUID
2137 *
2138 * Parameters:	guidp				Pointer to GUID to examine
2139 *		grnam				Pointer to group@domain buffer
2140 *
2141 * Returns:	0				Success
2142 *	kauth_cred_cache_lookup:EINVAL
2143 *
2144 * Implicit returns:
2145 *		*grnam				Modified, if successful
2146 *
2147 * Notes:	grnam is assumed to point to a buffer of MAXPATHLEN in size
2148 */
2149int
2150kauth_cred_guid2grnam(guid_t *guidp, char *grnam)
2151{
2152	return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GRNAM, guidp, grnam));
2153}
2154
2155
2156/*
2157 * kauth_cred_pwnam2guid
2158 *
2159 * Description:	Fetch PWNAM from GUID
2160 *
2161 * Parameters:	pwnam				String containing user@domain
2162 *		guidp				Pointer to buffer for GUID
2163 *
2164 * Returns:	0				Success
2165 *	kauth_cred_cache_lookup:EINVAL
2166 *
2167 * Implicit returns:
2168 *		*guidp				Modified, if successful
2169 *
2170 * Notes:	pwnam should not point to a request larger than MAXPATHLEN
2171 *		bytes in size, including the NUL termination of the string.
2172 */
2173int
2174kauth_cred_pwnam2guid(char *pwnam, guid_t *guidp)
2175{
2176	return(kauth_cred_cache_lookup(KI_VALID_PWNAM, KI_VALID_GUID, pwnam, guidp));
2177}
2178
2179
2180/*
2181 * kauth_cred_grnam2guid
2182 *
2183 * Description:	Fetch GRNAM from GUID
2184 *
2185 * Parameters:	grnam				String containing group@domain
2186 *		guidp				Pointer to buffer for GUID
2187 *
2188 * Returns:	0				Success
2189 *	kauth_cred_cache_lookup:EINVAL
2190 *
2191 * Implicit returns:
2192 *		*guidp				Modified, if successful
2193 *
2194 * Notes:	grnam should not point to a request larger than MAXPATHLEN
2195 *		bytes in size, including the NUL termination of the string.
2196 */
2197int
2198kauth_cred_grnam2guid(char *grnam, guid_t *guidp)
2199{
2200	return(kauth_cred_cache_lookup(KI_VALID_GRNAM, KI_VALID_GUID, grnam, guidp));
2201}
2202
2203
2204/*
2205 * kauth_cred_guid2uid
2206 *
2207 * Description:	Fetch UID from GUID
2208 *
2209 * Parameters:	guidp				Pointer to GUID to examine
2210 *		uidp				Pointer to buffer for UID
2211 *
2212 * Returns:	0				Success
2213 *	kauth_cred_cache_lookup:EINVAL
2214 *
2215 * Implicit returns:
2216 *		*uidp				Modified, if successful
2217 */
2218int
2219kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
2220{
2221	return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
2222}
2223
2224
2225/*
2226 * kauth_cred_guid2gid
2227 *
2228 * Description:	Fetch GID from GUID
2229 *
2230 * Parameters:	guidp				Pointer to GUID to examine
2231 *		gidp				Pointer to buffer for GID
2232 *
2233 * Returns:	0				Success
2234 *	kauth_cred_cache_lookup:EINVAL
2235 *
2236 * Implicit returns:
2237 *		*gidp				Modified, if successful
2238 */
2239int
2240kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
2241{
2242	return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
2243}
2244
2245
2246/*
2247 * kauth_cred_ntsid2uid
2248 *
2249 * Description:	Fetch UID from NTSID
2250 *
2251 * Parameters:	sidp				Pointer to NTSID to examine
2252 *		uidp				Pointer to buffer for UID
2253 *
2254 * Returns:	0				Success
2255 *	kauth_cred_cache_lookup:EINVAL
2256 *
2257 * Implicit returns:
2258 *		*uidp				Modified, if successful
2259 */
2260int
2261kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
2262{
2263	return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
2264}
2265
2266
2267/*
2268 * kauth_cred_ntsid2gid
2269 *
2270 * Description:	Fetch GID from NTSID
2271 *
2272 * Parameters:	sidp				Pointer to NTSID to examine
2273 *		gidp				Pointer to buffer for GID
2274 *
2275 * Returns:	0				Success
2276 *	kauth_cred_cache_lookup:EINVAL
2277 *
2278 * Implicit returns:
2279 *		*gidp				Modified, if successful
2280 */
2281int
2282kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
2283{
2284	return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
2285}
2286
2287
2288/*
2289 * kauth_cred_ntsid2guid
2290 *
2291 * Description:	Fetch GUID from NTSID
2292 *
2293 * Parameters:	sidp				Pointer to NTSID to examine
2294 *		guidp				Pointer to buffer for GUID
2295 *
2296 * Returns:	0				Success
2297 *	kauth_cred_cache_lookup:EINVAL
2298 *
2299 * Implicit returns:
2300 *		*guidp				Modified, if successful
2301 */
2302int
2303kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
2304{
2305	return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
2306}
2307
2308
2309/*
2310 * kauth_cred_uid2guid
2311 *
2312 * Description:	Fetch GUID from UID
2313 *
2314 * Parameters:	uid				UID to examine
2315 *		guidp				Pointer to buffer for GUID
2316 *
2317 * Returns:	0				Success
2318 *	kauth_cred_cache_lookup:EINVAL
2319 *
2320 * Implicit returns:
2321 *		*guidp				Modified, if successful
2322 */
2323int
2324kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
2325{
2326	return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
2327}
2328
2329
2330/*
2331 * kauth_cred_getguid
2332 *
2333 * Description:	Fetch GUID from credential
2334 *
2335 * Parameters:	cred				Credential to examine
2336 *		guidp				Pointer to buffer for GUID
2337 *
2338 * Returns:	0				Success
2339 *	kauth_cred_cache_lookup:EINVAL
2340 *
2341 * Implicit returns:
2342 *		*guidp				Modified, if successful
2343 */
2344int
2345kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
2346{
2347	NULLCRED_CHECK(cred);
2348	return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
2349}
2350
2351
2352/*
2353 * kauth_cred_getguid
2354 *
2355 * Description:	Fetch GUID from GID
2356 *
2357 * Parameters:	gid				GID to examine
2358 *		guidp				Pointer to buffer for GUID
2359 *
2360 * Returns:	0				Success
2361 *	kauth_cred_cache_lookup:EINVAL
2362 *
2363 * Implicit returns:
2364 *		*guidp				Modified, if successful
2365 */
2366int
2367kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
2368{
2369	return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
2370}
2371
2372
2373/*
2374 * kauth_cred_uid2ntsid
2375 *
2376 * Description:	Fetch NTSID from UID
2377 *
2378 * Parameters:	uid				UID to examine
2379 *		sidp				Pointer to buffer for NTSID
2380 *
2381 * Returns:	0				Success
2382 *	kauth_cred_cache_lookup:EINVAL
2383 *
2384 * Implicit returns:
2385 *		*sidp				Modified, if successful
2386 */
2387int
2388kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
2389{
2390	return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
2391}
2392
2393
2394/*
2395 * kauth_cred_getntsid
2396 *
2397 * Description:	Fetch NTSID from credential
2398 *
2399 * Parameters:	cred				Credential to examine
2400 *		sidp				Pointer to buffer for NTSID
2401 *
2402 * Returns:	0				Success
2403 *	kauth_cred_cache_lookup:EINVAL
2404 *
2405 * Implicit returns:
2406 *		*sidp				Modified, if successful
2407 */
2408int
2409kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
2410{
2411	NULLCRED_CHECK(cred);
2412	return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
2413}
2414
2415
2416/*
2417 * kauth_cred_gid2ntsid
2418 *
2419 * Description:	Fetch NTSID from GID
2420 *
2421 * Parameters:	gid				GID to examine
2422 *		sidp				Pointer to buffer for NTSID
2423 *
2424 * Returns:	0				Success
2425 *	kauth_cred_cache_lookup:EINVAL
2426 *
2427 * Implicit returns:
2428 *		*sidp				Modified, if successful
2429 */
2430int
2431kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
2432{
2433	return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
2434}
2435
2436
2437/*
2438 * kauth_cred_guid2ntsid
2439 *
2440 * Description:	Fetch NTSID from GUID
2441 *
2442 * Parameters:	guidp				Pointer to GUID to examine
2443 *		sidp				Pointer to buffer for NTSID
2444 *
2445 * Returns:	0				Success
2446 *	kauth_cred_cache_lookup:EINVAL
2447 *
2448 * Implicit returns:
2449 *		*sidp				Modified, if successful
2450 */
2451int
2452kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
2453{
2454	return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
2455}
2456
2457
2458/*
2459 * kauth_cred_cache_lookup
2460 *
2461 * Description:	Lookup a translation in the cache; if one is not found, and
2462 *		the attempt was not fatal, submit the request to the resolver
2463 *		instead, and wait for it to complete or be aborted.
2464 *
2465 * Parameters:	from				Identity information we have
2466 *		to				Identity information we want
2467 *		src				Pointer to buffer containing
2468 *						the source identity
2469 *		dst				Pointer to buffer to receive
2470 *						the target identity
2471 *
2472 * Returns:	0				Success
2473 *		EINVAL				Unknown source identity type
2474 */
2475#if CONFIG_EXT_RESOLVER
2476static int
2477kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
2478{
2479	struct kauth_identity ki;
2480	struct kauth_identity_extlookup el;
2481	int error;
2482	uint64_t extend_data = 0ULL;
2483	int (* expired)(struct kauth_identity *kip);
2484	char *namebuf = NULL;
2485
2486	KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
2487
2488	/*
2489	 * Look for an existing cache entry for this association.
2490	 * If the entry has not expired, return the cached information.
2491	 * We do not cache user@domain translations here; they use too
2492	 * much memory to hold onto forever, and can not be updated
2493	 * atomically.
2494	 */
2495	if (to == KI_VALID_PWNAM || to == KI_VALID_GRNAM) {
2496		namebuf = dst;
2497	}
2498	ki.ki_valid = 0;
2499	switch(from) {
2500	case KI_VALID_UID:
2501		error = kauth_identity_find_uid(*(uid_t *)src, &ki, namebuf);
2502		break;
2503	case KI_VALID_GID:
2504		error = kauth_identity_find_gid(*(gid_t *)src, &ki, namebuf);
2505		break;
2506	case KI_VALID_GUID:
2507		error = kauth_identity_find_guid((guid_t *)src, &ki, namebuf);
2508		break;
2509	case KI_VALID_NTSID:
2510		error = kauth_identity_find_ntsid((ntsid_t *)src, &ki, namebuf);
2511		break;
2512	case KI_VALID_PWNAM:
2513	case KI_VALID_GRNAM:
2514		/* Names are unique in their 'from' space */
2515		error = kauth_identity_find_nam((char *)src, from, &ki);
2516		break;
2517	default:
2518		return(EINVAL);
2519	}
2520	/* lookup failure or error */
2521	if (error != 0) {
2522		/* any other error is fatal */
2523		if (error != ENOENT) {
2524			/* XXX bogus check - this is not possible */
2525			KAUTH_DEBUG("CACHE - cache search error %d", error);
2526			return(error);
2527		}
2528	} else {
2529		/* found a valid cached entry, check expiry */
2530		switch(to) {
2531		case KI_VALID_GUID:
2532			expired = kauth_identity_guid_expired;
2533			break;
2534		case KI_VALID_NTSID:
2535			expired = kauth_identity_ntsid_expired;
2536			break;
2537		case KI_VALID_GROUPS:
2538			expired = kauth_identity_groups_expired;
2539			break;
2540		default:
2541			switch(from) {
2542			case KI_VALID_GUID:
2543				expired = kauth_identity_guid_expired;
2544				break;
2545			case KI_VALID_NTSID:
2546				expired = kauth_identity_ntsid_expired;
2547				break;
2548			default:
2549				expired = NULL;
2550			}
2551		}
2552
2553		/*
2554		 * If no expiry function, or not expired, we have found
2555		 * a hit.
2556		 */
2557		if (expired) {
2558			if (!expired(&ki)) {
2559				KAUTH_DEBUG("CACHE - entry valid, unexpired");
2560				expired = NULL; /* must clear it is used as a flag */
2561			} else {
2562				/*
2563				 * We leave ki_valid set here; it contains a
2564				 * translation but the TTL has expired.  If we can't
2565				 * get a result from the resolver, we will use it as
2566				 * a better-than nothing alternative.
2567				 */
2568
2569				KAUTH_DEBUG("CACHE - expired entry found");
2570			}
2571		} else {
2572			KAUTH_DEBUG("CACHE - no expiry function");
2573		}
2574
2575		if (!expired) {
2576			/* do we have a translation? */
2577			if (ki.ki_valid & to) {
2578				KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki.ki_valid);
2579				DTRACE_PROC4(kauth__identity__cache__hit, int, from, int, to, void *, src, void *, dst);
2580				goto found;
2581			} else {
2582				/*
2583				 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2584				 * If we went looking for a translation from GUID or NTSID and
2585				 * found a translation that wasn't for our desired type, then
2586				 * don't bother calling the resolver. We know that this
2587				 * GUID/NTSID can't translate to our desired type.
2588				 */
2589				switch(from) {
2590				case KI_VALID_GUID:
2591				case KI_VALID_NTSID:
2592					switch(to) {
2593					case KI_VALID_GID:
2594						if ((ki.ki_valid & KI_VALID_UID)) {
2595							KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_GID);
2596							return (ENOENT);
2597						}
2598						break;
2599					case KI_VALID_UID:
2600						if ((ki.ki_valid & KI_VALID_GID)) {
2601							KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_UID);
2602							return (ENOENT);
2603						}
2604						break;
2605					}
2606					break;
2607				}
2608			}
2609		}
2610	}
2611
2612	/*
2613	 * We failed to find a cache entry; call the resolver.
2614	 *
2615	 * Note:	We ask for as much non-extended data as we can get,
2616	 *		and only provide (or ask for) extended information if
2617	 *		we have a 'from' (or 'to') which requires it.  This
2618	 *		way we don't pay for the extra transfer overhead for
2619	 *		data we don't need.
2620	 */
2621	bzero(&el, sizeof(el));
2622	el.el_info_pid = current_proc()->p_pid;
2623	switch(from) {
2624	case KI_VALID_UID:
2625		el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
2626		el.el_uid = *(uid_t *)src;
2627		break;
2628	case KI_VALID_GID:
2629		el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
2630		el.el_gid = *(gid_t *)src;
2631		break;
2632	case KI_VALID_GUID:
2633		el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
2634		el.el_uguid = *(guid_t *)src;
2635		el.el_gguid = *(guid_t *)src;
2636		break;
2637	case KI_VALID_NTSID:
2638		el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
2639		el.el_usid = *(ntsid_t *)src;
2640		el.el_gsid = *(ntsid_t *)src;
2641		break;
2642	case KI_VALID_PWNAM:
2643		/* extra overhead */
2644		el.el_flags = KAUTH_EXTLOOKUP_VALID_PWNAM;
2645		extend_data = CAST_USER_ADDR_T(src);
2646		break;
2647	case KI_VALID_GRNAM:
2648		/* extra overhead */
2649		el.el_flags = KAUTH_EXTLOOKUP_VALID_GRNAM;
2650		extend_data = CAST_USER_ADDR_T(src);
2651		break;
2652	default:
2653		return(EINVAL);
2654	}
2655	/*
2656	 * Here we ask for everything all at once, to avoid having to work
2657	 * out what we really want now, or might want soon.
2658	 *
2659	 * Asking for SID translations when we don't know we need them right
2660	 * now is going to cause excess work to be done if we're connected
2661	 * to a network that thinks it can translate them.  This list needs
2662	 * to get smaller/smarter.
2663	 */
2664	el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
2665	    KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
2666	    KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
2667	if (to == KI_VALID_PWNAM) {
2668		/* extra overhead */
2669		el.el_flags |= KAUTH_EXTLOOKUP_WANT_PWNAM;
2670		extend_data = CAST_USER_ADDR_T(dst);
2671	}
2672	if (to == KI_VALID_GRNAM) {
2673		/* extra overhead */
2674		el.el_flags |= KAUTH_EXTLOOKUP_WANT_GRNAM;
2675		extend_data = CAST_USER_ADDR_T(dst);
2676	}
2677	if (to == KI_VALID_GROUPS) {
2678		/* Expensive and only useful for an NFS client not using kerberos */
2679		el.el_flags |= KAUTH_EXTLOOKUP_WANT_SUPGRPS;
2680		if (ki.ki_valid & KI_VALID_GROUPS) {
2681			/*
2682			 * Copy the current supplemental groups for the resolver.
2683			 * The resolver should check these groups first and if
2684			 * the user (uid) is still a member it should endeavor to
2685			 * keep them in the list. Otherwise NFS clients could get
2686			 * changing access to server file system objects on each
2687			 * expiration.
2688			 */
2689			el.el_sup_grp_cnt = ki.ki_supgrpcnt;
2690
2691			memcpy(el.el_sup_groups, ki.ki_supgrps, sizeof (el.el_sup_groups[0]) * ki.ki_supgrpcnt);
2692			/* Let the resolver know these were the previous valid groups */
2693			el.el_flags |= KAUTH_EXTLOOKUP_VALID_SUPGRPS;
2694			KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2695		} else
2696			KAUTH_DEBUG("GROUPS: no valid groups to send");
2697	}
2698
2699	/* Call resolver */
2700	KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
2701
2702	DTRACE_PROC3(kauth__id__resolver__submitted, int, from, int, to, uintptr_t, src);
2703
2704	error = kauth_resolver_submit(&el, extend_data);
2705
2706	DTRACE_PROC2(kauth__id__resolver__returned, int, error, struct kauth_identity_extlookup *, &el)
2707
2708	KAUTH_DEBUG("CACHE - resolver returned %d", error);
2709
2710	/* was the external lookup successful? */
2711	if (error == 0) {
2712		/*
2713		 * Save the results from the lookup - we may have other
2714		 * information, even if we didn't get a guid or the
2715		 * extended data.
2716		 *
2717		 * If we came from a name, we know the extend_data is valid.
2718		 */
2719		if (from == KI_VALID_PWNAM)
2720			el.el_flags |= KAUTH_EXTLOOKUP_VALID_PWNAM;
2721		else if (from == KI_VALID_GRNAM)
2722			el.el_flags |= KAUTH_EXTLOOKUP_VALID_GRNAM;
2723
2724		kauth_identity_updatecache(&el, &ki, extend_data);
2725
2726		/*
2727		 * Check to see if we have a valid cache entry
2728		 * originating from the result.
2729		 */
2730		if (!(ki.ki_valid & to)) {
2731			error = ENOENT;
2732		}
2733	}
2734	if (error)
2735		return(error);
2736found:
2737	/*
2738	 * Copy from the appropriate struct kauth_identity cache entry
2739	 * structure into the destination buffer area.
2740	 */
2741	switch(to) {
2742	case KI_VALID_UID:
2743		*(uid_t *)dst = ki.ki_uid;
2744		break;
2745	case KI_VALID_GID:
2746		*(gid_t *)dst = ki.ki_gid;
2747		break;
2748	case KI_VALID_GUID:
2749		*(guid_t *)dst = ki.ki_guid;
2750		break;
2751	case KI_VALID_NTSID:
2752		*(ntsid_t *)dst = ki.ki_ntsid;
2753		break;
2754	case KI_VALID_GROUPS: {
2755			struct supgroups *gp = (struct supgroups *)dst;
2756			u_int32_t limit = ki.ki_supgrpcnt;
2757
2758			if (gp->count) {
2759				limit = MIN(ki.ki_supgrpcnt, *gp->count);
2760				*gp->count = limit;
2761			}
2762
2763			memcpy(gp->groups, ki.ki_supgrps, sizeof(gid_t) * limit);
2764		}
2765		break;
2766	case KI_VALID_PWNAM:
2767	case KI_VALID_GRNAM:
2768		/* handled in kauth_resolver_complete() */
2769		break;
2770	default:
2771		return(EINVAL);
2772	}
2773	KAUTH_DEBUG("CACHE - returned successfully");
2774	return(0);
2775}
2776
2777
2778/*
2779 * Group membership cache.
2780 *
2781 * XXX the linked-list implementation here needs to be optimized.
2782 */
2783
2784/*
2785 * kauth_groups_init
2786 *
2787 * Description:	Initialize the groups cache
2788 *
2789 * Parameters:	(void)
2790 *
2791 * Returns:	(void)
2792 *
2793 * Notes:	Initialize the groups cache for use; the group cache is used
2794 *		to avoid unnecessary calls out to user space.
2795 *
2796 *		This function is called from kauth_init() in the file
2797 *		kern_authorization.c.
2798 */
2799void
2800kauth_groups_init(void)
2801{
2802	TAILQ_INIT(&kauth_groups);
2803	kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
2804}
2805
2806
2807/*
2808 * kauth_groups_expired
2809 *
2810 * Description:	Handle lazy expiration of group membership cache entries
2811 *
2812 * Parameters:	gm				group membership entry to
2813 *						check for expiration
2814 *
2815 * Returns:	1				Expired
2816 *		0				Not expired
2817 */
2818static int
2819kauth_groups_expired(struct kauth_group_membership *gm)
2820{
2821	struct timeval tv;
2822
2823	/*
2824	 * Expiration time of 0 means this entry is persistent.
2825	 */
2826	if (gm->gm_expiry == 0)
2827		return (0);
2828
2829	microuptime(&tv);
2830
2831	return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
2832}
2833
2834
2835/*
2836 * kauth_groups_lru
2837 *
2838 * Description:	Promote the entry to the head of the LRU, assumes the cache
2839 *		is locked.
2840 *
2841 * Parameters:	kip				group membership entry to move
2842 *						to the head of the LRU list,
2843 *						if it's not already there
2844 *
2845 * Returns:	(void)
2846 *
2847 * Notes:	This is called even if the entry has expired; typically an
2848 *		expired entry that's been looked up is about to be revalidated,
2849 *		and having it closer to the head of the LRU means finding it
2850 *		quickly again when the revalidation comes through.
2851 */
2852static void
2853kauth_groups_lru(struct kauth_group_membership *gm)
2854{
2855	if (gm != TAILQ_FIRST(&kauth_groups)) {
2856		TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2857		TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2858	}
2859}
2860
2861
2862/*
2863 * kauth_groups_updatecache
2864 *
2865 * Description:	Given a lookup result, add any group cache associations that
2866 *		we don't currently have.
2867 *
2868 * Parameters:	elp				External lookup result from
2869 *						user space daemon to kernel
2870 *		rkip				pointer to returned kauth
2871 *						identity, or NULL
2872 *
2873 * Returns:	(void)
2874 */
2875static void
2876kauth_groups_updatecache(struct kauth_identity_extlookup *el)
2877{
2878	struct kauth_group_membership *gm;
2879	struct timeval tv;
2880
2881	/* need a valid response if we are to cache anything */
2882	if ((el->el_flags &
2883		(KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
2884	    (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
2885		return;
2886
2887	microuptime(&tv);
2888
2889	/*
2890	 * Search for an existing record for this association before inserting
2891	 * a new one; if we find one, update it instead of creating a new one
2892	 */
2893	KAUTH_GROUPS_LOCK();
2894	TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2895		if ((el->el_uid == gm->gm_uid) &&
2896		    (el->el_gid == gm->gm_gid)) {
2897			if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2898				gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2899			} else {
2900				gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2901			}
2902			gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
2903			kauth_groups_lru(gm);
2904			break;
2905		}
2906	}
2907	KAUTH_GROUPS_UNLOCK();
2908
2909	/* if we found an entry to update, stop here */
2910	if (gm != NULL)
2911		return;
2912
2913	/* allocate a new record */
2914	MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
2915	if (gm != NULL) {
2916		gm->gm_uid = el->el_uid;
2917		gm->gm_gid = el->el_gid;
2918		if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2919			gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2920		} else {
2921			gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2922		}
2923		gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
2924	}
2925
2926	/*
2927	 * Insert the new entry.  Note that it's possible to race ourselves
2928	 * here and end up with duplicate entries in the list.  Wasteful, but
2929	 * harmless since the first into the list will never be looked up,
2930	 * and thus will eventually just fall off the end.
2931	 */
2932	KAUTH_GROUPS_LOCK();
2933	TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2934	if (++kauth_groups_count > kauth_groups_cachemax) {
2935		gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
2936		TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2937		kauth_groups_count--;
2938	} else {
2939		gm = NULL;
2940	}
2941	KAUTH_GROUPS_UNLOCK();
2942
2943	/* free expired cache entry */
2944	if (gm != NULL)
2945		FREE(gm, M_KAUTH);
2946}
2947
2948/*
2949 * Trim older entries from the group membership cache.
2950 *
2951 * Must be called with the group cache lock held.
2952 */
2953static void
2954kauth_groups_trimcache(int new_size) {
2955        struct kauth_group_membership *gm;
2956
2957	lck_mtx_assert(kauth_groups_mtx, LCK_MTX_ASSERT_OWNED);
2958
2959	while (kauth_groups_count > new_size) {
2960		gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
2961		TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2962		kauth_groups_count--;
2963		FREE(gm, M_KAUTH);
2964	}
2965}
2966#endif	/* CONFIG_EXT_RESOLVER */
2967
2968/*
2969 * Group membership KPI
2970 */
2971
2972/*
2973 * kauth_cred_ismember_gid
2974 *
2975 * Description:	Given a credential and a GID, determine if the GID is a member
2976 *		of one of the supplementary groups associated with the given
2977 *		credential
2978 *
2979 * Parameters:	cred				Credential to check in
2980 *		gid				GID to check for membership
2981 *		resultp				Pointer to int to contain the
2982 *						result of the call
2983 *
2984 * Returns:	0				Success
2985 *		ENOENT				Could not perform lookup
2986 *	kauth_resolver_submit:EWOULDBLOCK
2987 *	kauth_resolver_submit:EINTR
2988 *	kauth_resolver_submit:ENOMEM
2989 *	kauth_resolver_submit:ENOENT		User space daemon did not vend
2990 *						this credential.
2991 *	kauth_resolver_submit:???		Unlikely error from user space
2992 *
2993 * Implicit returns:
2994 *		*resultp (modified)	1	Is member
2995 *					0	Is not member
2996 *
2997 * Notes:	This function guarantees not to modify resultp when returning
2998 *		an error.
2999 *
3000 *		This function effectively checks the EGID as well, since the
3001 *		EGID is cr_groups[0] as an implementation detail.
3002 */
3003int
3004kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
3005{
3006	posix_cred_t pcred = posix_cred_get(cred);
3007	int i;
3008
3009	/*
3010	 * Check the per-credential list of override groups.
3011	 *
3012	 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3013	 * the cache should be used for that case.
3014	 */
3015	for (i = 0; i < pcred->cr_ngroups; i++) {
3016		if (gid == pcred->cr_groups[i]) {
3017			*resultp = 1;
3018			return(0);
3019		}
3020	}
3021
3022	/*
3023	 * If we don't have a UID for group membership checks, the in-cred list
3024	 * was authoritative and we can stop here.
3025	 */
3026	if (pcred->cr_gmuid == KAUTH_UID_NONE) {
3027		*resultp = 0;
3028		return(0);
3029	}
3030
3031#if CONFIG_EXT_RESOLVER
3032	struct kauth_group_membership *gm;
3033	struct kauth_identity_extlookup el;
3034	int error;
3035
3036	/*
3037	 * If the resolver hasn't checked in yet, we are early in the boot
3038	 * phase and the local group list is complete and authoritative.
3039	 */
3040	if (!kauth_resolver_registered) {
3041		*resultp = 0;
3042		return(0);
3043	}
3044
3045	/* TODO: */
3046	/* XXX check supplementary groups */
3047	/* XXX check whiteout groups */
3048	/* XXX nesting of supplementary/whiteout groups? */
3049
3050	/*
3051	 * Check the group cache.
3052	 */
3053	KAUTH_GROUPS_LOCK();
3054	TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
3055		if ((gm->gm_uid == pcred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
3056			kauth_groups_lru(gm);
3057			break;
3058		}
3059	}
3060
3061	/* did we find a membership entry? */
3062	if (gm != NULL)
3063		*resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
3064	KAUTH_GROUPS_UNLOCK();
3065
3066	/* if we did, we can return now */
3067	if (gm != NULL) {
3068		DTRACE_PROC2(kauth__group__cache__hit, int, pcred->cr_gmuid, int, gid);
3069		return(0);
3070	}
3071
3072	/* nothing in the cache, need to go to userland */
3073	bzero(&el, sizeof(el));
3074	el.el_info_pid = current_proc()->p_pid;
3075	el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
3076	el.el_uid = pcred->cr_gmuid;
3077	el.el_gid = gid;
3078	el.el_member_valid = 0;		/* XXX set by resolver? */
3079
3080	DTRACE_PROC2(kauth__group__resolver__submitted, int, el.el_uid, int, el.el_gid);
3081
3082	error = kauth_resolver_submit(&el, 0ULL);
3083
3084	DTRACE_PROC2(kauth__group__resolver__returned, int, error, int, el.el_flags);
3085
3086	if (error != 0)
3087		return(error);
3088	/* save the results from the lookup */
3089	kauth_groups_updatecache(&el);
3090
3091	/* if we successfully ascertained membership, report */
3092	if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
3093		*resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
3094		return(0);
3095	}
3096
3097	return(ENOENT);
3098#else
3099	*resultp = 0;
3100	return(0);
3101#endif
3102}
3103
3104/*
3105 * kauth_cred_ismember_guid
3106 *
3107 * Description:	Determine whether the supplied credential is a member of the
3108 *		group nominated by GUID.
3109 *
3110 * Parameters:	cred				Credential to check in
3111 *		guidp				Pointer to GUID whose group
3112 *						we are testing for membership
3113 *		resultp				Pointer to int to contain the
3114 *						result of the call
3115 *
3116 * Returns:	0				Success
3117 *	kauth_cred_guid2gid:EINVAL
3118 *	kauth_cred_ismember_gid:ENOENT
3119 *	kauth_resolver_submit:ENOENT		User space daemon did not vend
3120 *						this credential.
3121 *	kauth_cred_ismember_gid:EWOULDBLOCK
3122 *	kauth_cred_ismember_gid:EINTR
3123 *	kauth_cred_ismember_gid:ENOMEM
3124 *	kauth_cred_ismember_gid:???		Unlikely error from user space
3125 *
3126 * Implicit returns:
3127 *		*resultp (modified)	1	Is member
3128 *					0	Is not member
3129 */
3130int
3131kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp)
3132{
3133	int error = 0;
3134
3135	switch (kauth_wellknown_guid(guidp)) {
3136	case KAUTH_WKG_NOBODY:
3137		*resultp = 0;
3138		break;
3139	case KAUTH_WKG_EVERYBODY:
3140		*resultp = 1;
3141		break;
3142	default:
3143#if CONFIG_EXT_RESOLVER
3144	{
3145		struct kauth_identity ki;
3146		gid_t gid;
3147#if 6603280
3148		/*
3149		 * Grovel the identity cache looking for this GUID.
3150		 * If we find it, and it is for a user record, return
3151		 * false because it's not a group.
3152		 *
3153		 * This is necessary because we don't have -ve caching
3154		 * of group memberships, and we really want to avoid
3155		 * calling out to the resolver if at all possible.
3156		 *
3157		 * Because we're called by the ACL evaluator, and the
3158		 * ACL evaluator is likely to encounter ACEs for users,
3159		 * this is expected to be a common case.
3160		 */
3161		ki.ki_valid = 0;
3162		if ((error = kauth_identity_find_guid(guidp, &ki, NULL)) == 0 &&
3163		    !kauth_identity_guid_expired(&ki)) {
3164			if (ki.ki_valid & KI_VALID_GID) {
3165				/* It's a group after all... */
3166				gid = ki.ki_gid;
3167				goto do_check;
3168			}
3169			if (ki.ki_valid & KI_VALID_UID) {
3170				*resultp = 0;
3171				return (0);
3172			}
3173		}
3174#endif /* 6603280 */
3175		/*
3176		 * Attempt to translate the GUID to a GID.  Even if
3177		 * this fails, we will have primed the cache if it is
3178		 * a user record and we'll see it above the next time
3179		 * we're asked.
3180		 */
3181		if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
3182			/*
3183			 * If we have no guid -> gid translation, it's not a group and
3184			 * thus the cred can't be a member.
3185			 */
3186			if (error == ENOENT) {
3187				*resultp = 0;
3188				error = 0;
3189			}
3190		} else {
3191 do_check:
3192			error = kauth_cred_ismember_gid(cred, gid, resultp);
3193		}
3194	}
3195#else	/* CONFIG_EXT_RESOLVER */
3196		error = ENOENT;
3197#endif	/* CONFIG_EXT_RESOLVER */
3198		break;
3199	}
3200	return(error);
3201}
3202
3203/*
3204 * kauth_cred_gid_subset
3205 *
3206 * Description:	Given two credentials, determine if all GIDs associated with
3207 * 		the first are also associated with the second
3208 *
3209 * Parameters:	cred1				Credential to check for
3210 * 		cred2				Credential to check in
3211 *		resultp				Pointer to int to contain the
3212 *						result of the call
3213 *
3214 * Returns:	0				Success
3215 *		non-zero			See kauth_cred_ismember_gid for
3216 *						error codes
3217 *
3218 * Implicit returns:
3219 *		*resultp (modified)	1	Is subset
3220 *					0	Is not subset
3221 *
3222 * Notes:	This function guarantees not to modify resultp when returning
3223 *		an error.
3224 */
3225int
3226kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
3227{
3228	int i, err, res = 1;
3229	gid_t gid;
3230	posix_cred_t pcred1 = posix_cred_get(cred1);
3231	posix_cred_t pcred2 = posix_cred_get(cred2);
3232
3233	/* First, check the local list of groups */
3234	for (i = 0; i < pcred1->cr_ngroups; i++) {
3235		gid = pcred1->cr_groups[i];
3236		if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) {
3237			return err;
3238		}
3239
3240		if (!res && gid != pcred2->cr_rgid && gid != pcred2->cr_svgid) {
3241			*resultp = 0;
3242			return 0;
3243		}
3244	}
3245
3246	/* Check real gid */
3247	if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_rgid, &res)) != 0) {
3248		return err;
3249	}
3250
3251	if (!res && pcred1->cr_rgid != pcred2->cr_rgid &&
3252			pcred1->cr_rgid != pcred2->cr_svgid) {
3253		*resultp = 0;
3254		return 0;
3255	}
3256
3257	/* Finally, check saved gid */
3258	if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_svgid, &res)) != 0){
3259		return err;
3260	}
3261
3262	if (!res && pcred1->cr_svgid != pcred2->cr_rgid &&
3263			pcred1->cr_svgid != pcred2->cr_svgid) {
3264		*resultp = 0;
3265		return 0;
3266	}
3267
3268	*resultp = 1;
3269	return 0;
3270}
3271
3272
3273/*
3274 * kauth_cred_issuser
3275 *
3276 * Description:	Fast replacement for issuser()
3277 *
3278 * Parameters:	cred				Credential to check for super
3279 *						user privileges
3280 *
3281 * Returns:	0				Not super user
3282 *		!0				Is super user
3283 *
3284 * Notes:	This function uses a magic number which is not a manifest
3285 *		constant; this is bad practice.
3286 */
3287int
3288kauth_cred_issuser(kauth_cred_t cred)
3289{
3290	return(kauth_cred_getuid(cred) == 0);
3291}
3292
3293
3294/*
3295 * Credential KPI
3296 */
3297
3298/* lock protecting credential hash table */
3299static lck_mtx_t *kauth_cred_hash_mtx;
3300#define KAUTH_CRED_HASH_LOCK()		lck_mtx_lock(kauth_cred_hash_mtx);
3301#define KAUTH_CRED_HASH_UNLOCK()	lck_mtx_unlock(kauth_cred_hash_mtx);
3302#if KAUTH_CRED_HASH_DEBUG
3303#define KAUTH_CRED_HASH_LOCK_ASSERT()	lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3304#else	/* !KAUTH_CRED_HASH_DEBUG */
3305#define KAUTH_CRED_HASH_LOCK_ASSERT()
3306#endif	/* !KAUTH_CRED_HASH_DEBUG */
3307
3308
3309/*
3310 * kauth_cred_init
3311 *
3312 * Description:	Initialize the credential hash cache
3313 *
3314 * Parameters:	(void)
3315 *
3316 * Returns:	(void)
3317 *
3318 * Notes:	Intialize the credential hash cache for use; the credential
3319 *		hash cache is used convert duplicate credentials into a
3320 *		single reference counted credential in order to save wired
3321 *		kernel memory.  In practice, this generally means a desktop
3322 *		system runs with a few tens of credentials, instead of one
3323 *		per process, one per thread, one per vnode cache entry, and
3324 *		so on.  This generally results in savings of 200K or more
3325 *		(potentially much more on server systems).
3326 *
3327 *		The hash cache internally has a reference on the credential
3328 *		for itself as a means of avoiding a reclaim race for a
3329 *		credential in the process of having it's last non-hash
3330 *		reference released.  This would otherwise result in the
3331 *		possibility of a freed credential that was still in uses due
3332 *		a race.  This use is protected by the KAUTH_CRED_HASH_LOCK.
3333 *
3334 *		On final release, the hash reference is droped, and the
3335 *		credential is freed back to the system.
3336 *
3337 *		This function is called from kauth_init() in the file
3338 *		kern_authorization.c.
3339 */
3340void
3341kauth_cred_init(void)
3342{
3343	int		i;
3344
3345	kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
3346	kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
3347
3348	/*allocate credential hash table */
3349	MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
3350			(sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size),
3351			M_KAUTH, M_WAITOK | M_ZERO);
3352	if (kauth_cred_table_anchor == NULL)
3353		panic("startup: kauth_cred_init");
3354	for (i = 0; i < kauth_cred_table_size; i++) {
3355		TAILQ_INIT(&kauth_cred_table_anchor[i]);
3356	}
3357}
3358
3359
3360/*
3361 * kauth_getuid
3362 *
3363 * Description:	Get the current thread's effective UID.
3364 *
3365 * Parameters:	(void)
3366 *
3367 * Returns:	(uid_t)				The effective UID of the
3368 *						current thread
3369 */
3370uid_t
3371kauth_getuid(void)
3372{
3373	return(kauth_cred_getuid(kauth_cred_get()));
3374}
3375
3376
3377/*
3378 * kauth_getruid
3379 *
3380 * Description:	Get the current thread's real UID.
3381 *
3382 * Parameters:	(void)
3383 *
3384 * Returns:	(uid_t)				The real UID of the current
3385 *						thread
3386 */
3387uid_t
3388kauth_getruid(void)
3389{
3390	return(kauth_cred_getruid(kauth_cred_get()));
3391}
3392
3393
3394/*
3395 * kauth_getgid
3396 *
3397 * Description:	Get the current thread's effective GID.
3398 *
3399 * Parameters:	(void)
3400 *
3401 * Returns:	(gid_t)				The effective GID of the
3402 *						current thread
3403 */
3404gid_t
3405kauth_getgid(void)
3406{
3407	return(kauth_cred_getgid(kauth_cred_get()));
3408}
3409
3410
3411/*
3412 * kauth_getgid
3413 *
3414 * Description:	Get the current thread's real GID.
3415 *
3416 * Parameters:	(void)
3417 *
3418 * Returns:	(gid_t)				The real GID of the current
3419 *						thread
3420 */
3421gid_t
3422kauth_getrgid(void)
3423{
3424	return(kauth_cred_getrgid(kauth_cred_get()));
3425}
3426
3427
3428/*
3429 * kauth_cred_get
3430 *
3431 * Description:	Returns a pointer to the current thread's credential
3432 *
3433 * Parameters:	(void)
3434 *
3435 * Returns:	(kauth_cred_t)			Pointer to the current thread's
3436 *						credential
3437 *
3438 * Notes:	This function does not take a reference; because of this, the
3439 *		caller MUST NOT do anything that would let the thread's
3440 *		credential change while using the returned value, without
3441 *		first explicitly taking their own reference.
3442 *
3443 *		If a caller intends to take a reference on the resulting
3444 *		credential pointer from calling this function, it is strongly
3445 *		recommended that the caller use kauth_cred_get_with_ref()
3446 *		instead, to protect against any future changes to the cred
3447 *		locking protocols; such changes could otherwise potentially
3448 *		introduce race windows in the callers code.
3449 */
3450kauth_cred_t
3451kauth_cred_get(void)
3452{
3453	struct proc *p;
3454	struct uthread *uthread;
3455
3456	uthread = get_bsdthread_info(current_thread());
3457	/* sanity */
3458	if (uthread == NULL)
3459		panic("thread wants credential but has no BSD thread info");
3460	/*
3461	 * We can lazy-bind credentials to threads, as long as their processes
3462	 * have them.
3463	 *
3464	 * XXX If we later inline this function, the code in this block
3465	 * XXX should probably be called out in a function.
3466	 */
3467	if (uthread->uu_ucred == NOCRED) {
3468		if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3469			panic("thread wants credential but has no BSD process");
3470		uthread->uu_ucred = kauth_cred_proc_ref(p);
3471	}
3472	return(uthread->uu_ucred);
3473}
3474
3475void
3476mach_kauth_cred_uthread_update(void)
3477{
3478	uthread_t uthread;
3479	proc_t proc;
3480
3481	uthread = get_bsdthread_info(current_thread());
3482	proc = current_proc();
3483
3484	kauth_cred_uthread_update(uthread, proc);
3485}
3486
3487/*
3488 * kauth_cred_uthread_update
3489 *
3490 * Description:	Given a uthread, a proc, and whether or not the proc is locked,
3491 *		late-bind the uthread cred to the proc cred.
3492 *
3493 * Parameters:	uthread_t			The uthread to update
3494 *		proc_t				The process to update to
3495 *
3496 * Returns:	(void)
3497 *
3498 * Notes:	This code is common code called from system call or trap entry
3499 *		in the case that the process thread may have been changed
3500 *		since the last time the thread entered the kernel.  It is
3501 *		generally only called with the current uthread and process as
3502 *		parameters.
3503 */
3504void
3505kauth_cred_uthread_update(uthread_t uthread, proc_t proc)
3506{
3507	if (uthread->uu_ucred != proc->p_ucred &&
3508	    (uthread->uu_flag & UT_SETUID) == 0) {
3509		kauth_cred_t old = uthread->uu_ucred;
3510		uthread->uu_ucred = kauth_cred_proc_ref(proc);
3511		if (IS_VALID_CRED(old))
3512			kauth_cred_unref(&old);
3513	}
3514}
3515
3516
3517/*
3518 * kauth_cred_get_with_ref
3519 *
3520 * Description:	Takes a reference on the current thread's credential, and then
3521 *		returns a pointer to it to the caller.
3522 *
3523 * Parameters:	(void)
3524 *
3525 * Returns:	(kauth_cred_t)			Pointer to the current thread's
3526 *						newly referenced credential
3527 *
3528 * Notes:	This function takes a reference on the credential before
3529 *		returning it to the caller.
3530 *
3531 *		It is the responsibility of the calling code to release this
3532 *		reference when the credential is no longer in use.
3533 *
3534 *		Since the returned reference may be a persistent reference
3535 *		(e.g. one cached in another data structure with a lifetime
3536 *		longer than the calling function), this release may be delayed
3537 *		until such time as the persistent reference is to be destroyed.
3538 *		An example of this would be the per vnode credential cache used
3539 *		to accelerate lookup operations.
3540 */
3541kauth_cred_t
3542kauth_cred_get_with_ref(void)
3543{
3544	struct proc *procp;
3545	struct uthread *uthread;
3546
3547	uthread = get_bsdthread_info(current_thread());
3548	/* sanity checks */
3549	if (uthread == NULL)
3550		panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
3551	if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3552		panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
3553
3554	/*
3555	 * We can lazy-bind credentials to threads, as long as their processes
3556	 * have them.
3557	 *
3558	 * XXX If we later inline this function, the code in this block
3559	 * XXX should probably be called out in a function.
3560	 */
3561	if (uthread->uu_ucred == NOCRED) {
3562		/* take reference for new cred in thread */
3563		uthread->uu_ucred = kauth_cred_proc_ref(procp);
3564	}
3565	/* take a reference for our caller */
3566	kauth_cred_ref(uthread->uu_ucred);
3567	return(uthread->uu_ucred);
3568}
3569
3570
3571/*
3572 * kauth_cred_proc_ref
3573 *
3574 * Description:	Takes a reference on the current process's credential, and
3575 *		then returns a pointer to it to the caller.
3576 *
3577 * Parameters:	procp				Process whose credential we
3578 *						intend to take a reference on
3579 *
3580 * Returns:	(kauth_cred_t)			Pointer to the process's
3581 *						newly referenced credential
3582 *
3583 * Locks:	PROC_LOCK is held before taking the reference and released
3584 *		after the refeence is taken to protect the p_ucred field of
3585 *		the process referred to by procp.
3586 *
3587 * Notes:	This function takes a reference on the credential before
3588 *		returning it to the caller.
3589 *
3590 *		It is the responsibility of the calling code to release this
3591 *		reference when the credential is no longer in use.
3592 *
3593 *		Since the returned reference may be a persistent reference
3594 *		(e.g. one cached in another data structure with a lifetime
3595 *		longer than the calling function), this release may be delayed
3596 *		until such time as the persistent reference is to be destroyed.
3597 *		An example of this would be the per vnode credential cache used
3598 *		to accelerate lookup operations.
3599 */
3600kauth_cred_t
3601kauth_cred_proc_ref(proc_t procp)
3602{
3603	kauth_cred_t 	cred;
3604
3605	proc_lock(procp);
3606	cred = proc_ucred(procp);
3607	kauth_cred_ref(cred);
3608	proc_unlock(procp);
3609	return(cred);
3610}
3611
3612
3613/*
3614 * kauth_cred_alloc
3615 *
3616 * Description:	Allocate a new credential
3617 *
3618 * Parameters:	(void)
3619 *
3620 * Returns:	!NULL				Newly allocated credential
3621 *		NULL				Insufficient memory
3622 *
3623 * Notes:	The newly allocated credential is zero'ed as part of the
3624 *		allocation process, with the exception of the reference
3625 *		count, which is set to 1 to indicate a single reference
3626 *		held by the caller.
3627 *
3628 *		Since newly allocated credentials have no external pointers
3629 *		referencing them, prior to making them visible in an externally
3630 *		visible pointer (e.g. by adding them to the credential hash
3631 *		cache) is the only legal time in which an existing credential
3632 *		can be safely iinitialized or modified directly.
3633 *
3634 *		After initialization, the caller is expected to call the
3635 *		function kauth_cred_add() to add the credential to the hash
3636 *		cache, after which time it's frozen and becomes publically
3637 *		visible.
3638 *
3639 *		The release protocol depends on kauth_hash_add() being called
3640 *		before kauth_cred_rele() (there is a diagnostic panic which
3641 *		will trigger if this protocol is not observed).
3642 *
3643 * XXX:		This function really ought to be static, rather than being
3644 *		exported as KPI, since a failure of kauth_cred_add() can only
3645 *		be handled by an explicit free of the credential; such frees
3646 *		depend on knowlegdge of the allocation method used, which is
3647 *		permitted to change between kernel revisions.
3648 *
3649 * XXX:		In the insufficient resource case, this code panic's rather
3650 *		than returning a NULL pointer; the code that calls this
3651 *		function needs to be audited before this can be changed.
3652 */
3653kauth_cred_t
3654kauth_cred_alloc(void)
3655{
3656	kauth_cred_t newcred;
3657
3658	MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK);
3659	if (newcred != 0) {
3660		posix_cred_t newpcred = posix_cred_get(newcred);
3661		bzero(newcred, sizeof(*newcred));
3662		newcred->cr_ref = 1;
3663		newcred->cr_audit.as_aia_p = audit_default_aia_p;
3664		/* must do this, or cred has same group membership as uid 0 */
3665		newpcred->cr_gmuid = KAUTH_UID_NONE;
3666#if CRED_DIAGNOSTIC
3667	} else {
3668		panic("kauth_cred_alloc: couldn't allocate credential");
3669#endif
3670	}
3671
3672#if KAUTH_CRED_HASH_DEBUG
3673	kauth_cred_count++;
3674#endif
3675
3676#if CONFIG_MACF
3677	mac_cred_label_init(newcred);
3678#endif
3679
3680	return(newcred);
3681}
3682
3683
3684/*
3685 * kauth_cred_create
3686 *
3687 * Description:	Look to see if we already have a known credential in the hash
3688 *		cache; if one is found, bump the reference count and return
3689 *		it.  If there are no credentials that match the given
3690 *		credential, then allocate a new credential.
3691 *
3692 * Parameters:	cred				Template for credential to
3693 *						be created
3694 *
3695 * Returns:	(kauth_cred_t)			The credential that was found
3696 *						in the hash or created
3697 *		NULL				kauth_cred_add() failed, or
3698 *						there was not an egid specified
3699 *
3700 * Notes:	The gmuid is hard-defaulted to the UID specified.  Since we
3701 *		maintain this field, we can't expect callers to know how it
3702 *		needs to be set.  Callers should be prepared for this field
3703 *		to be overwritten.
3704 *
3705 * XXX:		This code will tight-loop if memory for a new credential is
3706 *		persistently unavailable; this is perhaps not the wisest way
3707 *		to handle this condition, but current callers do not expect
3708 *		a failure.
3709 */
3710kauth_cred_t
3711kauth_cred_create(kauth_cred_t cred)
3712{
3713	kauth_cred_t 	found_cred, new_cred = NULL;
3714	posix_cred_t	pcred = posix_cred_get(cred);
3715	int is_member = 0;
3716
3717	KAUTH_CRED_HASH_LOCK_ASSERT();
3718
3719	if (pcred->cr_flags & CRF_NOMEMBERD) {
3720		pcred->cr_gmuid = KAUTH_UID_NONE;
3721	} else {
3722		/*
3723		 * If the template credential is not opting out of external
3724		 * group membership resolution, then we need to check that
3725		 * the UID we will be using is resolvable by the external
3726		 * resolver.  If it's not, then we opt it out anyway, since
3727		 * all future external resolution requests will be failing
3728		 * anyway, and potentially taking a long time to do it.  We
3729		 * use gid 0 because we always know it will exist and not
3730		 * trigger additional lookups. This is OK, because we end up
3731		 * precatching the information here as a result.
3732		 */
3733		if (!kauth_cred_ismember_gid(cred, 0, &is_member)) {
3734			/*
3735			 * It's a recognized value; we don't really care about
3736			 * the answer, so long as it's something the external
3737			 * resolver could have vended.
3738			 */
3739			pcred->cr_gmuid = pcred->cr_uid;
3740		} else {
3741			/*
3742			 * It's not something the external resolver could
3743			 * have vended, so we don't want to ask it more
3744			 * questions about the credential in the future. This
3745			 * speeds up future lookups, as long as the caller
3746			 * caches results; otherwise, it the same recurring
3747			 * cost.  Since most credentials are used multiple
3748			 * times, we still get some performance win from this.
3749			 */
3750			pcred->cr_gmuid = KAUTH_UID_NONE;
3751			pcred->cr_flags |= CRF_NOMEMBERD;
3752		}
3753	}
3754
3755	/* Caller *must* specify at least the egid in cr_groups[0] */
3756	if (pcred->cr_ngroups < 1)
3757		return(NULL);
3758
3759	for (;;) {
3760		KAUTH_CRED_HASH_LOCK();
3761		found_cred = kauth_cred_find(cred);
3762		if (found_cred != NULL) {
3763			/*
3764			 * Found an existing credential so we'll bump
3765			 * reference count and return
3766			 */
3767			kauth_cred_ref(found_cred);
3768			KAUTH_CRED_HASH_UNLOCK();
3769			return(found_cred);
3770		}
3771		KAUTH_CRED_HASH_UNLOCK();
3772
3773		/*
3774		 * No existing credential found.  Create one and add it to
3775		 * our hash table.
3776		 */
3777		new_cred = kauth_cred_alloc();
3778		if (new_cred != NULL) {
3779			int		err;
3780			posix_cred_t	new_pcred = posix_cred_get(new_cred);
3781			new_pcred->cr_uid = pcred->cr_uid;
3782			new_pcred->cr_ruid = pcred->cr_ruid;
3783			new_pcred->cr_svuid = pcred->cr_svuid;
3784			new_pcred->cr_rgid = pcred->cr_rgid;
3785			new_pcred->cr_svgid = pcred->cr_svgid;
3786			new_pcred->cr_gmuid = pcred->cr_gmuid;
3787			new_pcred->cr_ngroups = pcred->cr_ngroups;
3788			bcopy(&pcred->cr_groups[0], &new_pcred->cr_groups[0], sizeof(new_pcred->cr_groups));
3789#if CONFIG_AUDIT
3790			bcopy(&cred->cr_audit, &new_cred->cr_audit,
3791			    sizeof(new_cred->cr_audit));
3792#endif
3793			new_pcred->cr_flags = pcred->cr_flags;
3794
3795			KAUTH_CRED_HASH_LOCK();
3796			err = kauth_cred_add(new_cred);
3797			KAUTH_CRED_HASH_UNLOCK();
3798
3799			/* Retry if kauth_cred_add returns non zero value */
3800			if (err == 0)
3801				break;
3802#if CONFIG_MACF
3803			mac_cred_label_destroy(new_cred);
3804#endif
3805			AUDIT_SESSION_UNREF(new_cred);
3806
3807			FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
3808			new_cred = NULL;
3809		}
3810	}
3811
3812	return(new_cred);
3813}
3814
3815
3816/*
3817 * kauth_cred_setresuid
3818 *
3819 * Description:	Update the given credential using the UID arguments.  The given
3820 *		UIDs are used to set the effective UID, real UID, saved UID,
3821 *		and GMUID (used for group membership checking).
3822 *
3823 * Parameters:	cred				The original credential
3824 *		ruid				The new real UID
3825 *		euid				The new effective UID
3826 *		svuid				The new saved UID
3827 *		gmuid				KAUTH_UID_NONE -or- the new
3828 *						group membership UID
3829 *
3830 * Returns:	(kauth_cred_t)			The updated credential
3831 *
3832 * Note:	gmuid is different in that a KAUTH_UID_NONE is a valid
3833 *		setting, so if you don't want it to change, pass it the
3834 *		previous value, explicitly.
3835 *
3836 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
3837 *		if it returns a credential other than the one it is passed,
3838 *		will have dropped the reference on the passed credential.  All
3839 *		callers should be aware of this, and treat this function as an
3840 *		unref + ref, potentially on different credentials.
3841 *
3842 *		Because of this, the caller is expected to take its own
3843 *		reference on the credential passed as the first parameter,
3844 *		and be prepared to release the reference on the credential
3845 *		that is returned to them, if it is not intended to be a
3846 *		persistent reference.
3847 */
3848kauth_cred_t
3849kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
3850{
3851	struct ucred temp_cred;
3852	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3853	posix_cred_t pcred = posix_cred_get(cred);
3854
3855	NULLCRED_CHECK(cred);
3856
3857	/*
3858	 * We don't need to do anything if the UIDs we are changing are
3859	 * already the same as the UIDs passed in
3860	 */
3861	if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) &&
3862	    (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) &&
3863	    (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) &&
3864	    (pcred->cr_gmuid == gmuid)) {
3865		/* no change needed */
3866		return(cred);
3867	}
3868
3869	/*
3870	 * Look up in cred hash table to see if we have a matching credential
3871	 * with the new values; this is done by calling kauth_cred_update().
3872	 */
3873	bcopy(cred, &temp_cred, sizeof(temp_cred));
3874	if (euid != KAUTH_UID_NONE) {
3875		temp_pcred->cr_uid = euid;
3876	}
3877	if (ruid != KAUTH_UID_NONE) {
3878		temp_pcred->cr_ruid = ruid;
3879	}
3880	if (svuid != KAUTH_UID_NONE) {
3881		temp_pcred->cr_svuid = svuid;
3882	}
3883
3884	/*
3885	 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3886	 * opt out of participation in external group resolution, unless we
3887	 * unless we explicitly opt back in later.
3888	 */
3889	if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) {
3890		temp_pcred->cr_flags |= CRF_NOMEMBERD;
3891	}
3892
3893	return(kauth_cred_update(cred, &temp_cred, TRUE));
3894}
3895
3896
3897/*
3898 * kauth_cred_setresgid
3899 *
3900 * Description:	Update the given credential using the GID arguments.  The given
3901 *		GIDs are used to set the effective GID, real GID, and saved
3902 *		GID.
3903 *
3904 * Parameters:	cred				The original credential
3905 *		rgid				The new real GID
3906 *		egid				The new effective GID
3907 *		svgid				The new saved GID
3908 *
3909 * Returns:	(kauth_cred_t)			The updated credential
3910 *
3911 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
3912 *		if it returns a credential other than the one it is passed,
3913 *		will have dropped the reference on the passed credential.  All
3914 *		callers should be aware of this, and treat this function as an
3915 *		unref + ref, potentially on different credentials.
3916 *
3917 *		Because of this, the caller is expected to take its own
3918 *		reference on the credential passed as the first parameter,
3919 *		and be prepared to release the reference on the credential
3920 *		that is returned to them, if it is not intended to be a
3921 *		persistent reference.
3922 */
3923kauth_cred_t
3924kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
3925{
3926	struct ucred 	temp_cred;
3927	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3928	posix_cred_t pcred = posix_cred_get(cred);
3929
3930	NULLCRED_CHECK(cred);
3931	DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
3932
3933	/*
3934	 * We don't need to do anything if the given GID are already the
3935	 * same as the GIDs in the credential.
3936	 */
3937	if (pcred->cr_groups[0] == egid &&
3938	    pcred->cr_rgid == rgid &&
3939	    pcred->cr_svgid == svgid) {
3940		/* no change needed */
3941		return(cred);
3942	}
3943
3944	/*
3945	 * Look up in cred hash table to see if we have a matching credential
3946	 * with the new values; this is done by calling kauth_cred_update().
3947	 */
3948	bcopy(cred, &temp_cred, sizeof(temp_cred));
3949	if (egid != KAUTH_GID_NONE) {
3950		/* displacing a supplementary group opts us out of memberd */
3951		if (kauth_cred_change_egid(&temp_cred, egid)) {
3952			DEBUG_CRED_CHANGE("displaced!\n");
3953			temp_pcred->cr_flags |= CRF_NOMEMBERD;
3954			temp_pcred->cr_gmuid = KAUTH_UID_NONE;
3955		} else {
3956			DEBUG_CRED_CHANGE("not displaced\n");
3957		}
3958	}
3959	if (rgid != KAUTH_GID_NONE) {
3960		temp_pcred->cr_rgid = rgid;
3961	}
3962	if (svgid != KAUTH_GID_NONE) {
3963		temp_pcred->cr_svgid = svgid;
3964	}
3965
3966	return(kauth_cred_update(cred, &temp_cred, TRUE));
3967}
3968
3969
3970/*
3971 * Update the given credential with the given groups.  We only allocate a new
3972 *	credential when the given gid actually results in changes to the existing
3973 *	credential.
3974 *	The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3975 *	which will be used for group membership checking.
3976 */
3977/*
3978 * kauth_cred_setgroups
3979 *
3980 * Description:	Update the given credential using the provide supplementary
3981 *		group list and group membership UID
3982 *
3983 * Parameters:	cred				The original credential
3984 *		groups				Pointer to gid_t array which
3985 *						contains the new group list
3986 *		groupcount			The count of valid groups which
3987 *						are contained in 'groups'
3988 *		gmuid				KAUTH_UID_NONE -or- the new
3989 *						group membership UID
3990 *
3991 * Returns:	(kauth_cred_t)			The updated credential
3992 *
3993 * Note:	gmuid is different in that a KAUTH_UID_NONE is a valid
3994 *		setting, so if you don't want it to change, pass it the
3995 *		previous value, explicitly.
3996 *
3997 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
3998 *		if it returns a credential other than the one it is passed,
3999 *		will have dropped the reference on the passed credential.  All
4000 *		callers should be aware of this, and treat this function as an
4001 *		unref + ref, potentially on different credentials.
4002 *
4003 *		Because of this, the caller is expected to take its own
4004 *		reference on the credential passed as the first parameter,
4005 *		and be prepared to release the reference on the credential
4006 *		that is returned to them, if it is not intended to be a
4007 *		persistent reference.
4008 *
4009 * XXX:		Changes are determined in ordinal order - if the caller passes
4010 *		in the same groups list that is already present in the
4011 *		credential, but the members are in a different order, even if
4012 *		the EGID is not modified (i.e. cr_groups[0] is the same), it
4013 *		is considered a modification to the credential, and a new
4014 *		credential is created.
4015 *
4016 *		This should perhaps be better optimized, but it is considered
4017 *		to be the caller's problem.
4018 */
4019kauth_cred_t
4020kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
4021{
4022	int		i;
4023	struct ucred temp_cred;
4024	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4025	posix_cred_t pcred;
4026
4027	NULLCRED_CHECK(cred);
4028
4029	pcred = posix_cred_get(cred);
4030
4031	/*
4032	 * We don't need to do anything if the given list of groups does not
4033	 * change.
4034	 */
4035	if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) {
4036		for (i = 0; i < groupcount; i++) {
4037			if (pcred->cr_groups[i] != groups[i])
4038				break;
4039		}
4040		if (i == groupcount) {
4041			/* no change needed */
4042			return(cred);
4043		}
4044	}
4045
4046	/*
4047	 * Look up in cred hash table to see if we have a matching credential
4048	 * with new values.  If we are setting or clearing the gmuid, then
4049	 * update the cr_flags, since clearing it is sticky.  This permits an
4050	 * opt-out of memberd processing using setgroups(), and an opt-in
4051	 * using initgroups().  This is required for POSIX conformance.
4052	 */
4053	bcopy(cred, &temp_cred, sizeof(temp_cred));
4054	temp_pcred->cr_ngroups = groupcount;
4055	bcopy(groups, temp_pcred->cr_groups, sizeof(temp_pcred->cr_groups));
4056	temp_pcred->cr_gmuid = gmuid;
4057	if (gmuid == KAUTH_UID_NONE)
4058		temp_pcred->cr_flags |= CRF_NOMEMBERD;
4059	else
4060		temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
4061
4062	return(kauth_cred_update(cred, &temp_cred, TRUE));
4063}
4064
4065/*
4066 * Notes:	The return value exists to account for the possibility of a
4067 *		kauth_cred_t without a POSIX label.  This will be the case in
4068 *		the future (see posix_cred_get() below, for more details).
4069 */
4070#if CONFIG_EXT_RESOLVER
4071int kauth_external_supplementary_groups_supported = 1;
4072
4073SYSCTL_INT(_kern, OID_AUTO, ds_supgroups_supported, CTLFLAG_RW | CTLFLAG_LOCKED, &kauth_external_supplementary_groups_supported, 0, "");
4074#endif
4075
4076int
4077kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, int *countp)
4078{
4079	int limit = NGROUPS;
4080	posix_cred_t pcred;
4081
4082	pcred = posix_cred_get(cred);
4083
4084#if CONFIG_EXT_RESOLVER
4085	/*
4086	 * If we've not opted out of using the resolver, then convert the cred to a list
4087	 * of supplemental groups. We do this only if there has been a resolver to talk to,
4088	 * since we may be too early in boot, or in an environment that isn't using DS.
4089	 */
4090	if (kauth_identitysvc_has_registered && kauth_external_supplementary_groups_supported && (pcred->cr_flags & CRF_NOMEMBERD) == 0) {
4091		uid_t uid = kauth_cred_getuid(cred);
4092		int err;
4093
4094		err = kauth_cred_uid2groups(&uid, grouplist, countp);
4095		if (!err)
4096			return 0;
4097
4098		/* On error just fall through */
4099		KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err);
4100	}
4101#endif /* CONFIG_EXT_RESOLVER */
4102
4103	/*
4104	 * If they just want a copy of the groups list, they may not care
4105	 * about the actual count.  If they specify an input count, however,
4106	 * treat it as an indicator of the buffer size available in grouplist,
4107	 * and limit the returned list to that size.
4108	 */
4109	if (countp) {
4110		limit = MIN(*countp, pcred->cr_ngroups);
4111		*countp = limit;
4112	}
4113
4114	memcpy(grouplist, pcred->cr_groups, sizeof(gid_t) * limit);
4115
4116	return 0;
4117}
4118
4119
4120/*
4121 * kauth_cred_setuidgid
4122 *
4123 * Description:	Update the given credential using the UID and GID arguments.
4124 *		The given UID is used to set the effective UID, real UID, and
4125 *		saved UID.  The given GID is used to set the effective GID,
4126 *		real GID, and saved GID.
4127 *
4128 * Parameters:	cred				The original credential
4129 *		uid				The new UID to use
4130 *		gid				The new GID to use
4131 *
4132 * Returns:	(kauth_cred_t)			The updated credential
4133 *
4134 * Notes:	We set the gmuid to uid if the credential we are inheriting
4135 *		from has not opted out of memberd participation; otherwise
4136 *		we set it to KAUTH_UID_NONE
4137 *
4138 *		This code is only ever called from the per-thread credential
4139 *		code path in the "set per thread credential" case; and in
4140 *		posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4141 *		flag is set.
4142 *
4143 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
4144 *		if it returns a credential other than the one it is passed,
4145 *		will have dropped the reference on the passed credential.  All
4146 *		callers should be aware of this, and treat this function as an
4147 *		unref + ref, potentially on different credentials.
4148 *
4149 *		Because of this, the caller is expected to take its own
4150 *		reference on the credential passed as the first parameter,
4151 *		and be prepared to release the reference on the credential
4152 *		that is returned to them, if it is not intended to be a
4153 *		persistent reference.
4154 */
4155kauth_cred_t
4156kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4157{
4158	struct ucred temp_cred;
4159	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4160	posix_cred_t pcred;
4161
4162	NULLCRED_CHECK(cred);
4163
4164	pcred = posix_cred_get(cred);
4165
4166	/*
4167	 * We don't need to do anything if the effective, real and saved
4168	 * user IDs are already the same as the user ID passed into us.
4169	 */
4170	if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid &&
4171		pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) {
4172		/* no change needed */
4173		return(cred);
4174	}
4175
4176	/*
4177	 * Look up in cred hash table to see if we have a matching credential
4178	 * with the new values.
4179	 */
4180	bzero(&temp_cred, sizeof(temp_cred));
4181	temp_pcred->cr_uid = uid;
4182	temp_pcred->cr_ruid = uid;
4183	temp_pcred->cr_svuid = uid;
4184	temp_pcred->cr_flags = pcred->cr_flags;
4185	/* inherit the opt-out of memberd */
4186	if (pcred->cr_flags & CRF_NOMEMBERD) {
4187		temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4188		temp_pcred->cr_flags |= CRF_NOMEMBERD;
4189	} else {
4190		temp_pcred->cr_gmuid = uid;
4191		temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
4192	}
4193	temp_pcred->cr_ngroups = 1;
4194	/* displacing a supplementary group opts us out of memberd */
4195	if (kauth_cred_change_egid(&temp_cred, gid)) {
4196		temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4197		temp_pcred->cr_flags |= CRF_NOMEMBERD;
4198	}
4199	temp_pcred->cr_rgid = gid;
4200	temp_pcred->cr_svgid = gid;
4201#if CONFIG_MACF
4202	temp_cred.cr_label = cred->cr_label;
4203#endif
4204
4205	return(kauth_cred_update(cred, &temp_cred, TRUE));
4206}
4207
4208
4209/*
4210 * kauth_cred_setsvuidgid
4211 *
4212 * Description:	Function used by execve to set the saved uid and gid values
4213 *		for suid/sgid programs
4214 *
4215 * Parameters:	cred				The credential to update
4216 *		uid				The saved uid to set
4217 *		gid				The saved gid to set
4218 *
4219 * Returns:	(kauth_cred_t)			The updated credential
4220 *
4221 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
4222 *		if it returns a credential other than the one it is passed,
4223 *		will have dropped the reference on the passed credential.  All
4224 *		callers should be aware of this, and treat this function as an
4225 *		unref + ref, potentially on different credentials.
4226 *
4227 *		Because of this, the caller is expected to take its own
4228 *		reference on the credential passed as the first parameter,
4229 *		and be prepared to release the reference on the credential
4230 *		that is returned to them, if it is not intended to be a
4231 *		persistent reference.
4232 */
4233kauth_cred_t
4234kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4235{
4236	struct ucred temp_cred;
4237	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4238	posix_cred_t pcred;
4239
4240	NULLCRED_CHECK(cred);
4241
4242	pcred = posix_cred_get(cred);
4243
4244	DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
4245
4246	/*
4247	 * We don't need to do anything if the effective, real and saved
4248	 * uids are already the same as the uid provided.  This check is
4249	 * likely insufficient.
4250	 */
4251	if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) {
4252		/* no change needed */
4253		return(cred);
4254	}
4255	DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4256
4257	/* look up in cred hash table to see if we have a matching credential
4258	 * with new values.
4259	 */
4260	bcopy(cred, &temp_cred, sizeof(temp_cred));
4261	temp_pcred->cr_svuid = uid;
4262	temp_pcred->cr_svgid = gid;
4263
4264	return(kauth_cred_update(cred, &temp_cred, TRUE));
4265}
4266
4267
4268/*
4269 * kauth_cred_setauditinfo
4270 *
4271 * Description:	Update the given credential using the given au_session_t.
4272 *
4273 * Parameters:	cred				The original credential
4274 *		auditinfo_p			Pointer to ne audit information
4275 *
4276 * Returns:	(kauth_cred_t)			The updated credential
4277 *
4278 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
4279 *		if it returns a credential other than the one it is passed,
4280 *		will have dropped the reference on the passed credential.  All
4281 *		callers should be aware of this, and treat this function as an
4282 *		unref + ref, potentially on different credentials.
4283 *
4284 *		Because of this, the caller is expected to take its own
4285 *		reference on the credential passed as the first parameter,
4286 *		and be prepared to release the reference on the credential
4287 *		that is returned to them, if it is not intended to be a
4288 *		persistent reference.
4289 */
4290kauth_cred_t
4291kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
4292{
4293	struct ucred temp_cred;
4294
4295	NULLCRED_CHECK(cred);
4296
4297	/*
4298	 * We don't need to do anything if the audit info is already the
4299	 * same as the audit info in the credential provided.
4300	 */
4301	if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) {
4302		/* no change needed */
4303		return(cred);
4304	}
4305
4306	bcopy(cred, &temp_cred, sizeof(temp_cred));
4307	bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
4308
4309	return(kauth_cred_update(cred, &temp_cred, FALSE));
4310}
4311
4312#if CONFIG_MACF
4313/*
4314 * kauth_cred_label_update
4315 *
4316 * Description:	Update the MAC label associated with a credential
4317 *
4318 * Parameters:	cred				The original credential
4319 *		label				The MAC label to set
4320 *
4321 * Returns:	(kauth_cred_t)			The updated credential
4322 *
4323 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
4324 *		if it returns a credential other than the one it is passed,
4325 *		will have dropped the reference on the passed credential.  All
4326 *		callers should be aware of this, and treat this function as an
4327 *		unref + ref, potentially on different credentials.
4328 *
4329 *		Because of this, the caller is expected to take its own
4330 *		reference on the credential passed as the first parameter,
4331 *		and be prepared to release the reference on the credential
4332 *		that is returned to them, if it is not intended to be a
4333 *		persistent reference.
4334 */
4335kauth_cred_t
4336kauth_cred_label_update(kauth_cred_t cred, struct label *label)
4337{
4338	kauth_cred_t newcred;
4339	struct ucred temp_cred;
4340
4341	bcopy(cred, &temp_cred, sizeof(temp_cred));
4342
4343	mac_cred_label_init(&temp_cred);
4344	mac_cred_label_associate(cred, &temp_cred);
4345	mac_cred_label_update(&temp_cred, label);
4346
4347	newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4348	mac_cred_label_destroy(&temp_cred);
4349	return (newcred);
4350}
4351
4352/*
4353 * kauth_cred_label_update_execve
4354 *
4355 * Description:	Update the MAC label associated with a credential as
4356 *		part of exec
4357 *
4358 * Parameters:	cred				The original credential
4359 *		vp				The exec vnode
4360 *		scriptl				The script MAC label
4361 *		execl				The executable MAC label
4362 *		disjointp			Pointer to flag to set if old
4363 *						and returned credentials are
4364 *						disjoint
4365 *
4366 * Returns:	(kauth_cred_t)			The updated credential
4367 *
4368 * Implicit returns:
4369 *		*disjointp			Set to 1 for disjoint creds
4370 *
4371 * IMPORTANT:	This function is implemented via kauth_cred_update(), which,
4372 *		if it returns a credential other than the one it is passed,
4373 *		will have dropped the reference on the passed credential.  All
4374 *		callers should be aware of this, and treat this function as an
4375 *		unref + ref, potentially on different credentials.
4376 *
4377 *		Because of this, the caller is expected to take its own
4378 *		reference on the credential passed as the first parameter,
4379 *		and be prepared to release the reference on the credential
4380 *		that is returned to them, if it is not intended to be a
4381 *		persistent reference.
4382 */
4383
4384static
4385kauth_cred_t
4386kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
4387	struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4388	struct label *execl, unsigned int *csflags, void *macextensions, int *disjointp, int *labelupdateerror)
4389{
4390	kauth_cred_t newcred;
4391	struct ucred temp_cred;
4392
4393	bcopy(cred, &temp_cred, sizeof(temp_cred));
4394
4395	mac_cred_label_init(&temp_cred);
4396	mac_cred_label_associate(cred, &temp_cred);
4397	mac_cred_label_update_execve(ctx, &temp_cred,
4398						  vp, offset, scriptvp, scriptl, execl, csflags,
4399						  macextensions, disjointp, labelupdateerror);
4400
4401	newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4402	mac_cred_label_destroy(&temp_cred);
4403	return (newcred);
4404}
4405
4406/*
4407 *  kauth_proc_label_update
4408 *
4409 * Description:  Update the label inside the credential associated with the process.
4410 *
4411 * Parameters:	p			The process to modify
4412 *				label		The label to place in the process credential
4413 *
4414 * Notes:		The credential associated with the process may change as a result
4415 *				of this call.  The caller should not assume the process reference to
4416 *				the old credential still exists.
4417 */
4418int kauth_proc_label_update(struct proc *p, struct label *label)
4419{
4420	kauth_cred_t my_cred, my_new_cred;
4421
4422	my_cred = kauth_cred_proc_ref(p);
4423
4424	DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
4425
4426	/* get current credential and take a reference while we muck with it */
4427	for (;;) {
4428
4429  		/*
4430		 * Set the credential with new info.  If there is no change,
4431		 * we get back the same credential we passed in; if there is
4432		 * a change, we drop the reference on the credential we
4433		 * passed in.  The subsequent compare is safe, because it is
4434		 * a pointer compare rather than a contents compare.
4435  		 */
4436		my_new_cred = kauth_cred_label_update(my_cred, label);
4437		if (my_cred != my_new_cred) {
4438
4439			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);
4440
4441			proc_lock(p);
4442			/*
4443			 * We need to protect for a race where another thread
4444			 * also changed the credential after we took our
4445			 * reference.  If p_ucred has changed then we should
4446			 * restart this again with the new cred.
4447			 */
4448			if (p->p_ucred != my_cred) {
4449				proc_unlock(p);
4450				kauth_cred_unref(&my_new_cred);
4451				my_cred = kauth_cred_proc_ref(p);
4452				/* try again */
4453				continue;
4454			}
4455			p->p_ucred = my_new_cred;
4456			/* update cred on proc */
4457			PROC_UPDATE_CREDS_ONPROC(p);
4458
4459			mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4460			proc_unlock(p);
4461		}
4462		break;
4463	}
4464	/* Drop old proc reference or our extra reference */
4465	kauth_cred_unref(&my_cred);
4466
4467	return (0);
4468}
4469
4470/*
4471 *  kauth_proc_label_update_execve
4472 *
4473 * Description: Update the label inside the credential associated with the
4474 *		process as part of a transitioning execve.  The label will
4475 *		be updated by the policies as part of this processing, not
4476 *		provided up front.
4477 *
4478 * Parameters:	p			The process to modify
4479 *		ctx			The context of the exec
4480 *		vp			The vnode being exec'ed
4481 *		scriptl			The script MAC label
4482 *		execl			The executable MAC label
4483 *		lupdateerror	The error place holder for MAC label authority
4484 *						to update about possible termination
4485 *
4486 * Returns:	0			Label update did not make credential
4487 *					disjoint
4488 *		1			Label update caused credential to be
4489 *					disjoint
4490 *
4491 * Notes:	The credential associated with the process WILL change as a
4492 *		result of this call.  The caller should not assume the process
4493 *		reference to the old credential still exists.
4494 */
4495
4496void
4497kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
4498	struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4499	struct label *execl, unsigned int *csflags, void *macextensions, int *disjoint, int *update_return)
4500{
4501	kauth_cred_t my_cred, my_new_cred;
4502	my_cred = kauth_cred_proc_ref(p);
4503
4504	DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
4505
4506	/* get current credential and take a reference while we muck with it */
4507	for (;;) {
4508
4509  		/*
4510		 * Set the credential with new info.  If there is no change,
4511		 * we get back the same credential we passed in; if there is
4512		 * a change, we drop the reference on the credential we
4513		 * passed in.  The subsequent compare is safe, because it is
4514		 * a pointer compare rather than a contents compare.
4515  		 */
4516		my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, offset, scriptvp, scriptl, execl, csflags, macextensions, disjoint, update_return);
4517		if (my_cred != my_new_cred) {
4518
4519			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);
4520
4521			proc_lock(p);
4522			/*
4523			 * We need to protect for a race where another thread
4524			 * also changed the credential after we took our
4525			 * reference.  If p_ucred has changed then we should
4526			 * restart this again with the new cred.
4527			 */
4528			if (p->p_ucred != my_cred) {
4529				proc_unlock(p);
4530				kauth_cred_unref(&my_new_cred);
4531				my_cred = kauth_cred_proc_ref(p);
4532				/* try again */
4533				continue;
4534			}
4535			p->p_ucred = my_new_cred;
4536			/* update cred on proc */
4537			PROC_UPDATE_CREDS_ONPROC(p);
4538			mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4539			proc_unlock(p);
4540		}
4541		break;
4542	}
4543	/* Drop old proc reference or our extra reference */
4544	kauth_cred_unref(&my_cred);
4545}
4546
4547#if 1
4548/*
4549 * for temporary binary compatibility
4550 */
4551kauth_cred_t	kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
4552kauth_cred_t
4553kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
4554{
4555	return kauth_cred_label_update(cred, label);
4556}
4557
4558int kauth_proc_setlabel(struct proc *p, struct label *label);
4559int
4560kauth_proc_setlabel(struct proc *p, struct label *label)
4561{
4562	return kauth_proc_label_update(p, label);
4563}
4564#endif
4565
4566#else
4567
4568/* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4569 * Since we cannot build our export lists based on the kernel configuration we need
4570 * to define a stub.
4571 */
4572kauth_cred_t
4573kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
4574{
4575	return(NULL);
4576}
4577
4578int
4579kauth_proc_label_update(__unused struct proc *p, __unused void *label)
4580{
4581	return (0);
4582}
4583
4584#if 1
4585/*
4586 * for temporary binary compatibility
4587 */
4588kauth_cred_t	kauth_cred_setlabel(kauth_cred_t cred, void *label);
4589kauth_cred_t
4590kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
4591{
4592	return NULL;
4593}
4594
4595int kauth_proc_setlabel(struct proc *p, void *label);
4596int
4597kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
4598{
4599	return (0);
4600}
4601#endif
4602#endif
4603
4604/*
4605 * kauth_cred_ref
4606 *
4607 * Description:	Add a reference to the passed credential
4608 *
4609 * Parameters:	cred				The credential to reference
4610 *
4611 * Returns:	(void)
4612 *
4613 * Notes:	This function adds a reference to the provided credential;
4614 *		the existing reference on the credential is assumed to be
4615 *		held stable over this operation by taking the appropriate
4616 *		lock to protect the pointer from which it is being referenced,
4617 *		if necessary (e.g. the proc lock is held over the call if the
4618 *		credential being referenced is from p_ucred, the vnode lock
4619 *		if from the per vnode name cache cred cache, and so on).
4620 *
4621 *		This is safe from the kauth_cred_unref() path, since an atomic
4622 *		add is used, and the unref path specifically checks to see that
4623 *		the value has not been changed to add a reference between the
4624 *		time the credential is unreferenced by another pointer and the
4625 *		time it is unreferenced from the cred hash cache.
4626 */
4627void
4628kauth_cred_ref(kauth_cred_t cred)
4629{
4630	int		old_value;
4631
4632	NULLCRED_CHECK(cred);
4633
4634	old_value = OSAddAtomicLong(1, (long*)&cred->cr_ref);
4635
4636	if (old_value < 1)
4637		panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4638
4639#if 0 // use this to watch a specific credential
4640	if ( is_target_cred( cred ) != 0 ) {
4641 		get_backtrace( );
4642	}
4643#endif
4644
4645	return;
4646}
4647
4648
4649/*
4650 * kauth_cred_unref_hashlocked
4651 *
4652 * Description:	release a credential reference; when the last reference is
4653 *		released, the credential will be freed.
4654 *
4655 * Parameters:	credp				Pointer to address containing
4656 *						credential to be freed
4657 *
4658 * Returns:	TRUE if the credential must be destroyed by the caller.
4659 *		FALSE otherwise.
4660 *
4661 * Implicit returns:
4662 *		*credp				Set to NOCRED
4663 *
4664 * Notes:	This function assumes the credential hash lock is held.
4665 *
4666 *		This function is internal use only, since the hash lock is
4667 *		scoped to this compilation unit.
4668 *
4669 *		This function destroys the contents of the pointer passed by
4670 *		the caller to prevent the caller accidentally attempting to
4671 *		release a given reference twice in error.
4672 *
4673 *		The last reference is considered to be released when a release
4674 *		of a credential of a reference count of 2 occurs; this is an
4675 *		intended effect, to take into account the reference held by
4676 *		the credential hash, which is released at the same time.
4677 */
4678static boolean_t
4679kauth_cred_unref_hashlocked(kauth_cred_t *credp)
4680{
4681	int		old_value;
4682	boolean_t	destroy_it = FALSE;
4683
4684	KAUTH_CRED_HASH_LOCK_ASSERT();
4685	NULLCRED_CHECK(*credp);
4686
4687	old_value = OSAddAtomicLong(-1, (long*)&(*credp)->cr_ref);
4688
4689#if DIAGNOSTIC
4690	if (old_value == 0)
4691		panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
4692	if (old_value == 1)
4693		panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
4694#endif
4695
4696#if 0 // use this to watch a specific credential
4697	if ( is_target_cred( *credp ) != 0 ) {
4698		get_backtrace( );
4699	}
4700#endif
4701
4702	/*
4703	 * If the old_value is 2, then we have just released the last external
4704	 * reference to this credential
4705	 */
4706	if (old_value < 3) {
4707		/* The last absolute reference is our credential hash table */
4708		destroy_it = kauth_cred_remove(*credp);
4709	}
4710
4711	if (destroy_it == FALSE) {
4712		*credp = NOCRED;
4713	}
4714
4715	return (destroy_it);
4716}
4717
4718
4719/*
4720 * kauth_cred_unref
4721 *
4722 * Description:	Release a credential reference while holding the credential
4723 *		hash lock; when the last reference is released, the credential
4724 *		will be freed.
4725 *
4726 * Parameters:	credp				Pointer to address containing
4727 *						credential to be freed
4728 *
4729 * Returns:	(void)
4730 *
4731 * Implicit returns:
4732 *		*credp				Set to NOCRED
4733 *
4734 * Notes:	See kauth_cred_unref_hashlocked() for more information.
4735 *
4736 */
4737void
4738kauth_cred_unref(kauth_cred_t *credp)
4739{
4740	boolean_t destroy_it;
4741
4742	KAUTH_CRED_HASH_LOCK();
4743	destroy_it = kauth_cred_unref_hashlocked(credp);
4744	KAUTH_CRED_HASH_UNLOCK();
4745
4746	if (destroy_it == TRUE) {
4747		assert(*credp != NOCRED);
4748#if CONFIG_MACF
4749		mac_cred_label_destroy(*credp);
4750#endif
4751		AUDIT_SESSION_UNREF(*credp);
4752
4753		(*credp)->cr_ref = 0;
4754		FREE_ZONE(*credp, sizeof(*(*credp)), M_CRED);
4755		*credp = NOCRED;
4756	}
4757}
4758
4759
4760#ifndef __LP64__
4761/*
4762 * kauth_cred_rele
4763 *
4764 * Description:	release a credential reference; when the last reference is
4765 *		released, the credential will be freed
4766 *
4767 * Parameters:	cred				Credential to release
4768 *
4769 * Returns:	(void)
4770 *
4771 * DEPRECATED:	This interface is obsolete due to a failure to clear out the
4772 *		clear the pointer in the caller to avoid multiple releases of
4773 *		the same credential.  The currently recommended interface is
4774 *		kauth_cred_unref().
4775 */
4776void
4777kauth_cred_rele(kauth_cred_t cred)
4778{
4779	kauth_cred_unref(&cred);
4780}
4781#endif /* !__LP64__ */
4782
4783
4784/*
4785 * kauth_cred_dup
4786 *
4787 * Description:	Duplicate a credential via alloc and copy; the new credential
4788 *		has only it's own
4789 *
4790 * Parameters:	cred				The credential to duplicate
4791 *
4792 * Returns:	(kauth_cred_t)			The duplicate credential
4793 *
4794 * Notes:	The typical value to calling this routine is if you are going
4795 *		to modify an existing credential, and expect to need a new one
4796 *		from the hash cache.
4797 *
4798 *		This should probably not be used in the majority of cases;
4799 *		if you are using it instead of kauth_cred_create(), you are
4800 *		likely making a mistake.
4801 *
4802 *		The newly allocated credential is copied as part of the
4803 *		allocation process, with the exception of the reference
4804 *		count, which is set to 1 to indicate a single reference
4805 *		held by the caller.
4806 *
4807 *		Since newly allocated credentials have no external pointers
4808 *		referencing them, prior to making them visible in an externally
4809 *		visible pointer (e.g. by adding them to the credential hash
4810 *		cache) is the only legal time in which an existing credential
4811 *		can be safely initialized or modified directly.
4812 *
4813 *		After initialization, the caller is expected to call the
4814 *		function kauth_cred_add() to add the credential to the hash
4815 *		cache, after which time it's frozen and becomes publicly
4816 *		visible.
4817 *
4818 *		The release protocol depends on kauth_hash_add() being called
4819 *		before kauth_cred_rele() (there is a diagnostic panic which
4820 *		will trigger if this protocol is not observed).
4821 *
4822 */
4823kauth_cred_t
4824kauth_cred_dup(kauth_cred_t cred)
4825{
4826	kauth_cred_t newcred;
4827#if CONFIG_MACF
4828	struct label *temp_label;
4829#endif
4830
4831#if CRED_DIAGNOSTIC
4832	if (cred == NOCRED || cred == FSCRED)
4833		panic("kauth_cred_dup: bad credential");
4834#endif
4835	newcred = kauth_cred_alloc();
4836	if (newcred != NULL) {
4837#if CONFIG_MACF
4838		temp_label = newcred->cr_label;
4839#endif
4840		bcopy(cred, newcred, sizeof(*newcred));
4841#if CONFIG_MACF
4842		newcred->cr_label = temp_label;
4843		mac_cred_label_associate(cred, newcred);
4844#endif
4845		AUDIT_SESSION_REF(cred);
4846		newcred->cr_ref = 1;
4847	}
4848	return(newcred);
4849}
4850
4851/*
4852 * kauth_cred_copy_real
4853 *
4854 * Description:	Returns a credential based on the passed credential but which
4855 *		reflects the real rather than effective UID and GID.
4856 *
4857 * Parameters:	cred				The credential from which to
4858 *						derive the new credential
4859 *
4860 * Returns:	(kauth_cred_t)			The copied credential
4861 *
4862 * IMPORTANT:	This function DOES NOT utilize kauth_cred_update(); as a
4863 *		result, the caller is responsible for dropping BOTH the
4864 *		additional reference on the passed cred (if any), and the
4865 *		credential returned by this function.  The drop should be
4866 *		via the kauth_cred_unref() KPI.
4867 */
4868kauth_cred_t
4869kauth_cred_copy_real(kauth_cred_t cred)
4870{
4871	kauth_cred_t newcred = NULL, found_cred;
4872	struct ucred temp_cred;
4873	posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4874	posix_cred_t pcred = posix_cred_get(cred);
4875
4876	/* if the credential is already 'real', just take a reference */
4877	if ((pcred->cr_ruid == pcred->cr_uid) &&
4878	    (pcred->cr_rgid == pcred->cr_gid)) {
4879		kauth_cred_ref(cred);
4880		return(cred);
4881	}
4882
4883	/*
4884	 * Look up in cred hash table to see if we have a matching credential
4885	 * with the new values.
4886	 */
4887	bcopy(cred, &temp_cred, sizeof(temp_cred));
4888	temp_pcred->cr_uid = pcred->cr_ruid;
4889	/* displacing a supplementary group opts us out of memberd */
4890	if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) {
4891		temp_pcred->cr_flags |= CRF_NOMEMBERD;
4892		temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4893	}
4894	/*
4895	 * If the cred is not opted out, make sure we are using the r/euid
4896	 * for group checks
4897	 */
4898	if (temp_pcred->cr_gmuid != KAUTH_UID_NONE)
4899		temp_pcred->cr_gmuid = pcred->cr_ruid;
4900
4901	for (;;) {
4902		int		err;
4903
4904		KAUTH_CRED_HASH_LOCK();
4905		found_cred = kauth_cred_find(&temp_cred);
4906		if (found_cred == cred) {
4907			/* same cred so just bail */
4908			KAUTH_CRED_HASH_UNLOCK();
4909			return(cred);
4910		}
4911		if (found_cred != NULL) {
4912			/*
4913			 * Found a match so we bump reference count on new
4914			 * one.  We leave the old one alone.
4915			 */
4916			kauth_cred_ref(found_cred);
4917			KAUTH_CRED_HASH_UNLOCK();
4918			return(found_cred);
4919		}
4920
4921		/*
4922		 * Must allocate a new credential, copy in old credential
4923		 * data and update the real user and group IDs.
4924		 */
4925		newcred = kauth_cred_dup(&temp_cred);
4926		err = kauth_cred_add(newcred);
4927		KAUTH_CRED_HASH_UNLOCK();
4928
4929		/* Retry if kauth_cred_add() fails */
4930		if (err == 0)
4931			break;
4932#if CONFIG_MACF
4933		mac_cred_label_destroy(newcred);
4934#endif
4935		AUDIT_SESSION_UNREF(newcred);
4936
4937		FREE_ZONE(newcred, sizeof(*newcred), M_CRED);
4938		newcred = NULL;
4939	}
4940
4941	return(newcred);
4942}
4943
4944
4945/*
4946 * kauth_cred_update
4947 *
4948 * Description:	Common code to update a credential
4949 *
4950 * Parameters:	old_cred			Reference counted credential
4951 *						to update
4952 *		model_cred			Non-reference counted model
4953 *						credential to apply to the
4954 *						credential to be updated
4955 *		retain_auditinfo		Flag as to whether or not the
4956 *						audit information should be
4957 *						copied from the old_cred into
4958 *						the model_cred
4959 *
4960 * Returns:	(kauth_cred_t)			The updated credential
4961 *
4962 * IMPORTANT:	This function will potentially return a credential other than
4963 *		the one it is passed, and if so, it will have dropped the
4964 *		reference on the passed credential.  All callers should be
4965 *		aware of this, and treat this function as an unref + ref,
4966 *		potentially on different credentials.
4967 *
4968 *		Because of this, the caller is expected to take its own
4969 *		reference on the credential passed as the first parameter,
4970 *		and be prepared to release the reference on the credential
4971 *		that is returned to them, if it is not intended to be a
4972 *		persistent reference.
4973 */
4974static kauth_cred_t
4975kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
4976	boolean_t retain_auditinfo)
4977{
4978	kauth_cred_t found_cred, new_cred = NULL;
4979
4980	/*
4981	 * Make sure we carry the auditinfo forward to the new credential
4982	 * unless we are actually updating the auditinfo.
4983	 */
4984	if (retain_auditinfo) {
4985		bcopy(&old_cred->cr_audit, &model_cred->cr_audit,
4986		    sizeof(model_cred->cr_audit));
4987	}
4988
4989	for (;;) {
4990		int		err;
4991
4992		KAUTH_CRED_HASH_LOCK();
4993		found_cred = kauth_cred_find(model_cred);
4994		if (found_cred == old_cred) {
4995			/* same cred so just bail */
4996			KAUTH_CRED_HASH_UNLOCK();
4997			return(old_cred);
4998		}
4999		if (found_cred != NULL) {
5000			boolean_t destroy_it;
5001
5002			DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
5003			/*
5004			 * Found a match so we bump reference count on new
5005			 * one and decrement reference count on the old one.
5006			 */
5007			kauth_cred_ref(found_cred);
5008			destroy_it = kauth_cred_unref_hashlocked(&old_cred);
5009			KAUTH_CRED_HASH_UNLOCK();
5010			if (destroy_it == TRUE) {
5011				assert(old_cred != NOCRED);
5012#if CONFIG_MACF
5013				mac_cred_label_destroy(old_cred);
5014#endif
5015				AUDIT_SESSION_UNREF(old_cred);
5016
5017				old_cred->cr_ref = 0;
5018				FREE_ZONE(old_cred, sizeof(*old_cred), M_CRED);
5019				old_cred = NOCRED;
5020
5021			}
5022			return(found_cred);
5023		}
5024
5025		/*
5026		 * Must allocate a new credential using the model.  also
5027		 * adds the new credential to the credential hash table.
5028		 */
5029		new_cred = kauth_cred_dup(model_cred);
5030		err = kauth_cred_add(new_cred);
5031		KAUTH_CRED_HASH_UNLOCK();
5032
5033		/* retry if kauth_cred_add returns non zero value */
5034		if (err == 0)
5035			break;
5036#if CONFIG_MACF
5037		mac_cred_label_destroy(new_cred);
5038#endif
5039		AUDIT_SESSION_UNREF(new_cred);
5040
5041		FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
5042		new_cred = NULL;
5043	}
5044
5045	DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred);
5046	kauth_cred_unref(&old_cred);
5047	return(new_cred);
5048}
5049
5050
5051/*
5052 * kauth_cred_add
5053 *
5054 * Description:	Add the given credential to our credential hash table and
5055 *		take an additional reference to account for our use of the
5056 *		credential in the hash table
5057 *
5058 * Parameters:	new_cred			Credential to insert into cred
5059 *						hash cache
5060 *
5061 * Returns:	0				Success
5062 *		-1				Hash insertion failed: caller
5063 *						should retry
5064 *
5065 * Locks:	Caller is expected to hold KAUTH_CRED_HASH_LOCK
5066 *
5067 * Notes:	The 'new_cred' MUST NOT already be in the cred hash cache
5068 */
5069static int
5070kauth_cred_add(kauth_cred_t new_cred)
5071{
5072	u_long			hash_key;
5073
5074	KAUTH_CRED_HASH_LOCK_ASSERT();
5075
5076	hash_key = kauth_cred_get_hashkey(new_cred);
5077	hash_key %= kauth_cred_table_size;
5078
5079	/* race fix - there is a window where another matching credential
5080	 * could have been inserted between the time this one was created and we
5081	 * got the hash lock.  If we find a match return an error and have the
5082	 * the caller retry.
5083	 */
5084	if (kauth_cred_find(new_cred) != NULL) {
5085		return(-1);
5086	}
5087
5088	/* take a reference for our use in credential hash table */
5089	kauth_cred_ref(new_cred);
5090
5091	/* insert the credential into the hash table */
5092	TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
5093
5094	return(0);
5095}
5096
5097
5098/*
5099 * kauth_cred_remove
5100 *
5101 * Description:	Remove the given credential from our credential hash table
5102 *
5103 * Parameters:	cred				Credential to remove from cred
5104 *						hash cache
5105 *
5106 * Returns:	TRUE if the cred was found & removed from the hash; FALSE if not.
5107 *
5108 * Locks:	Caller is expected to hold KAUTH_CRED_HASH_LOCK
5109 *
5110 * Notes:	The check for the reference increment after entry is generally
5111 *		agree to be safe, since we use atomic operations, and the
5112 *		following code occurs with the hash lock held; in theory, this
5113 *		protects us from the 2->1 reference that gets us here.
5114 */
5115static boolean_t
5116kauth_cred_remove(kauth_cred_t cred)
5117{
5118	u_long			hash_key;
5119	kauth_cred_t	found_cred;
5120
5121	hash_key = kauth_cred_get_hashkey(cred);
5122	hash_key %= kauth_cred_table_size;
5123
5124	/* Avoid race */
5125	if (cred->cr_ref < 1)
5126		panic("cred reference underflow");
5127	if (cred->cr_ref > 1)
5128		return (FALSE);		/* someone else got a ref */
5129
5130	/* Find cred in the credential hash table */
5131	TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
5132		if (found_cred == cred) {
5133			/* found a match, remove it from the hash table */
5134			TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
5135#if KAUTH_CRED_HASH_DEBUG
5136			kauth_cred_count--;
5137#endif
5138			return (TRUE);
5139		}
5140	}
5141
5142	/* Did not find a match... this should not happen! XXX Make panic? */
5143	printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
5144	return (FALSE);
5145}
5146
5147
5148/*
5149 * kauth_cred_find
5150 *
5151 * Description:	Using the given credential data, look for a match in our
5152 *		credential hash table
5153 *
5154 * Parameters:	cred				Credential to lookup in cred
5155 *						hash cache
5156 *
5157 * Returns:	NULL				Not found
5158 *		!NULL				Matching credential already in
5159 *						cred hash cache
5160 *
5161 * Locks:	Caller is expected to hold KAUTH_CRED_HASH_LOCK
5162 */
5163kauth_cred_t
5164kauth_cred_find(kauth_cred_t cred)
5165{
5166	u_long			hash_key;
5167	kauth_cred_t	found_cred;
5168	posix_cred_t pcred = posix_cred_get(cred);
5169
5170	KAUTH_CRED_HASH_LOCK_ASSERT();
5171
5172#if KAUTH_CRED_HASH_DEBUG
5173	static int		test_count = 0;
5174
5175	test_count++;
5176	if ((test_count % 200) == 0) {
5177		kauth_cred_hash_print();
5178	}
5179#endif
5180
5181	hash_key = kauth_cred_get_hashkey(cred);
5182	hash_key %= kauth_cred_table_size;
5183
5184	/* Find cred in the credential hash table */
5185	TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
5186		boolean_t match;
5187		posix_cred_t found_pcred = posix_cred_get(found_cred);
5188
5189		/*
5190		 * don't worry about the label unless the flags in
5191		 * either credential tell us to.
5192		 */
5193		match = (bcmp(found_pcred, pcred, sizeof (*pcred)) == 0) ? TRUE : FALSE;
5194		match = match && ((bcmp(&found_cred->cr_audit, &cred->cr_audit,
5195			sizeof(cred->cr_audit)) == 0) ? TRUE : FALSE);
5196#if CONFIG_MACF
5197		if (((found_pcred->cr_flags & CRF_MAC_ENFORCE) != 0) ||
5198		    ((pcred->cr_flags & CRF_MAC_ENFORCE) != 0)) {
5199			match = match && mac_cred_label_compare(found_cred->cr_label,
5200				cred->cr_label);
5201		}
5202#endif
5203		if (match) {
5204			/* found a match */
5205			return(found_cred);
5206		}
5207	}
5208	/* No match found */
5209
5210	return(NULL);
5211}
5212
5213
5214/*
5215 * kauth_cred_hash
5216 *
5217 * Description:	Generates a hash key using data that makes up a credential;
5218 *		based on ElfHash
5219 *
5220 * Parameters:	datap				Pointer to data to hash
5221 *		data_len			Count of bytes to hash
5222 *		start_key			Start key value
5223 *
5224 * Returns:	(u_long)			Returned hash key
5225 */
5226static inline u_long
5227kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
5228{
5229	u_long	hash_key = start_key;
5230	u_long	temp;
5231
5232	while (data_len > 0) {
5233		hash_key = (hash_key << 4) + *datap++;
5234		temp = hash_key & 0xF0000000;
5235		if (temp) {
5236			hash_key ^= temp >> 24;
5237		}
5238		hash_key &= ~temp;
5239		data_len--;
5240	}
5241	return(hash_key);
5242}
5243
5244
5245/*
5246 * kauth_cred_get_hashkey
5247 *
5248 * Description:	Generate a hash key using data that makes up a credential;
5249 *		based on ElfHash.  We hash on the entire credential data,
5250 *		not including the ref count or the TAILQ, which are mutable;
5251 *		everything else isn't.
5252 *
5253 * Parameters:	cred				Credential for which hash is
5254 *						desired
5255 *
5256 * Returns:	(u_long)			Returned hash key
5257 *
5258 * Notes:	When actually moving the POSIX credential into a real label,
5259 *		remember to update this hash computation.
5260 */
5261static u_long
5262kauth_cred_get_hashkey(kauth_cred_t cred)
5263{
5264#if CONFIG_MACF
5265	posix_cred_t pcred = posix_cred_get(cred);
5266#endif
5267	u_long	hash_key = 0;
5268
5269	hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix,
5270							   sizeof (struct posix_cred),
5271							   hash_key);
5272	hash_key = kauth_cred_hash((uint8_t *)&cred->cr_audit,
5273							   sizeof(struct au_session),
5274							   hash_key);
5275#if CONFIG_MACF
5276	if (pcred->cr_flags & CRF_MAC_ENFORCE) {
5277		hash_key = kauth_cred_hash((uint8_t *)cred->cr_label,
5278								   sizeof (struct label),
5279								   hash_key);
5280	}
5281#endif
5282	return(hash_key);
5283}
5284
5285
5286#if KAUTH_CRED_HASH_DEBUG
5287/*
5288 * kauth_cred_hash_print
5289 *
5290 * Description:	Print out cred hash cache table information for debugging
5291 *		purposes, including the credential contents
5292 *
5293 * Parameters:	(void)
5294 *
5295 * Returns:	(void)
5296 *
5297 * Implicit returns:	Results in console output
5298 */
5299static void
5300kauth_cred_hash_print(void)
5301{
5302	int 			i, j;
5303	kauth_cred_t	found_cred;
5304
5305	printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
5306	/* count slot hits, misses, collisions, and max depth */
5307	for (i = 0; i < kauth_cred_table_size; i++) {
5308		printf("[%02d] ", i);
5309		j = 0;
5310		TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5311			if (j > 0) {
5312				printf("---- ");
5313			}
5314			j++;
5315			kauth_cred_print(found_cred);
5316			printf("\n");
5317		}
5318		if (j == 0) {
5319			printf("NOCRED \n");
5320		}
5321	}
5322}
5323#endif	/* KAUTH_CRED_HASH_DEBUG */
5324
5325
5326#if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5327/*
5328 * kauth_cred_print
5329 *
5330 * Description:	Print out an individual credential's contents for debugging
5331 *		purposes
5332 *
5333 * Parameters:	cred				The credential to print out
5334 *
5335 * Returns:	(void)
5336 *
5337 * Implicit returns:	Results in console output
5338 */
5339void
5340kauth_cred_print(kauth_cred_t cred)
5341{
5342	int 	i;
5343
5344	printf("%p - refs %lu flags 0x%08x uids e%d r%d sv%d gm%d ", cred, cred->cr_ref, cred->cr_flags, cred->cr_uid, cred->cr_ruid, cred->cr_svuid, cred->cr_gmuid);
5345	printf("group count %d gids ", cred->cr_ngroups);
5346	for (i = 0; i < NGROUPS; i++) {
5347		if (i == 0)
5348			printf("e");
5349		printf("%d ", cred->cr_groups[i]);
5350	}
5351	printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid);
5352	printf("auditinfo_addr %d %d %d %d %d %d\n",
5353		cred->cr_audit.s_aia_p->ai_auid,
5354		cred->cr_audit.as_mask.am_success,
5355		cred->cr_audit.as_mask.am_failure,
5356		cred->cr_audit.as_aia_p->ai_termid.at_port,
5357		cred->cr_audit.as_aia_p->ai_termid.at_addr[0],
5358		cred->cr_audit.as_aia_p->ai_asid);
5359}
5360
5361int is_target_cred( kauth_cred_t the_cred )
5362{
5363	if ( the_cred->cr_uid != 0 )
5364		return( 0 );
5365	if ( the_cred->cr_ruid != 0 )
5366		return( 0 );
5367	if ( the_cred->cr_svuid != 0 )
5368		return( 0 );
5369	if ( the_cred->cr_ngroups != 11 )
5370		return( 0 );
5371	if ( the_cred->cr_groups[0] != 11 )
5372		return( 0 );
5373	if ( the_cred->cr_groups[1] != 81 )
5374		return( 0 );
5375	if ( the_cred->cr_groups[2] != 63947 )
5376		return( 0 );
5377	if ( the_cred->cr_groups[3] != 80288 )
5378		return( 0 );
5379	if ( the_cred->cr_groups[4] != 89006 )
5380		return( 0 );
5381	if ( the_cred->cr_groups[5] != 52173 )
5382		return( 0 );
5383	if ( the_cred->cr_groups[6] != 84524 )
5384		return( 0 );
5385	if ( the_cred->cr_groups[7] != 79 )
5386		return( 0 );
5387	if ( the_cred->cr_groups[8] != 80292 )
5388		return( 0 );
5389	if ( the_cred->cr_groups[9] != 80 )
5390		return( 0 );
5391	if ( the_cred->cr_groups[10] != 90824 )
5392		return( 0 );
5393	if ( the_cred->cr_rgid != 11 )
5394		return( 0 );
5395	if ( the_cred->cr_svgid != 11 )
5396		return( 0 );
5397	if ( the_cred->cr_gmuid != 3475 )
5398		return( 0 );
5399	if ( the_cred->cr_audit.as_aia_p->ai_auid != 3475 )
5400		return( 0 );
5401/*
5402	if ( the_cred->cr_audit.as_mask.am_success != 0 )
5403		return( 0 );
5404	if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5405		return( 0 );
5406	if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5407		return( 0 );
5408	if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5409		return( 0 );
5410	if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5411		return( 0 );
5412	if ( the_cred->cr_flags != 0 )
5413		return( 0 );
5414*/
5415	return( -1 ); // found target cred
5416}
5417
5418void get_backtrace( void )
5419{
5420	int				my_slot;
5421	void *			my_stack[ MAX_STACK_DEPTH ];
5422	int				i, my_depth;
5423
5424	if ( cred_debug_buf_p == NULL ) {
5425		MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK);
5426		bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p));
5427	}
5428
5429	if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) {
5430		/* buffer is full */
5431		return;
5432	}
5433
5434	my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
5435	if ( my_depth == 0 ) {
5436		printf("%s - OSBacktrace failed \n", __FUNCTION__);
5437		return;
5438	}
5439
5440	/* fill new backtrace */
5441	my_slot = cred_debug_buf_p->next_slot;
5442	cred_debug_buf_p->next_slot++;
5443	cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
5444	for ( i = 0; i < my_depth; i++ ) {
5445		cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
5446	}
5447
5448	return;
5449}
5450
5451
5452/* subset of struct ucred for use in sysctl_dump_creds */
5453struct debug_ucred {
5454	void	*credp;
5455	u_long	cr_ref;				/* reference count */
5456	uid_t	cr_uid;				/* effective user id */
5457	uid_t	cr_ruid;			/* real user id */
5458	uid_t	cr_svuid;			/* saved user id */
5459	short	cr_ngroups;			/* number of groups in advisory list */
5460	gid_t	cr_groups[NGROUPS];	/* advisory group list */
5461	gid_t	cr_rgid;			/* real group id */
5462	gid_t	cr_svgid;			/* saved group id */
5463	uid_t	cr_gmuid;			/* UID for group membership purposes */
5464	struct auditinfo_addr cr_audit;	/* user auditing data. */
5465	void	*cr_label;			/* MACF label */
5466	int		cr_flags;			/* flags on credential */
5467};
5468typedef struct debug_ucred debug_ucred;
5469
5470SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
5471    NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
5472
5473/*	accessed by:
5474 *	err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5475 */
5476
5477static int
5478sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5479{
5480	int 			i, j, counter = 0;
5481	int				error;
5482	size_t			space;
5483	kauth_cred_t	found_cred;
5484	debug_ucred *	cred_listp;
5485	debug_ucred *	nextp;
5486
5487	/* This is a readonly node. */
5488	if (req->newptr != USER_ADDR_NULL)
5489		return (EPERM);
5490
5491	/* calculate space needed */
5492	for (i = 0; i < kauth_cred_table_size; i++) {
5493		TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5494			counter++;
5495		}
5496	}
5497
5498	/* they are querying us so just return the space required. */
5499	if (req->oldptr == USER_ADDR_NULL) {
5500		counter += 10; // add in some padding;
5501		req->oldidx = counter * sizeof(debug_ucred);
5502		return 0;
5503	}
5504
5505	MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK );
5506	if ( cred_listp == NULL ) {
5507		return (ENOMEM);
5508	}
5509
5510	/* fill in creds to send back */
5511	nextp = cred_listp;
5512	space = 0;
5513	for (i = 0; i < kauth_cred_table_size; i++) {
5514		TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5515			nextp->credp = found_cred;
5516			nextp->cr_ref = found_cred->cr_ref;
5517			nextp->cr_uid = found_cred->cr_uid;
5518			nextp->cr_ruid = found_cred->cr_ruid;
5519			nextp->cr_svuid = found_cred->cr_svuid;
5520			nextp->cr_ngroups = found_cred->cr_ngroups;
5521			for ( j = 0; j < nextp->cr_ngroups; j++ ) {
5522				nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
5523			}
5524			nextp->cr_rgid = found_cred->cr_rgid;
5525			nextp->cr_svgid = found_cred->cr_svgid;
5526			nextp->cr_gmuid = found_cred->cr_gmuid;
5527			nextp->cr_audit.ai_auid =
5528			    found_cred->cr_audit.as_aia_p->ai_auid;
5529			nextp->cr_audit.ai_mask.am_success =
5530			    found_cred->cr_audit.as_mask.am_success;
5531			nextp->cr_audit.ai_mask.am_failure =
5532			    found_cred->cr_audit.as_mask.am_failure;
5533			nextp->cr_audit.ai_termid.at_port =
5534			    found_cred->cr_audit.as_aia_p->ai_termid.at_port;
5535			nextp->cr_audit.ai_termid.at_type =
5536			    found_cred->cr_audit.as_aia_p->ai_termid.at_type;
5537			nextp->cr_audit.ai_termid.at_addr[0] =
5538			    found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0];
5539			nextp->cr_audit.ai_termid.at_addr[1] =
5540			    found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1];
5541			nextp->cr_audit.ai_termid.at_addr[2] =
5542			    found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2];
5543			nextp->cr_audit.ai_termid.at_addr[3] =
5544			    found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3];
5545			nextp->cr_audit.ai_asid =
5546			    found_cred->cr_audit.as_aia_p->ai_asid;
5547			nextp->cr_audit.ai_flags =
5548			    found_cred->cr_audit.as_aia_p->ai_flags;
5549			nextp->cr_label = found_cred->cr_label;
5550			nextp->cr_flags = found_cred->cr_flags;
5551			nextp++;
5552			space += sizeof(debug_ucred);
5553			if ( space > req->oldlen ) {
5554				FREE(cred_listp, M_TEMP);
5555				return (ENOMEM);
5556			}
5557		}
5558	}
5559	req->oldlen = space;
5560	error = SYSCTL_OUT(req, cred_listp, req->oldlen);
5561	FREE(cred_listp, M_TEMP);
5562	return (error);
5563}
5564
5565
5566SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
5567    NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
5568
5569/*	accessed by:
5570 *	err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5571 */
5572
5573static int
5574sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5575{
5576	int 			i, j;
5577	int				error;
5578	size_t			space;
5579	cred_debug_buffer *	bt_bufp;
5580	cred_backtrace *	nextp;
5581
5582	/* This is a readonly node. */
5583	if (req->newptr != USER_ADDR_NULL)
5584		return (EPERM);
5585
5586	if ( cred_debug_buf_p == NULL ) {
5587		return (EAGAIN);
5588	}
5589
5590	/* calculate space needed */
5591	space = sizeof( cred_debug_buf_p->next_slot );
5592	space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
5593
5594	/* they are querying us so just return the space required. */
5595	if (req->oldptr == USER_ADDR_NULL) {
5596		req->oldidx = space;
5597		return 0;
5598	}
5599
5600	if ( space > req->oldlen ) {
5601		return (ENOMEM);
5602	}
5603
5604	MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK );
5605	if ( bt_bufp == NULL ) {
5606		return (ENOMEM);
5607	}
5608
5609	/* fill in backtrace info to send back */
5610	bt_bufp->next_slot = cred_debug_buf_p->next_slot;
5611	space = sizeof(bt_bufp->next_slot);
5612
5613	nextp = &bt_bufp->stack_buffer[ 0 ];
5614	for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
5615		nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
5616		for ( j = 0; j < nextp->depth; j++ ) {
5617			nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
5618		}
5619		space += sizeof(*nextp);
5620		nextp++;
5621	}
5622	req->oldlen = space;
5623	error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
5624	FREE(bt_bufp, M_TEMP);
5625	return (error);
5626}
5627
5628#endif	/* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5629
5630
5631/*
5632 **********************************************************************
5633 * The following routines will be moved to a policy_posix.c module at
5634 * some future point.
5635 **********************************************************************
5636 */
5637
5638/*
5639 * posix_cred_create
5640 *
5641 * Description:	Helper function to create a kauth_cred_t credential that is
5642 *		initally labelled with a specific POSIX credential label
5643 *
5644 * Parameters:	pcred			The posix_cred_t to use as the initial
5645 *					label value
5646 *
5647 * Returns:	(kauth_cred_t)		The credential that was found in the
5648 *					hash or creates
5649 *		NULL			kauth_cred_add() failed, or there was
5650 *					no egid specified, or we failed to
5651 *					attach a label to the new credential
5652 *
5653 * Notes:	This function currently wraps kauth_cred_create(), and is the
5654 *		only consumer of that ill-fated function, apart from bsd_init().
5655 *		It exists solely to support the NFS server code creation of
5656 *		credentials based on the over-the-wire RPC calls containing
5657 *		traditional POSIX credential information being tunneled to
5658 *		the server host from the client machine.
5659 *
5660 *		In the future, we hope this function goes away.
5661 *
5662 *		In the short term, it creates a temporary credential, puts
5663 *		the POSIX information from NFS into it, and then calls
5664 *		kauth_cred_create(), as an internal implementation detail.
5665 *
5666 *		If we have to keep it around in the medium term, it will
5667 *		create a new kauth_cred_t, then label it with a POSIX label
5668 *		corresponding to the contents of the kauth_cred_t.  If the
5669 *		policy_posix MACF module is not loaded, it will instead
5670 *		substitute a posix_cred_t which GRANTS all access (effectively
5671 *		a "root" credential) in order to not prevent NFS from working
5672 *		in the case that we are not supporting POSIX credentials.
5673 */
5674kauth_cred_t
5675posix_cred_create(posix_cred_t pcred)
5676{
5677	struct ucred temp_cred;
5678
5679	bzero(&temp_cred, sizeof(temp_cred));
5680	temp_cred.cr_posix = *pcred;
5681
5682	return kauth_cred_create(&temp_cred);
5683}
5684
5685
5686/*
5687 * posix_cred_get
5688 *
5689 * Description:	Given a kauth_cred_t, return the POSIX credential label, if
5690 *		any, which is associated with it.
5691 *
5692 * Parameters:	cred			The credential to obtain the label from
5693 *
5694 * Returns:	posix_cred_t		The POSIX credential label
5695 *
5696 * Notes:	In the event that the policy_posix MACF module IS NOT loaded,
5697 *		this function will return a pointer to a posix_cred_t which
5698 *		GRANTS all access (effectively, a "root" credential).  This is
5699 *		necessary to support legacy code which insists on tightly
5700 *		integrating POSIX credentials into its APIs, including, but
5701 *		not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5702 *		NFSv3, signals, dtrace, and a large number of kauth routines
5703 *		used to implement POSIX permissions related system calls.
5704 *
5705 *		In the event that the policy_posix MACF module IS loaded, and
5706 *		there is no POSIX label on the kauth_cred_t credential, this
5707 *		function will return a pointer to a posix_cred_t which DENIES
5708 *		all access (effectively, a "deny rights granted by POSIX"
5709 *		credential).  This is necessary to support the concept of a
5710 *		transiently loaded POSIX policy, or kauth_cred_t credentials
5711 *		which can not be used in conjunctions with POSIX permissions
5712 *		checks.
5713 *
5714 *		This function currently returns the address of the cr_posix
5715 *		field of the supplied kauth_cred_t credential, and as such
5716 *		currently can not fail.  In the future, this will not be the
5717 *		case.
5718 */
5719posix_cred_t
5720posix_cred_get(kauth_cred_t cred)
5721{
5722	return(&cred->cr_posix);
5723}
5724
5725
5726/*
5727 * posix_cred_label
5728 *
5729 * Description:	Label a kauth_cred_t with a POSIX credential label
5730 *
5731 * Parameters:	cred			The credential to label
5732 *		pcred			The POSIX credential t label it with
5733 *
5734 * Returns:	(void)
5735 *
5736 * Notes:	This function is currently void in order to permit it to fit
5737 *		in with the current MACF framework label methods which allow
5738 *		labeling to fail silently.  This is like acceptable for
5739 *		mandatory access controls, but not for POSIX, since those
5740 *		access controls are advisory.  We will need to consider a
5741 *		return value in a future version of the MACF API.
5742 *
5743 *		This operation currently cannot fail, as currently the POSIX
5744 *		credential is a subfield of the kauth_cred_t (ucred), which
5745 *		MUST be valid.  In the future, this will not be the case.
5746 */
5747void
5748posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
5749{
5750	cred->cr_posix = *pcred;	/* structure assign for now */
5751}
5752
5753
5754/*
5755 * posix_cred_access
5756 *
5757 * Description:	Perform a POSIX access check for a protected object
5758 *
5759 * Parameters:	cred			The credential to check
5760 *		object_uid		The POSIX UID of the protected object
5761 *		object_gid		The POSIX GID of the protected object
5762 *		object_mode		The POSIX mode of the protected object
5763 *		mode_req		The requested POSIX access rights
5764 *
5765 * Returns	0			Access is granted
5766 *		EACCES			Access is denied
5767 *
5768 * Notes:	This code optimizes the case where the world and group rights
5769 *		would both grant the requested rights to avoid making a group
5770 *		membership query.  This is a big performance win in the case
5771 *		where this is true.
5772 */
5773int
5774posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
5775{
5776	int is_member;
5777	mode_t mode_owner = (object_mode & S_IRWXU);
5778	mode_t mode_group = (object_mode & S_IRWXG) << 3;
5779	mode_t mode_world = (object_mode & S_IRWXO) << 6;
5780
5781	/*
5782	 * Check first for owner rights
5783	 */
5784	if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req)
5785		return (0);
5786
5787	/*
5788	 * Combined group and world rights check, if we don't have owner rights
5789	 *
5790	 * OPTIMIZED: If group and world rights would grant the same bits, and
5791	 * they set of requested bits is in both, then we can simply check the
5792	 * world rights, avoiding a group membership check, which is expensive.
5793	 */
5794	if ((mode_req & mode_group & mode_world) == mode_req) {
5795		return (0);
5796	} else {
5797		/*
5798		 * NON-OPTIMIZED: requires group membership check.
5799		 */
5800		if ((mode_req & mode_group) != mode_req) {
5801			/*
5802			 * exclusion group : treat errors as "is a member"
5803			 *
5804			 * NON-OPTIMIZED: +group would deny; must check group
5805			 */
5806			if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5807				/*
5808				 * DENY: +group denies
5809				 */
5810				return (EACCES);
5811			} else {
5812				if ((mode_req & mode_world) != mode_req) {
5813					/*
5814					 * DENY: both -group & world would deny
5815					 */
5816					return (EACCES);
5817				} else {
5818					/*
5819					 * ALLOW: allowed by -group and +world
5820					 */
5821					return (0);
5822				}
5823			}
5824		} else {
5825			/*
5826			 * inclusion group; treat errors as "not a member"
5827			 *
5828			 * NON-OPTIMIZED: +group allows, world denies; must
5829			 * check group
5830			 */
5831			if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5832				/*
5833				 * ALLOW: allowed by +group
5834				 */
5835				return (0);
5836			} else {
5837				if ((mode_req & mode_world) != mode_req) {
5838					/*
5839					 * DENY: both -group & world would deny
5840					 */
5841					return (EACCES);
5842				} else {
5843					/*
5844					 * ALLOW: allowed by -group and +world
5845					 */
5846					return (0);
5847				}
5848			}
5849		}
5850	}
5851}
5852