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