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