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