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 static LCK_GRP_DECLARE(ucred_rw_lock_group, "ucred_rw locks");
3376
3377 struct ucred_rw {
3378 LIST_ENTRY(ucred_rw) crw_link;
3379 struct ucred *crw_cred;
3380 os_refcnt_t crw_weak_ref;
3381 lck_mtx_t crw_lock;
3382 };
3383
3384 #define KAUTH_CRED_TABLE_SIZE 128
3385 LIST_HEAD(kauth_cred_entry_head, ucred_rw);
3386
3387 static ZONE_DECLARE(ucred_rw_zone, "cred_rw", sizeof(struct ucred_rw), ZC_ZFREE_CLEARMEM);
3388 static SECURITY_READ_ONLY_LATE(zone_t) ucred_zone;
3389
3390 static struct kauth_cred_entry_head
3391 kauth_cred_table_anchor[KAUTH_CRED_TABLE_SIZE];
3392
3393 /* lock protecting credential hash table */
3394 static LCK_MTX_DECLARE(kauth_cred_hash_mtx, &kauth_lck_grp);
3395 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(&kauth_cred_hash_mtx);
3396 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(&kauth_cred_hash_mtx);
3397 #define KAUTH_CRED_HASH_LOCK_ASSERT() LCK_MTX_ASSERT(&kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
3398
3399 /*
3400 * kauth_cred_init
3401 *
3402 * Description: Initialize the credential hash cache
3403 *
3404 * Parameters: (void)
3405 *
3406 * Returns: (void)
3407 *
3408 * Notes: Intialize the credential hash cache for use; the credential
3409 * hash cache is used convert duplicate credentials into a
3410 * single reference counted credential in order to save wired
3411 * kernel memory. In practice, this generally means a desktop
3412 * system runs with a few tens of credentials, instead of one
3413 * per process, one per thread, one per vnode cache entry, and
3414 * so on. This generally results in savings of 200K or more
3415 * (potentially much more on server systems).
3416 *
3417 * We also create the kernel and init creds before lockdown
3418 * so that vfs_context0 and initcred pointers can be made constant.
3419 *
3420 * We do this in the "LOCKS" stage because we need
3421 * the kauth_cred_hash_mtx to be initialized.
3422 */
3423 __startup_func
3424 static void
kauth_cred_init(void)3425 kauth_cred_init(void)
3426 {
3427 struct ucred kernel_cred_template = {
3428 .cr_posix.cr_ngroups = 1,
3429 .cr_posix.cr_flags = CRF_NOMEMBERD,
3430 .cr_audit.as_aia_p = audit_default_aia_p,
3431 };
3432
3433 for (int i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
3434 LIST_INIT(&kauth_cred_table_anchor[i]);
3435 }
3436
3437 ucred_zone = zone_create_ext("cred", sizeof(struct ucred),
3438 ZC_READONLY | ZC_ZFREE_CLEARMEM, ZONE_ID_KAUTH_CRED, NULL);
3439
3440 vfs_context0.vc_ucred = kauth_cred_create(&kernel_cred_template);
3441 }
3442 STARTUP(LOCKS, STARTUP_RANK_MIDDLE, kauth_cred_init);
3443
3444 /*
3445 * kauth_getuid
3446 *
3447 * Description: Get the current thread's effective UID.
3448 *
3449 * Parameters: (void)
3450 *
3451 * Returns: (uid_t) The effective UID of the
3452 * current thread
3453 */
3454 uid_t
kauth_getuid(void)3455 kauth_getuid(void)
3456 {
3457 return kauth_cred_getuid(kauth_cred_get());
3458 }
3459
3460
3461 /*
3462 * kauth_getruid
3463 *
3464 * Description: Get the current thread's real UID.
3465 *
3466 * Parameters: (void)
3467 *
3468 * Returns: (uid_t) The real UID of the current
3469 * thread
3470 */
3471 uid_t
kauth_getruid(void)3472 kauth_getruid(void)
3473 {
3474 return kauth_cred_getruid(kauth_cred_get());
3475 }
3476
3477
3478 /*
3479 * kauth_getgid
3480 *
3481 * Description: Get the current thread's effective GID.
3482 *
3483 * Parameters: (void)
3484 *
3485 * Returns: (gid_t) The effective GID of the
3486 * current thread
3487 */
3488 gid_t
kauth_getgid(void)3489 kauth_getgid(void)
3490 {
3491 return kauth_cred_getgid(kauth_cred_get());
3492 }
3493
3494
3495 /*
3496 * kauth_getgid
3497 *
3498 * Description: Get the current thread's real GID.
3499 *
3500 * Parameters: (void)
3501 *
3502 * Returns: (gid_t) The real GID of the current
3503 * thread
3504 */
3505 gid_t
kauth_getrgid(void)3506 kauth_getrgid(void)
3507 {
3508 return kauth_cred_getrgid(kauth_cred_get());
3509 }
3510
3511
3512 /*
3513 * kauth_cred_get
3514 *
3515 * Description: Returns a pointer to the current thread's credential
3516 *
3517 * Parameters: (void)
3518 *
3519 * Returns: (kauth_cred_t) Pointer to the current thread's
3520 * credential
3521 *
3522 * Notes: This function does not take a reference; because of this, the
3523 * caller MUST NOT do anything that would let the thread's
3524 * credential change while using the returned value, without
3525 * first explicitly taking their own reference.
3526 *
3527 * If a caller intends to take a reference on the resulting
3528 * credential pointer from calling this function, it is strongly
3529 * recommended that the caller use kauth_cred_get_with_ref()
3530 * instead, to protect against any future changes to the cred
3531 * locking protocols; such changes could otherwise potentially
3532 * introduce race windows in the callers code.
3533 *
3534 * This might return NOCRED for kernel threads only,
3535 * before bsd_init() has run.
3536 */
3537 kauth_cred_t
kauth_cred_get(void)3538 kauth_cred_get(void)
3539 {
3540 return current_thread_ro()->tro_cred;
3541 }
3542
3543 void
mach_kauth_cred_thread_update(void)3544 mach_kauth_cred_thread_update(void)
3545 {
3546 kauth_cred_thread_update(current_thread(), current_proc());
3547 }
3548
3549 __attribute__((noinline))
3550 static void
kauth_cred_thread_update_slow(thread_ro_t tro,proc_t proc)3551 kauth_cred_thread_update_slow(thread_ro_t tro, proc_t proc)
3552 {
3553 struct ucred *cred = kauth_cred_proc_ref(proc);
3554 thread_ro_update_cred(tro, cred);
3555 kauth_cred_unref(&cred);
3556 }
3557
3558 /*
3559 * kauth_cred_thread_update
3560 *
3561 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3562 * late-bind the uthread cred to the proc cred.
3563 *
3564 * Parameters: thread_t The thread to update
3565 * proc_t The process to update to
3566 *
3567 * Returns: (void)
3568 *
3569 * Notes: This code is common code called from system call or trap entry
3570 * in the case that the process thread may have been changed
3571 * since the last time the thread entered the kernel.
3572 */
3573 __attribute__((always_inline))
3574 void
kauth_cred_thread_update(thread_t thread,proc_t proc)3575 kauth_cred_thread_update(thread_t thread, proc_t proc)
3576 {
3577 thread_ro_t tro = get_thread_ro(thread);
3578
3579 if (__improbable(tro->tro_task != kernel_task &&
3580 tro->tro_cred != proc_ucred(proc) &&
3581 (tro->tro_flags & TRO_SETUID) == 0)) {
3582 kauth_cred_thread_update_slow(tro, proc);
3583 }
3584 }
3585
3586
3587 /*
3588 * kauth_cred_get_with_ref
3589 *
3590 * Description: Takes a reference on the current thread's credential, and then
3591 * returns a pointer to it to the caller.
3592 *
3593 * Parameters: (void)
3594 *
3595 * Returns: (kauth_cred_t) Pointer to the current thread's
3596 * newly referenced credential
3597 *
3598 * Notes: This function takes a reference on the credential before
3599 * returning it to the caller.
3600 *
3601 * It is the responsibility of the calling code to release this
3602 * reference when the credential is no longer in use.
3603 *
3604 * Since the returned reference may be a persistent reference
3605 * (e.g. one cached in another data structure with a lifetime
3606 * longer than the calling function), this release may be delayed
3607 * until such time as the persistent reference is to be destroyed.
3608 * An example of this would be the per vnode credential cache used
3609 * to accelerate lookup operations.
3610 */
3611 kauth_cred_t
kauth_cred_get_with_ref(void)3612 kauth_cred_get_with_ref(void)
3613 {
3614 struct ucred *ucred = kauth_cred_get();
3615 kauth_cred_ref(ucred);
3616 return ucred;
3617 }
3618
3619
3620 /*
3621 * kauth_cred_proc_ref
3622 *
3623 * Description: Takes a reference on the current process's credential, and
3624 * then returns a pointer to it to the caller.
3625 *
3626 * Parameters: procp Process whose credential we
3627 * intend to take a reference on
3628 *
3629 * Returns: (kauth_cred_t) Pointer to the process's
3630 * newly referenced credential
3631 *
3632 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
3633 * after the refeence is taken to protect the p_ucred field of
3634 * the process referred to by procp.
3635 *
3636 * Notes: This function takes a reference on the credential before
3637 * returning it to the caller.
3638 *
3639 * It is the responsibility of the calling code to release this
3640 * reference when the credential is no longer in use.
3641 *
3642 * Since the returned reference may be a persistent reference
3643 * (e.g. one cached in another data structure with a lifetime
3644 * longer than the calling function), this release may be delayed
3645 * until such time as the persistent reference is to be destroyed.
3646 * An example of this would be the per vnode credential cache used
3647 * to accelerate lookup operations.
3648 */
3649 kauth_cred_t
kauth_cred_proc_ref(proc_t procp)3650 kauth_cred_proc_ref(proc_t procp)
3651 {
3652 kauth_cred_t cred;
3653
3654 proc_ucred_lock(procp);
3655 cred = proc_ucred(procp);
3656 kauth_cred_ref(cred);
3657 proc_ucred_unlock(procp);
3658 return cred;
3659 }
3660
3661 /*
3662 * kauth_cred_alloc
3663 *
3664 * Description: Allocate a new credential
3665 *
3666 * Parameters: cred_setup Optional code block to
3667 * initialize the new credential.
3668 *
3669 * Returns: !NULL Newly allocated credential
3670 * NULL Insufficient memory
3671 *
3672 * Notes: The newly allocated credential is zero'ed as part of the
3673 * allocation process, with the exception of the reference
3674 * count, which is set to 0 to indicate the caller still has
3675 * to call kauth_cred_add().
3676 *
3677 * Since newly allocated credentials have no external pointers
3678 * referencing them, prior to making them visible in an externally
3679 * visible pointer (e.g. by adding them to the credential hash
3680 * cache) is the only legal time in which an existing credential
3681 * can be safely iinitialized or modified directly.
3682 *
3683 * After initialization, the caller is expected to call the
3684 * function kauth_cred_add() to add the credential to the hash
3685 * cache, after which time it's frozen and becomes publically
3686 * visible.
3687 *
3688 * The release protocol depends on kauth_hash_add() being called
3689 * before kauth_cred_rele() (there is a diagnostic panic which
3690 * will trigger if this protocol is not observed).
3691 *
3692 * XXX: This function really ought to be static, rather than being
3693 * exported as KPI, since a failure of kauth_cred_add() can only
3694 * be handled by an explicit free of the credential; such frees
3695 * depend on knowlegdge of the allocation method used, which is
3696 * permitted to change between kernel revisions.
3697 *
3698 * XXX: In the insufficient resource case, this code panic's rather
3699 * than returning a NULL pointer; the code that calls this
3700 * function needs to be audited before this can be changed.
3701 */
3702 static kauth_cred_t
3703 kauth_cred_alloc(void (^cred_setup)(kauth_cred_t))
3704 {
3705 kauth_cred_t newcred;
3706 struct ucred model_cred = {};
3707 struct ucred_rw *rw;
3708
3709 /* Set some defaults: */
3710 model_cred.cr_posix.cr_gmuid = KAUTH_UID_NONE;
3711 model_cred.cr_audit.as_aia_p = audit_default_aia_p;
3712 #if CONFIG_MACF
3713 mac_cred_label_init(&model_cred);
3714 #endif
3715
3716 /* Now allow caller setup: */
3717 if (cred_setup) {
3718 cred_setup(&model_cred);
3719 }
3720
3721 /* Continue with construction: */
3722 rw = zalloc_flags(ucred_rw_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
3723 lck_mtx_init(&rw->crw_lock, &ucred_rw_lock_group, LCK_ATTR_NULL);
3724 os_ref_init(&rw->crw_weak_ref, NULL);
3725
3726 model_cred.cr_rw = rw;
3727
3728 newcred = zalloc_ro(ZONE_ID_KAUTH_CRED, Z_WAITOK | Z_ZERO | Z_NOFAIL);
3729 rw->crw_cred = newcred;
3730
3731 #if HAS_APPLE_PAC
3732 {
3733 void *naked_ptr = model_cred.cr_label;
3734 void *signed_ptr;
3735 signed_ptr = ptrauth_sign_unauthenticated(naked_ptr,
3736 ptrauth_key_process_independent_data,
3737 ptrauth_blend_discriminator(&newcred->cr_label,
3738 OS_PTRAUTH_DISCRIMINATOR("ucred.cr_label")));
3739 memcpy((void *)&model_cred.cr_label, &signed_ptr, sizeof(void *));
3740 }
3741 #endif
3742
3743 zalloc_ro_update_elem(ZONE_ID_KAUTH_CRED, newcred, &model_cred);
3744 return newcred;
3745 }
3746
3747 kauth_cred_t
kauth_cred_require(kauth_cred_t cred)3748 kauth_cred_require(kauth_cred_t cred)
3749 {
3750 zone_require_ro(ZONE_ID_KAUTH_CRED, sizeof(struct ucred), cred);
3751 return cred;
3752 }
3753
3754 __abortlike
3755 static void
kauth_cred_verify_panic(kauth_cred_t cred,struct ucred_rw * cred_rw)3756 kauth_cred_verify_panic(kauth_cred_t cred, struct ucred_rw *cred_rw)
3757 {
3758 panic("kauth_cred_t backref mismatch: cred:%p cred->cr_rw:%p "
3759 "cred_rw:%p", cred, cred->cr_rw, cred_rw);
3760 }
3761
3762 __pure2
3763 static struct ucred_rw *
kauth_cred_rw(kauth_cred_t cred)3764 kauth_cred_rw(kauth_cred_t cred)
3765 {
3766 struct ucred_rw *rw = kauth_cred_require(cred)->cr_rw;
3767
3768 if (__improbable(rw->crw_cred != cred)) {
3769 kauth_cred_verify_panic(cred, rw);
3770 }
3771
3772 return rw;
3773 }
3774
3775 __abortlike
3776 static void
kauth_cred_rw_verify_panic(struct ucred_rw * cred_rw,kauth_cred_t cred)3777 kauth_cred_rw_verify_panic(struct ucred_rw *cred_rw, kauth_cred_t cred)
3778 {
3779 panic("ucred_rw backref mismatch: cred_rw:%p cred_rw->crw_cred:%p "
3780 "cred: %p", cred_rw, cred_rw->crw_cred, cred);
3781 }
3782
3783 __pure2
3784 static kauth_cred_t
kauth_cred_ro(struct ucred_rw * cred_rw)3785 kauth_cred_ro(struct ucred_rw *cred_rw)
3786 {
3787 kauth_cred_t cred = kauth_cred_require(cred_rw->crw_cred);
3788
3789 if (__improbable(cred->cr_rw != cred_rw)) {
3790 kauth_cred_rw_verify_panic(cred_rw, cred);
3791 }
3792
3793 return cred;
3794 }
3795
3796 /*
3797 * kauth_cred_free
3798 *
3799 * Description: Destroy a credential
3800 *
3801 * Parameters: cred Credential to destroy.
3802 */
3803 __attribute__((noinline))
3804 static void
kauth_cred_free(kauth_cred_t cred,bool remove)3805 kauth_cred_free(kauth_cred_t cred, bool remove)
3806 {
3807 struct ucred_rw *rw = kauth_cred_rw(cred);
3808 #if CONFIG_MACF
3809 struct ucred mut_copy = *cred;
3810 #endif
3811
3812 if (cred == vfs_context0.vc_ucred) {
3813 panic("Over-release of the kernel credentials");
3814 }
3815
3816 if (remove) {
3817 KAUTH_CRED_HASH_LOCK();
3818 kauth_cred_remove_locked(rw);
3819 KAUTH_CRED_HASH_UNLOCK();
3820 }
3821
3822 if (os_atomic_load(&cred->cr_ref, relaxed) != 0) {
3823 panic("%s: freeing credential with active long-term ref", __func__);
3824 }
3825
3826 lck_mtx_destroy(&rw->crw_lock, &ucred_rw_lock_group);
3827
3828 #if CONFIG_MACF
3829 mac_cred_label_destroy(&mut_copy);
3830 #endif
3831 AUDIT_SESSION_UNREF(cred);
3832
3833 zfree(ucred_rw_zone, rw);
3834 zfree_ro(ZONE_ID_KAUTH_CRED, cred);
3835 }
3836
3837 /*
3838 * kauth_cred_create
3839 *
3840 * Description: Look to see if we already have a known credential in the hash
3841 * cache; if one is found, bump the reference count and return
3842 * it. If there are no credentials that match the given
3843 * credential, then allocate a new credential.
3844 *
3845 * Parameters: cred Template for credential to
3846 * be created
3847 *
3848 * Returns: (kauth_cred_t) The credential that was found
3849 * in the hash or created
3850 * NULL kauth_cred_add() failed, or
3851 * there was not an egid specified
3852 *
3853 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3854 * maintain this field, we can't expect callers to know how it
3855 * needs to be set. Callers should be prepared for this field
3856 * to be overwritten.
3857 */
3858 kauth_cred_t
kauth_cred_create(kauth_cred_t cred)3859 kauth_cred_create(kauth_cred_t cred)
3860 {
3861 kauth_cred_t found_cred, new_cred = NULL;
3862 posix_cred_t pcred = posix_cred_get(cred);
3863 int is_member = 0;
3864
3865 if (pcred->cr_flags & CRF_NOMEMBERD) {
3866 pcred->cr_gmuid = KAUTH_UID_NONE;
3867 } else {
3868 /*
3869 * If the template credential is not opting out of external
3870 * group membership resolution, then we need to check that
3871 * the UID we will be using is resolvable by the external
3872 * resolver. If it's not, then we opt it out anyway, since
3873 * all future external resolution requests will be failing
3874 * anyway, and potentially taking a long time to do it. We
3875 * use gid 0 because we always know it will exist and not
3876 * trigger additional lookups. This is OK, because we end up
3877 * precatching the information here as a result.
3878 */
3879 if (!kauth_cred_ismember_gid(cred, 0, &is_member)) {
3880 /*
3881 * It's a recognized value; we don't really care about
3882 * the answer, so long as it's something the external
3883 * resolver could have vended.
3884 */
3885 pcred->cr_gmuid = pcred->cr_uid;
3886 } else {
3887 /*
3888 * It's not something the external resolver could
3889 * have vended, so we don't want to ask it more
3890 * questions about the credential in the future. This
3891 * speeds up future lookups, as long as the caller
3892 * caches results; otherwise, it the same recurring
3893 * cost. Since most credentials are used multiple
3894 * times, we still get some performance win from this.
3895 */
3896 pcred->cr_gmuid = KAUTH_UID_NONE;
3897 pcred->cr_flags |= CRF_NOMEMBERD;
3898 }
3899 }
3900
3901 /* Caller *must* specify at least the egid in cr_groups[0] */
3902 if (pcred->cr_ngroups < 1) {
3903 return NULL;
3904 }
3905
3906 struct kauth_cred_entry_head *bucket = kauth_cred_get_bucket(cred);
3907
3908 KAUTH_CRED_HASH_LOCK();
3909 found_cred = kauth_cred_find_and_ref(cred, bucket);
3910 KAUTH_CRED_HASH_UNLOCK();
3911 if (found_cred != NULL) {
3912 return found_cred;
3913 }
3914
3915 /*
3916 * No existing credential found. Create one and add it to
3917 * our hash table.
3918 */
3919 new_cred = kauth_cred_alloc(^(kauth_cred_t setup_cred) {
3920 *posix_cred_get(setup_cred) = *pcred;
3921 #if CONFIG_AUDIT
3922 setup_cred->cr_audit = cred->cr_audit;
3923 #endif
3924 });
3925
3926 new_cred = kauth_cred_add(new_cred, bucket);
3927
3928 return new_cred;
3929 }
3930
3931
3932 /*
3933 * kauth_cred_setresuid
3934 *
3935 * Description: Update the given credential using the UID arguments. The given
3936 * UIDs are used to set the effective UID, real UID, saved UID,
3937 * and GMUID (used for group membership checking).
3938 *
3939 * Parameters: cred The original credential
3940 * ruid The new real UID
3941 * euid The new effective UID
3942 * svuid The new saved UID
3943 * gmuid KAUTH_UID_NONE -or- the new
3944 * group membership UID
3945 *
3946 * Returns: (kauth_cred_t) The updated credential
3947 *
3948 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3949 * setting, so if you don't want it to change, pass it the
3950 * previous value, explicitly.
3951 *
3952 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3953 * if it returns a credential other than the one it is passed,
3954 * will have dropped the reference on the passed credential. All
3955 * callers should be aware of this, and treat this function as an
3956 * unref + ref, potentially on different credentials.
3957 *
3958 * Because of this, the caller is expected to take its own
3959 * reference on the credential passed as the first parameter,
3960 * and be prepared to release the reference on the credential
3961 * that is returned to them, if it is not intended to be a
3962 * persistent reference.
3963 */
3964 kauth_cred_t
kauth_cred_setresuid(kauth_cred_t cred,uid_t ruid,uid_t euid,uid_t svuid,uid_t gmuid)3965 kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
3966 {
3967 struct ucred temp_cred;
3968 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3969 posix_cred_t pcred = posix_cred_get(cred);
3970
3971 /*
3972 * We don't need to do anything if the UIDs we are changing are
3973 * already the same as the UIDs passed in
3974 */
3975 if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) &&
3976 (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) &&
3977 (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) &&
3978 (pcred->cr_gmuid == gmuid)) {
3979 /* no change needed */
3980 return cred;
3981 }
3982
3983 /*
3984 * Look up in cred hash table to see if we have a matching credential
3985 * with the new values; this is done by calling kauth_cred_update().
3986 */
3987 temp_cred = *cred;
3988 if (euid != KAUTH_UID_NONE) {
3989 temp_pcred->cr_uid = euid;
3990 }
3991 if (ruid != KAUTH_UID_NONE) {
3992 temp_pcred->cr_ruid = ruid;
3993 }
3994 if (svuid != KAUTH_UID_NONE) {
3995 temp_pcred->cr_svuid = svuid;
3996 }
3997
3998 /*
3999 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
4000 * opt out of participation in external group resolution, unless we
4001 * unless we explicitly opt back in later.
4002 */
4003 if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) {
4004 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4005 }
4006
4007 return kauth_cred_update(cred, &temp_cred, TRUE);
4008 }
4009
4010
4011 /*
4012 * kauth_cred_setresgid
4013 *
4014 * Description: Update the given credential using the GID arguments. The given
4015 * GIDs are used to set the effective GID, real GID, and saved
4016 * GID.
4017 *
4018 * Parameters: cred The original credential
4019 * rgid The new real GID
4020 * egid The new effective GID
4021 * svgid The new saved GID
4022 *
4023 * Returns: (kauth_cred_t) The updated credential
4024 *
4025 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4026 * if it returns a credential other than the one it is passed,
4027 * will have dropped the reference on the passed credential. All
4028 * callers should be aware of this, and treat this function as an
4029 * unref + ref, potentially on different credentials.
4030 *
4031 * Because of this, the caller is expected to take its own
4032 * reference on the credential passed as the first parameter,
4033 * and be prepared to release the reference on the credential
4034 * that is returned to them, if it is not intended to be a
4035 * persistent reference.
4036 */
4037 kauth_cred_t
kauth_cred_setresgid(kauth_cred_t cred,gid_t rgid,gid_t egid,gid_t svgid)4038 kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
4039 {
4040 struct ucred temp_cred;
4041 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4042 posix_cred_t pcred = posix_cred_get(cred);
4043
4044 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
4045
4046 /*
4047 * We don't need to do anything if the given GID are already the
4048 * same as the GIDs in the credential.
4049 */
4050 if (pcred->cr_groups[0] == egid &&
4051 pcred->cr_rgid == rgid &&
4052 pcred->cr_svgid == svgid) {
4053 /* no change needed */
4054 return cred;
4055 }
4056
4057 /*
4058 * Look up in cred hash table to see if we have a matching credential
4059 * with the new values; this is done by calling kauth_cred_update().
4060 */
4061 temp_cred = *cred;
4062 if (egid != KAUTH_GID_NONE) {
4063 /* displacing a supplementary group opts us out of memberd */
4064 if (kauth_cred_change_egid(&temp_cred, egid)) {
4065 DEBUG_CRED_CHANGE("displaced!\n");
4066 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4067 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4068 } else {
4069 DEBUG_CRED_CHANGE("not displaced\n");
4070 }
4071 }
4072 if (rgid != KAUTH_GID_NONE) {
4073 temp_pcred->cr_rgid = rgid;
4074 }
4075 if (svgid != KAUTH_GID_NONE) {
4076 temp_pcred->cr_svgid = svgid;
4077 }
4078
4079 return kauth_cred_update(cred, &temp_cred, TRUE);
4080 }
4081
4082
4083 /*
4084 * Update the given credential with the given groups. We only allocate a new
4085 * credential when the given gid actually results in changes to the existing
4086 * credential.
4087 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4088 * which will be used for group membership checking.
4089 */
4090 /*
4091 * kauth_cred_setgroups
4092 *
4093 * Description: Update the given credential using the provide supplementary
4094 * group list and group membership UID
4095 *
4096 * Parameters: cred The original credential
4097 * groups Pointer to gid_t array which
4098 * contains the new group list
4099 * groupcount The count of valid groups which
4100 * are contained in 'groups'
4101 * gmuid KAUTH_UID_NONE -or- the new
4102 * group membership UID
4103 *
4104 * Returns: (kauth_cred_t) The updated credential
4105 *
4106 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4107 * setting, so if you don't want it to change, pass it the
4108 * previous value, explicitly.
4109 *
4110 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4111 * if it returns a credential other than the one it is passed,
4112 * will have dropped the reference on the passed credential. All
4113 * callers should be aware of this, and treat this function as an
4114 * unref + ref, potentially on different credentials.
4115 *
4116 * Because of this, the caller is expected to take its own
4117 * reference on the credential passed as the first parameter,
4118 * and be prepared to release the reference on the credential
4119 * that is returned to them, if it is not intended to be a
4120 * persistent reference.
4121 *
4122 * XXX: Changes are determined in ordinal order - if the caller passes
4123 * in the same groups list that is already present in the
4124 * credential, but the members are in a different order, even if
4125 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4126 * is considered a modification to the credential, and a new
4127 * credential is created.
4128 *
4129 * This should perhaps be better optimized, but it is considered
4130 * to be the caller's problem.
4131 */
4132 kauth_cred_t
kauth_cred_setgroups(kauth_cred_t cred,gid_t * groups,size_t groupcount,uid_t gmuid)4133 kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, size_t groupcount, uid_t gmuid)
4134 {
4135 size_t i;
4136 struct ucred temp_cred;
4137 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4138 posix_cred_t pcred;
4139
4140 assert(groupcount <= NGROUPS);
4141 groupcount = MIN(groupcount, NGROUPS);
4142
4143 pcred = posix_cred_get(cred);
4144
4145 /*
4146 * We don't need to do anything if the given list of groups does not
4147 * change.
4148 */
4149 if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) {
4150 for (i = 0; i < groupcount; i++) {
4151 if (pcred->cr_groups[i] != groups[i]) {
4152 break;
4153 }
4154 }
4155 if (i == groupcount) {
4156 /* no change needed */
4157 return cred;
4158 }
4159 }
4160
4161 /*
4162 * Look up in cred hash table to see if we have a matching credential
4163 * with new values. If we are setting or clearing the gmuid, then
4164 * update the cr_flags, since clearing it is sticky. This permits an
4165 * opt-out of memberd processing using setgroups(), and an opt-in
4166 * using initgroups(). This is required for POSIX conformance.
4167 */
4168 temp_cred = *cred;
4169 temp_pcred->cr_ngroups = (short)groupcount;
4170 bcopy(groups, temp_pcred->cr_groups, groupcount * sizeof(temp_pcred->cr_groups[0]));
4171 temp_pcred->cr_gmuid = gmuid;
4172 if (gmuid == KAUTH_UID_NONE) {
4173 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4174 } else {
4175 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
4176 }
4177
4178 return kauth_cred_update(cred, &temp_cred, TRUE);
4179 }
4180
4181 /*
4182 * Notes: The return value exists to account for the possibility of a
4183 * kauth_cred_t without a POSIX label. This will be the case in
4184 * the future (see posix_cred_get() below, for more details).
4185 */
4186 #if CONFIG_EXT_RESOLVER
4187 int kauth_external_supplementary_groups_supported = 1;
4188
4189 SYSCTL_INT(_kern, OID_AUTO, ds_supgroups_supported, CTLFLAG_RW | CTLFLAG_LOCKED, &kauth_external_supplementary_groups_supported, 0, "");
4190 #endif
4191
4192 int
kauth_cred_getgroups(kauth_cred_t cred,gid_t * grouplist,size_t * countp)4193 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, size_t *countp)
4194 {
4195 size_t limit = NGROUPS;
4196 posix_cred_t pcred;
4197
4198 if (cred == NULL) {
4199 KAUTH_DEBUG("kauth_cred_getgroups got NULL credential");
4200 return EINVAL;
4201 }
4202
4203 if (grouplist == NULL) {
4204 KAUTH_DEBUG("kauth_cred_getgroups got NULL group list");
4205 return EINVAL;
4206 }
4207
4208 pcred = posix_cred_get(cred);
4209
4210 #if CONFIG_EXT_RESOLVER
4211 /*
4212 * If we've not opted out of using the resolver, then convert the cred to a list
4213 * of supplemental groups. We do this only if there has been a resolver to talk to,
4214 * since we may be too early in boot, or in an environment that isn't using DS.
4215 */
4216 if (kauth_identitysvc_has_registered && kauth_external_supplementary_groups_supported && (pcred->cr_flags & CRF_NOMEMBERD) == 0) {
4217 uid_t uid = kauth_cred_getuid(cred);
4218 int err;
4219
4220 err = kauth_cred_uid2groups(&uid, grouplist, countp);
4221 if (!err) {
4222 return 0;
4223 }
4224
4225 /* On error just fall through */
4226 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err);
4227 }
4228 #endif /* CONFIG_EXT_RESOLVER */
4229
4230 /*
4231 * If they just want a copy of the groups list, they may not care
4232 * about the actual count. If they specify an input count, however,
4233 * treat it as an indicator of the buffer size available in grouplist,
4234 * and limit the returned list to that size.
4235 */
4236 if (countp) {
4237 limit = MIN(*countp, pcred->cr_ngroups);
4238 *countp = limit;
4239 }
4240
4241 memcpy(grouplist, pcred->cr_groups, sizeof(gid_t) * limit);
4242
4243 return 0;
4244 }
4245
4246
4247 /*
4248 * kauth_cred_setuidgid
4249 *
4250 * Description: Update the given credential using the UID and GID arguments.
4251 * The given UID is used to set the effective UID, real UID, and
4252 * saved UID. The given GID is used to set the effective GID,
4253 * real GID, and saved GID.
4254 *
4255 * Parameters: cred The original credential
4256 * uid The new UID to use
4257 * gid The new GID to use
4258 *
4259 * Returns: (kauth_cred_t) The updated credential
4260 *
4261 * Notes: We set the gmuid to uid if the credential we are inheriting
4262 * from has not opted out of memberd participation; otherwise
4263 * we set it to KAUTH_UID_NONE
4264 *
4265 * This code is only ever called from the per-thread credential
4266 * code path in the "set per thread credential" case; and in
4267 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4268 * flag is set.
4269 *
4270 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4271 * if it returns a credential other than the one it is passed,
4272 * will have dropped the reference on the passed credential. All
4273 * callers should be aware of this, and treat this function as an
4274 * unref + ref, potentially on different credentials.
4275 *
4276 * Because of this, the caller is expected to take its own
4277 * reference on the credential passed as the first parameter,
4278 * and be prepared to release the reference on the credential
4279 * that is returned to them, if it is not intended to be a
4280 * persistent reference.
4281 */
4282 kauth_cred_t
kauth_cred_setuidgid(kauth_cred_t cred,uid_t uid,gid_t gid)4283 kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4284 {
4285 struct ucred temp_cred;
4286 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4287 posix_cred_t pcred;
4288
4289 pcred = posix_cred_get(cred);
4290
4291 /*
4292 * We don't need to do anything if the effective, real and saved
4293 * user IDs are already the same as the user ID passed into us.
4294 */
4295 if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid &&
4296 pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) {
4297 /* no change needed */
4298 return cred;
4299 }
4300
4301 /*
4302 * Look up in cred hash table to see if we have a matching credential
4303 * with the new values.
4304 */
4305 bzero(&temp_cred, sizeof(temp_cred));
4306 temp_pcred->cr_uid = uid;
4307 temp_pcred->cr_ruid = uid;
4308 temp_pcred->cr_svuid = uid;
4309 temp_pcred->cr_flags = pcred->cr_flags;
4310 /* inherit the opt-out of memberd */
4311 if (pcred->cr_flags & CRF_NOMEMBERD) {
4312 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4313 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4314 } else {
4315 temp_pcred->cr_gmuid = uid;
4316 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
4317 }
4318 temp_pcred->cr_ngroups = 1;
4319 /* displacing a supplementary group opts us out of memberd */
4320 if (kauth_cred_change_egid(&temp_cred, gid)) {
4321 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4322 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4323 }
4324 temp_pcred->cr_rgid = gid;
4325 temp_pcred->cr_svgid = gid;
4326 #if CONFIG_MACF
4327 temp_cred.cr_label = mac_cred_label(cred);
4328 #endif
4329
4330 return kauth_cred_update(cred, &temp_cred, TRUE);
4331 }
4332
4333
4334 /*
4335 * kauth_cred_setsvuidgid
4336 *
4337 * Description: Function used by execve to set the saved uid and gid values
4338 * for suid/sgid programs
4339 *
4340 * Parameters: cred The credential to update
4341 * uid The saved uid to set
4342 * gid The saved gid to set
4343 *
4344 * Returns: (kauth_cred_t) The updated credential
4345 *
4346 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4347 * if it returns a credential other than the one it is passed,
4348 * will have dropped the reference on the passed credential. All
4349 * callers should be aware of this, and treat this function as an
4350 * unref + ref, potentially on different credentials.
4351 *
4352 * Because of this, the caller is expected to take its own
4353 * reference on the credential passed as the first parameter,
4354 * and be prepared to release the reference on the credential
4355 * that is returned to them, if it is not intended to be a
4356 * persistent reference.
4357 */
4358 kauth_cred_t
kauth_cred_setsvuidgid(kauth_cred_t cred,uid_t uid,gid_t gid)4359 kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4360 {
4361 struct ucred temp_cred;
4362 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4363 posix_cred_t pcred;
4364
4365 pcred = posix_cred_get(cred);
4366
4367 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
4368
4369 /*
4370 * We don't need to do anything if the effective, real and saved
4371 * uids are already the same as the uid provided. This check is
4372 * likely insufficient.
4373 */
4374 if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) {
4375 /* no change needed */
4376 return cred;
4377 }
4378 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
4379
4380 /* look up in cred hash table to see if we have a matching credential
4381 * with new values.
4382 */
4383 temp_cred = *cred;
4384 temp_pcred->cr_svuid = uid;
4385 temp_pcred->cr_svgid = gid;
4386
4387 return kauth_cred_update(cred, &temp_cred, TRUE);
4388 }
4389
4390
4391 /*
4392 * kauth_cred_setauditinfo
4393 *
4394 * Description: Update the given credential using the given au_session_t.
4395 *
4396 * Parameters: cred The original credential
4397 * auditinfo_p Pointer to ne audit information
4398 *
4399 * Returns: (kauth_cred_t) The updated credential
4400 *
4401 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4402 * if it returns a credential other than the one it is passed,
4403 * will have dropped the reference on the passed credential. All
4404 * callers should be aware of this, and treat this function as an
4405 * unref + ref, potentially on different credentials.
4406 *
4407 * Because of this, the caller is expected to take its own
4408 * reference on the credential passed as the first parameter,
4409 * and be prepared to release the reference on the credential
4410 * that is returned to them, if it is not intended to be a
4411 * persistent reference.
4412 */
4413 kauth_cred_t
kauth_cred_setauditinfo(kauth_cred_t cred,au_session_t * auditinfo_p)4414 kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
4415 {
4416 struct ucred temp_cred;
4417
4418 /*
4419 * We don't need to do anything if the audit info is already the
4420 * same as the audit info in the credential provided.
4421 */
4422 if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) {
4423 /* no change needed */
4424 return cred;
4425 }
4426
4427 temp_cred = *cred;
4428 bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
4429
4430 return kauth_cred_update(cred, &temp_cred, FALSE);
4431 }
4432
4433 #if CONFIG_MACF
4434 /*
4435 * kauth_cred_label_update
4436 *
4437 * Description: Update the MAC label associated with a credential
4438 *
4439 * Parameters: cred The original credential
4440 * label The MAC label to set
4441 *
4442 * Returns: (kauth_cred_t) The updated credential
4443 *
4444 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4445 * if it returns a credential other than the one it is passed,
4446 * will have dropped the reference on the passed credential. All
4447 * callers should be aware of this, and treat this function as an
4448 * unref + ref, potentially on different credentials.
4449 *
4450 * Because of this, the caller is expected to take its own
4451 * reference on the credential passed as the first parameter,
4452 * and be prepared to release the reference on the credential
4453 * that is returned to them, if it is not intended to be a
4454 * persistent reference.
4455 */
4456 kauth_cred_t
kauth_cred_label_update(kauth_cred_t cred,struct label * label)4457 kauth_cred_label_update(kauth_cred_t cred, struct label *label)
4458 {
4459 kauth_cred_t newcred;
4460 struct ucred temp_cred;
4461
4462 temp_cred = *cred;
4463
4464 mac_cred_label_init(&temp_cred);
4465 mac_cred_label_associate(cred, &temp_cred);
4466 mac_cred_label_update(&temp_cred, label);
4467
4468 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4469 mac_cred_label_destroy(&temp_cred);
4470 return newcred;
4471 }
4472
4473 /*
4474 * kauth_cred_label_update_execve
4475 *
4476 * Description: Update the MAC label associated with a credential as
4477 * part of exec
4478 *
4479 * Parameters: cred The original credential
4480 * vp The exec vnode
4481 * scriptl The script MAC label
4482 * execl The executable MAC label
4483 * disjointp Pointer to flag to set if old
4484 * and returned credentials are
4485 * disjoint
4486 *
4487 * Returns: (kauth_cred_t) The updated credential
4488 *
4489 * Implicit returns:
4490 * *disjointp Set to 1 for disjoint creds
4491 *
4492 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4493 * if it returns a credential other than the one it is passed,
4494 * will have dropped the reference on the passed credential. All
4495 * callers should be aware of this, and treat this function as an
4496 * unref + ref, potentially on different credentials.
4497 *
4498 * Because of this, the caller is expected to take its own
4499 * reference on the credential passed as the first parameter,
4500 * and be prepared to release the reference on the credential
4501 * that is returned to them, if it is not intended to be a
4502 * persistent reference.
4503 */
4504
4505 static
4506 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)4507 kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
4508 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4509 struct label *execl, unsigned int *csflags, void *macextensions, int *disjointp, int *labelupdateerror)
4510 {
4511 kauth_cred_t newcred;
4512 struct ucred temp_cred;
4513
4514 temp_cred = *cred;
4515
4516 mac_cred_label_init(&temp_cred);
4517 mac_cred_label_associate(cred, &temp_cred);
4518 mac_cred_label_update_execve(ctx, &temp_cred,
4519 vp, offset, scriptvp, scriptl, execl, csflags,
4520 macextensions, disjointp, labelupdateerror);
4521
4522 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4523 mac_cred_label_destroy(&temp_cred);
4524 return newcred;
4525 }
4526
4527 /*
4528 * kauth_proc_label_update
4529 *
4530 * Description: Update the label inside the credential associated with the process.
4531 *
4532 * Parameters: p The process to modify
4533 * label The label to place in the process credential
4534 *
4535 * Notes: The credential associated with the process may change as a result
4536 * of this call. The caller should not assume the process reference to
4537 * the old credential still exists.
4538 */
4539 int
kauth_proc_label_update(struct proc * p,struct label * label)4540 kauth_proc_label_update(struct proc *p, struct label *label)
4541 {
4542 proc_update_label(p, false, ^(kauth_cred_t cred) {
4543 return kauth_cred_label_update(cred, label);
4544 });
4545 return 0;
4546 }
4547
4548 /*
4549 * kauth_proc_label_update_execve
4550 *
4551 * Description: Update the label inside the credential associated with the
4552 * process as part of a transitioning execve. The label will
4553 * be updated by the policies as part of this processing, not
4554 * provided up front.
4555 *
4556 * Parameters: p The process to modify
4557 * ctx The context of the exec
4558 * vp The vnode being exec'ed
4559 * scriptl The script MAC label
4560 * execl The executable MAC label
4561 * lupdateerror The error place holder for MAC label authority
4562 * to update about possible termination
4563 *
4564 * Returns: 0 Label update did not make credential
4565 * disjoint
4566 * 1 Label update caused credential to be
4567 * disjoint
4568 *
4569 * Notes: The credential associated with the process WILL change as a
4570 * result of this call. The caller should not assume the process
4571 * reference to the old credential still exists.
4572 */
4573
4574 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)4575 kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
4576 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4577 struct label *execl, unsigned int *csflags, void *macextensions, int *disjoint, int *update_return)
4578 {
4579 proc_update_label(p, false, ^(kauth_cred_t cred) {
4580 return kauth_cred_label_update_execve(cred, ctx, vp, offset,
4581 scriptvp, scriptl, execl, csflags, macextensions, disjoint,
4582 update_return);
4583 });
4584 }
4585
4586 #if 1
4587 /*
4588 * for temporary binary compatibility
4589 */
4590 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
4591 kauth_cred_t
kauth_cred_setlabel(kauth_cred_t cred,struct label * label)4592 kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
4593 {
4594 return kauth_cred_label_update(cred, label);
4595 }
4596
4597 int kauth_proc_setlabel(struct proc *p, struct label *label);
4598 int
kauth_proc_setlabel(struct proc * p,struct label * label)4599 kauth_proc_setlabel(struct proc *p, struct label *label)
4600 {
4601 return kauth_proc_label_update(p, label);
4602 }
4603 #endif
4604
4605 #else
4606
4607 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4608 * Since we cannot build our export lists based on the kernel configuration we need
4609 * to define a stub.
4610 */
4611 kauth_cred_t
kauth_cred_label_update(__unused kauth_cred_t cred,__unused void * label)4612 kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
4613 {
4614 return NULL;
4615 }
4616
4617 int
kauth_proc_label_update(__unused struct proc * p,__unused void * label)4618 kauth_proc_label_update(__unused struct proc *p, __unused void *label)
4619 {
4620 return 0;
4621 }
4622
4623 #if 1
4624 /*
4625 * for temporary binary compatibility
4626 */
4627 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
4628 kauth_cred_t
kauth_cred_setlabel(__unused kauth_cred_t cred,__unused void * label)4629 kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
4630 {
4631 return NULL;
4632 }
4633
4634 int kauth_proc_setlabel(struct proc *p, void *label);
4635 int
kauth_proc_setlabel(__unused struct proc * p,__unused void * label)4636 kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
4637 {
4638 return 0;
4639 }
4640 #endif
4641 #endif
4642
4643 #define KAUTH_CRED_REF_MAX 0x0ffffffful
4644
4645 __attribute__((noinline, cold, noreturn))
4646 static void
kauth_cred_panic_over_released(kauth_cred_t cred)4647 kauth_cred_panic_over_released(kauth_cred_t cred)
4648 {
4649 panic("kauth_cred_unref: cred %p over-released", cred);
4650 __builtin_unreachable();
4651 }
4652
4653 __attribute__((noinline, cold, noreturn))
4654 static void
kauth_cred_panic_over_retain(kauth_cred_t cred)4655 kauth_cred_panic_over_retain(kauth_cred_t cred)
4656 {
4657 panic("kauth_cred_ref: cred %p over-retained", cred);
4658 __builtin_unreachable();
4659 }
4660
4661 /*
4662 * kauth_cred_tryref
4663 *
4664 * Description: Tries to take a reference, used from kauth_cred_find_and_ref
4665 * to debounce the race with kauth_cred_unref.
4666 *
4667 * Parameters: cred The credential to reference
4668 *
4669 * Returns: (bool) Whether the reference was taken
4670 */
4671 static inline bool
kauth_cred_tryref(kauth_cred_t cred)4672 kauth_cred_tryref(kauth_cred_t cred)
4673 {
4674 return os_ref_retain_try(&kauth_cred_rw(cred)->crw_weak_ref);
4675 }
4676
4677 /*
4678 * kauth_cred_ref
4679 *
4680 * Description: Add a reference to the passed credential
4681 *
4682 * Parameters: cred The credential to reference
4683 *
4684 * Returns: (void)
4685 */
4686 void
kauth_cred_ref(kauth_cred_t cred)4687 kauth_cred_ref(kauth_cred_t cred)
4688 {
4689 os_ref_retain(&kauth_cred_rw(cred)->crw_weak_ref);
4690 }
4691
4692 /*
4693 * kauth_cred_unref_fast
4694 *
4695 * Description: Release a credential reference.
4696 *
4697 * Parameters: credp Pointer to address containing
4698 * credential to be freed
4699 *
4700 * Returns: true This was the last reference.
4701 * false The object has more refs.
4702 *
4703 */
4704 static inline bool
kauth_cred_unref_fast(kauth_cred_t cred)4705 kauth_cred_unref_fast(kauth_cred_t cred)
4706 {
4707 return os_ref_release(&kauth_cred_rw(cred)->crw_weak_ref) == 0;
4708 }
4709
4710 /*
4711 * kauth_cred_unref
4712 *
4713 * Description: Release a credential reference.
4714 * Frees the credential if it is the last ref.
4715 *
4716 * Parameters: credp Pointer to address containing
4717 * credential to be freed
4718 *
4719 * Returns: (void)
4720 *
4721 * Implicit returns:
4722 * *credp Set to NOCRED
4723 *
4724 */
4725 void
4726 (kauth_cred_unref)(kauth_cred_t * credp)
4727 {
4728 if (kauth_cred_unref_fast(*credp)) {
4729 kauth_cred_free(*credp, true);
4730 }
4731
4732 *credp = NOCRED;
4733 }
4734
4735 static void
kauth_cred_set_ref_locked(kauth_cred_t cred,unsigned long ref)4736 kauth_cred_set_ref_locked(kauth_cred_t cred, unsigned long ref)
4737 {
4738 zalloc_ro_update_field(ZONE_ID_KAUTH_CRED, cred, cr_ref, &ref);
4739 }
4740
4741 static unsigned long
kauth_cred_get_ref_locked(const kauth_cred_t cred)4742 kauth_cred_get_ref_locked(const kauth_cred_t cred)
4743 {
4744 return os_atomic_load(&cred->cr_ref, relaxed);
4745 }
4746
4747 static void
kauth_cred_hold(kauth_cred_t cred,bool need_ref)4748 kauth_cred_hold(kauth_cred_t cred, bool need_ref)
4749 {
4750 struct ucred_rw *rw;
4751 unsigned long ref;
4752
4753 if (need_ref) {
4754 kauth_cred_ref(cred);
4755 }
4756
4757 rw = kauth_cred_rw(cred);
4758 lck_mtx_lock(&rw->crw_lock);
4759 ref = kauth_cred_get_ref_locked(cred);
4760 if (ref >= KAUTH_CRED_REF_MAX) {
4761 kauth_cred_panic_over_retain(cred);
4762 }
4763 kauth_cred_set_ref_locked(cred, ref + 1);
4764 lck_mtx_unlock(&rw->crw_lock);
4765 }
4766
4767 static void
kauth_cred_drop(kauth_cred_t * credp)4768 kauth_cred_drop(kauth_cred_t *credp)
4769 {
4770 unsigned long ref;
4771 kauth_cred_t cred = *credp;
4772 struct ucred_rw *rw;
4773
4774 rw = kauth_cred_rw(cred);
4775
4776 lck_mtx_lock(&rw->crw_lock);
4777 ref = kauth_cred_get_ref_locked(cred);
4778 if (__improbable(ref == 0)) {
4779 kauth_cred_panic_over_released(cred);
4780 }
4781 kauth_cred_set_ref_locked(cred, ref - 1);
4782 lck_mtx_unlock(&rw->crw_lock);
4783
4784 kauth_cred_unref(credp);
4785 }
4786
4787 /*
4788 * kauth_cred_set
4789 *
4790 * Description: Store a long-term credential reference to a credential pointer,
4791 * dropping the long-term reference on any previous credential held
4792 * at the address.
4793 *
4794 * Parameters: credp Pointer to the credential
4795 * storage field. If *credp points
4796 * to a valid credential before
4797 * this call, its long-term
4798 * reference will be dropped.
4799 * new_cred The new credential to take a
4800 * long-term reference to and
4801 * assign to *credp. May be
4802 * NOCRED.
4803 *
4804 * Returns: (void)
4805 *
4806 * Notes: Taking/dropping a long-term reference is costly in terms of
4807 * performance.
4808 */
4809 void
4810 (kauth_cred_set)(kauth_cred_t * credp, kauth_cred_t new_cred)
4811 {
4812 kauth_cred_t old_cred = *credp;
4813
4814 if (old_cred != new_cred) {
4815 if (IS_VALID_CRED(new_cred)) {
4816 kauth_cred_hold(new_cred, true);
4817 }
4818
4819 *credp = new_cred;
4820
4821 if (IS_VALID_CRED(old_cred)) {
4822 kauth_cred_drop(&old_cred);
4823 }
4824 }
4825 }
4826
4827 void
4828 (kauth_cred_set_and_unref)(kauth_cred_t * credp, kauth_cred_t * new_credp)
4829 {
4830 kauth_cred_t old_cred = *credp;
4831 kauth_cred_t new_cred = *new_credp;
4832
4833 if (old_cred != new_cred) {
4834 /*
4835 * `new_cred` must be valid to be unref'd, so omit the
4836 * `IS_VALID_CRED` check.
4837 */
4838 kauth_cred_hold(new_cred, false);
4839 *credp = new_cred;
4840 *new_credp = NOCRED;
4841
4842 if (IS_VALID_CRED(old_cred)) {
4843 kauth_cred_drop(&old_cred);
4844 }
4845 } else {
4846 kauth_cred_unref(new_credp);
4847 }
4848 }
4849
4850 #ifndef __LP64__
4851 /*
4852 * kauth_cred_rele
4853 *
4854 * Description: release a credential reference; when the last reference is
4855 * released, the credential will be freed
4856 *
4857 * Parameters: cred Credential to release
4858 *
4859 * Returns: (void)
4860 *
4861 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4862 * clear the pointer in the caller to avoid multiple releases of
4863 * the same credential. The currently recommended interface is
4864 * kauth_cred_unref().
4865 */
4866 void
kauth_cred_rele(kauth_cred_t cred)4867 kauth_cred_rele(kauth_cred_t cred)
4868 {
4869 kauth_cred_unref(&cred);
4870 }
4871 #endif /* !__LP64__ */
4872
4873
4874 /*
4875 * kauth_cred_dup
4876 *
4877 * Description: Duplicate a credential via alloc and copy; the new credential
4878 * has only it's own
4879 *
4880 * Parameters: cred The credential to duplicate
4881 *
4882 * Returns: (kauth_cred_t) The duplicate credential
4883 *
4884 * Notes: The typical value to calling this routine is if you are going
4885 * to modify an existing credential, and expect to need a new one
4886 * from the hash cache.
4887 *
4888 * This should probably not be used in the majority of cases;
4889 * if you are using it instead of kauth_cred_create(), you are
4890 * likely making a mistake.
4891 *
4892 * The newly allocated credential is copied as part of the
4893 * allocation process, with the exception of the reference
4894 * count, which is set to 0 to indicate the caller still has
4895 * to call kauth_cred_add().
4896 *
4897 * Since newly allocated credentials have no external pointers
4898 * referencing them, prior to making them visible in an externally
4899 * visible pointer (e.g. by adding them to the credential hash
4900 * cache) is the only legal time in which an existing credential
4901 * can be safely initialized or modified directly.
4902 *
4903 * After initialization, the caller is expected to call the
4904 * function kauth_cred_add() to add the credential to the hash
4905 * cache, after which time it's frozen and becomes publicly
4906 * visible.
4907 *
4908 * The release protocol depends on kauth_hash_add() being called
4909 * before kauth_cred_rele() (there is a diagnostic panic which
4910 * will trigger if this protocol is not observed).
4911 *
4912 */
4913 static kauth_cred_t
kauth_cred_dup(kauth_cred_t cred)4914 kauth_cred_dup(kauth_cred_t cred)
4915 {
4916 kauth_cred_t newcred;
4917
4918 assert(cred != NOCRED && cred != FSCRED);
4919 newcred = kauth_cred_alloc(^(kauth_cred_t setup_cred) {
4920 setup_cred->cr_posix = cred->cr_posix;
4921 #if CONFIG_AUDIT
4922 setup_cred->cr_audit = cred->cr_audit;
4923 #endif
4924 #if CONFIG_MACF
4925 mac_cred_label_associate(cred, setup_cred);
4926 #endif
4927 AUDIT_SESSION_REF(setup_cred);
4928 });
4929
4930 return newcred;
4931 }
4932
4933 /*
4934 * kauth_cred_copy_real
4935 *
4936 * Description: Returns a credential based on the passed credential but which
4937 * reflects the real rather than effective UID and GID.
4938 *
4939 * Parameters: cred The credential from which to
4940 * derive the new credential
4941 *
4942 * Returns: (kauth_cred_t) The copied credential
4943 *
4944 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4945 * result, the caller is responsible for dropping BOTH the
4946 * additional reference on the passed cred (if any), and the
4947 * credential returned by this function. The drop should be
4948 * via the kauth_cred_unref() KPI.
4949 */
4950 kauth_cred_t
kauth_cred_copy_real(kauth_cred_t cred)4951 kauth_cred_copy_real(kauth_cred_t cred)
4952 {
4953 kauth_cred_t newcred = NULL, found_cred;
4954 struct ucred temp_cred;
4955 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4956 posix_cred_t pcred = posix_cred_get(cred);
4957
4958 /* if the credential is already 'real', just take a reference */
4959 if ((pcred->cr_ruid == pcred->cr_uid) &&
4960 (pcred->cr_rgid == pcred->cr_gid)) {
4961 kauth_cred_ref(cred);
4962 return cred;
4963 }
4964
4965 /*
4966 * Look up in cred hash table to see if we have a matching credential
4967 * with the new values.
4968 */
4969 temp_cred = *cred;
4970 temp_pcred->cr_uid = pcred->cr_ruid;
4971 /* displacing a supplementary group opts us out of memberd */
4972 if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) {
4973 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4974 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4975 }
4976 /*
4977 * If the cred is not opted out, make sure we are using the r/euid
4978 * for group checks
4979 */
4980 if (temp_pcred->cr_gmuid != KAUTH_UID_NONE) {
4981 temp_pcred->cr_gmuid = pcred->cr_ruid;
4982 }
4983
4984 struct kauth_cred_entry_head *bucket = kauth_cred_get_bucket(cred);
4985
4986 KAUTH_CRED_HASH_LOCK();
4987 found_cred = kauth_cred_find_and_ref(&temp_cred, bucket);
4988 KAUTH_CRED_HASH_UNLOCK();
4989
4990 if (found_cred) {
4991 return found_cred;
4992 }
4993
4994 /*
4995 * Must allocate a new credential, copy in old credential
4996 * data and update the real user and group IDs.
4997 */
4998 newcred = kauth_cred_dup(&temp_cred);
4999 return kauth_cred_add(newcred, bucket);
5000 }
5001
5002
5003 /*
5004 * kauth_cred_update
5005 *
5006 * Description: Common code to update a credential
5007 *
5008 * Parameters: old_cred Reference counted credential
5009 * to update
5010 * model_cred Non-reference counted model
5011 * credential to apply to the
5012 * credential to be updated
5013 * retain_auditinfo Flag as to whether or not the
5014 * audit information should be
5015 * copied from the old_cred into
5016 * the model_cred
5017 *
5018 * Returns: (kauth_cred_t) The updated credential
5019 *
5020 * IMPORTANT: This function will potentially return a credential other than
5021 * the one it is passed, and if so, it will have dropped the
5022 * reference on the passed credential. All callers should be
5023 * aware of this, and treat this function as an unref + ref,
5024 * potentially on different credentials.
5025 *
5026 * Because of this, the caller is expected to take its own
5027 * reference on the credential passed as the first parameter,
5028 * and be prepared to release the reference on the credential
5029 * that is returned to them, if it is not intended to be a
5030 * persistent reference.
5031 */
5032 static kauth_cred_t
kauth_cred_update(kauth_cred_t old_cred,kauth_cred_t model_cred,boolean_t retain_auditinfo)5033 kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
5034 boolean_t retain_auditinfo)
5035 {
5036 kauth_cred_t cred;
5037
5038 old_cred = kauth_cred_require(old_cred);
5039
5040 /*
5041 * Make sure we carry the auditinfo forward to the new credential
5042 * unless we are actually updating the auditinfo.
5043 */
5044 if (retain_auditinfo) {
5045 model_cred->cr_audit = old_cred->cr_audit;
5046 }
5047
5048 if (kauth_cred_is_equal(old_cred, model_cred)) {
5049 return old_cred;
5050 }
5051
5052 struct kauth_cred_entry_head *bucket = kauth_cred_get_bucket(model_cred);
5053
5054 KAUTH_CRED_HASH_LOCK();
5055 cred = kauth_cred_find_and_ref(model_cred, bucket);
5056 if (cred != NULL) {
5057 /*
5058 * We found a hit, so we can get rid of the old_cred.
5059 * If we didn't, then we need to keep the old_cred around,
5060 * because `model_cred` has copies of things such as the cr_label
5061 * or audit session that it has not refcounts for.
5062 */
5063 bool needs_free = kauth_cred_unref_fast(old_cred);
5064 if (needs_free) {
5065 kauth_cred_remove_locked(old_cred->cr_rw);
5066 }
5067 KAUTH_CRED_HASH_UNLOCK();
5068
5069 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n",
5070 old_cred, cred);
5071 if (needs_free) {
5072 kauth_cred_free(old_cred, false);
5073 }
5074 return cred;
5075 }
5076
5077 KAUTH_CRED_HASH_UNLOCK();
5078
5079 /*
5080 * Must allocate a new credential using the model. also
5081 * adds the new credential to the credential hash table.
5082 */
5083 cred = kauth_cred_dup(model_cred);
5084 cred = kauth_cred_add(cred, bucket);
5085 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n",
5086 old_cred, cred);
5087
5088
5089 /*
5090 * This can't be done before the kauth_cred_dup() as the model_cred
5091 * has pointers that old_cred owns references for.
5092 */
5093 kauth_cred_unref(&old_cred);
5094 return cred;
5095 }
5096
5097
5098 /*
5099 * kauth_cred_add
5100 *
5101 * Description: Add the given credential to our credential hash table and
5102 * take an initial reference to account for the object being
5103 * now valid.
5104 *
5105 * Parameters: new_cred Credential to insert into cred
5106 * hash cache, or to destroy when
5107 * a collision is detected.
5108 *
5109 * Returns: (kauth_thread_t) The inserted cred, or the
5110 * collision that was found.
5111 *
5112 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
5113 */
5114 static kauth_cred_t
kauth_cred_add(kauth_cred_t new_cred,struct kauth_cred_entry_head * bucket)5115 kauth_cred_add(kauth_cred_t new_cred, struct kauth_cred_entry_head *bucket)
5116 {
5117 kauth_cred_t found_cred;
5118
5119 KAUTH_CRED_HASH_LOCK();
5120 found_cred = kauth_cred_find_and_ref(new_cred, bucket);
5121 if (found_cred) {
5122 KAUTH_CRED_HASH_UNLOCK();
5123 kauth_cred_free(new_cred, false);
5124 return found_cred;
5125 }
5126
5127 if (new_cred->cr_ref != 0) {
5128 panic("kauth_cred_add: invalid cred %p", new_cred);
5129 }
5130
5131 /* insert the credential into the hash table */
5132 LIST_INSERT_HEAD(bucket, kauth_cred_rw(new_cred), crw_link);
5133
5134 KAUTH_CRED_HASH_UNLOCK();
5135 return new_cred;
5136 }
5137
5138 /*
5139 * kauth_cred_remove_locked
5140 *
5141 * Description: Remove the given credential from our credential hash table.
5142 *
5143 * Parameters: cred_rw Credential to remove.
5144 *
5145 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5146 */
5147 static void
kauth_cred_remove_locked(struct ucred_rw * cred_rw)5148 kauth_cred_remove_locked(struct ucred_rw *cred_rw)
5149 {
5150 KAUTH_CRED_HASH_LOCK_ASSERT();
5151
5152 if (cred_rw->crw_link.le_prev == NULL) {
5153 panic("kauth_cred_unref: cred_rw %p never added", cred_rw);
5154 }
5155
5156 LIST_REMOVE(cred_rw, crw_link);
5157 }
5158
5159 /*
5160 * kauth_cred_is_equal
5161 *
5162 * Description: Returns whether two credentions are identical.
5163 *
5164 * Parameters: cred1 Credential to compare
5165 * cred2 Credential to compare
5166 *
5167 * Returns: true Credentials are equal
5168 * false Credentials are different
5169 */
5170 static bool
kauth_cred_is_equal(kauth_cred_t cred1,kauth_cred_t cred2)5171 kauth_cred_is_equal(kauth_cred_t cred1, kauth_cred_t cred2)
5172 {
5173 posix_cred_t pcred1 = posix_cred_get(cred1);
5174 posix_cred_t pcred2 = posix_cred_get(cred2);
5175
5176 /*
5177 * don't worry about the label unless the flags in
5178 * either credential tell us to.
5179 */
5180 if (memcmp(pcred1, pcred2, sizeof(*pcred1))) {
5181 return false;
5182 }
5183 if (memcmp(&cred1->cr_audit, &cred2->cr_audit, sizeof(cred1->cr_audit))) {
5184 return false;
5185 }
5186 #if CONFIG_MACF
5187 /* Note: we know the flags are equal, so we only need to test one */
5188 if (pcred1->cr_flags & CRF_MAC_ENFORCE) {
5189 if (!mac_cred_label_is_equal(mac_cred_label(cred1), mac_cred_label(cred2))) {
5190 return false;
5191 }
5192 }
5193 #endif
5194 return true;
5195 }
5196
5197 /*
5198 * kauth_cred_find_and_ref
5199 *
5200 * Description: Using the given credential data, look for a match in our
5201 * credential hash table
5202 *
5203 * Parameters: cred Credential to lookup in cred
5204 * hash cache
5205 *
5206 * Returns: NULL Not found
5207 * !NULL Matching credential already in
5208 * cred hash cache, with a +1 ref
5209 *
5210 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5211 */
5212 static kauth_cred_t
kauth_cred_find_and_ref(kauth_cred_t cred,struct kauth_cred_entry_head * bucket)5213 kauth_cred_find_and_ref(kauth_cred_t cred, struct kauth_cred_entry_head *bucket)
5214 {
5215 struct ucred_rw *found_cred_rw;
5216 kauth_cred_t found_cred = NULL;
5217
5218 KAUTH_CRED_HASH_LOCK_ASSERT();
5219
5220 /* Find cred in the credential hash table */
5221 LIST_FOREACH(found_cred_rw, bucket, crw_link) {
5222 if (kauth_cred_is_equal(found_cred_rw->crw_cred, cred)) {
5223 found_cred = kauth_cred_ro(found_cred_rw);
5224 /*
5225 * newer entries are inserted at the head,
5226 * no hit further in the chain can possibly
5227 * be successfully retained.
5228 */
5229 if (!kauth_cred_tryref(found_cred)) {
5230 found_cred = NULL;
5231 }
5232 break;
5233 }
5234 }
5235
5236 return found_cred;
5237 }
5238
5239 /*
5240 * kauth_cred_find
5241 *
5242 * Description: This interface is sadly KPI but people can't possibly use it,
5243 * as they need to hold a lock that isn't exposed.
5244 *
5245 * Parameters: cred Credential to lookup in cred
5246 * hash cache
5247 *
5248 * Returns: NULL Not found
5249 * !NULL Matching credential already in
5250 * cred hash cache
5251 *
5252 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5253 */
5254 kauth_cred_t
kauth_cred_find(kauth_cred_t cred)5255 kauth_cred_find(kauth_cred_t cred)
5256 {
5257 struct kauth_cred_entry_head *bucket = kauth_cred_get_bucket(cred);
5258 struct ucred_rw *found_cred_rw;
5259 kauth_cred_t found_cred = NULL;
5260
5261 KAUTH_CRED_HASH_LOCK_ASSERT();
5262
5263 /* Find cred in the credential hash table */
5264 LIST_FOREACH(found_cred_rw, bucket, crw_link) {
5265 if (kauth_cred_is_equal(found_cred_rw->crw_cred, cred)) {
5266 found_cred = kauth_cred_require(found_cred_rw->crw_cred);
5267 break;
5268 }
5269 }
5270
5271 return found_cred;
5272 }
5273
5274
5275 /*
5276 * kauth_cred_get_bucket
5277 *
5278 * Description: Generate a hash key using data that makes up a credential;
5279 * based on ElfHash. We hash on the entire credential data,
5280 * not including the ref count or the TAILQ, which are mutable;
5281 * everything else isn't.
5282 *
5283 * Returns the bucket correspondong to this hash key.
5284 *
5285 * Parameters: cred Credential for which hash is
5286 * desired
5287 *
5288 * Returns: (kauth_cred_entry_head *) Returned bucket.
5289 *
5290 * Notes: When actually moving the POSIX credential into a real label,
5291 * remember to update this hash computation.
5292 */
5293 static struct kauth_cred_entry_head *
kauth_cred_get_bucket(kauth_cred_t cred)5294 kauth_cred_get_bucket(kauth_cred_t cred)
5295 {
5296 #if CONFIG_MACF
5297 posix_cred_t pcred = posix_cred_get(cred);
5298 #endif
5299 uint32_t hash_key = 0;
5300
5301 hash_key = os_hash_jenkins_update(&cred->cr_posix,
5302 sizeof(struct posix_cred), hash_key);
5303
5304 hash_key = os_hash_jenkins_update(&cred->cr_audit,
5305 sizeof(struct au_session), hash_key);
5306 #if CONFIG_MACF
5307 if (pcred->cr_flags & CRF_MAC_ENFORCE) {
5308 hash_key = mac_cred_label_hash_update(mac_cred_label(cred), hash_key);
5309 }
5310 #endif /* CONFIG_MACF */
5311
5312 hash_key = os_hash_jenkins_finish(hash_key);
5313 hash_key %= KAUTH_CRED_TABLE_SIZE;
5314 return &kauth_cred_table_anchor[hash_key];
5315 }
5316
5317
5318 /*
5319 **********************************************************************
5320 * The following routines will be moved to a policy_posix.c module at
5321 * some future point.
5322 **********************************************************************
5323 */
5324
5325 /*
5326 * posix_cred_create
5327 *
5328 * Description: Helper function to create a kauth_cred_t credential that is
5329 * initally labelled with a specific POSIX credential label
5330 *
5331 * Parameters: pcred The posix_cred_t to use as the initial
5332 * label value
5333 *
5334 * Returns: (kauth_cred_t) The credential that was found in the
5335 * hash or creates
5336 * NULL kauth_cred_add() failed, or there was
5337 * no egid specified, or we failed to
5338 * attach a label to the new credential
5339 *
5340 * Notes: This function currently wraps kauth_cred_create(), and is the
5341 * only consumer of that ill-fated function, apart from bsd_init().
5342 * It exists solely to support the NFS server code creation of
5343 * credentials based on the over-the-wire RPC calls containing
5344 * traditional POSIX credential information being tunneled to
5345 * the server host from the client machine.
5346 *
5347 * In the future, we hope this function goes away.
5348 *
5349 * In the short term, it creates a temporary credential, puts
5350 * the POSIX information from NFS into it, and then calls
5351 * kauth_cred_create(), as an internal implementation detail.
5352 *
5353 * If we have to keep it around in the medium term, it will
5354 * create a new kauth_cred_t, then label it with a POSIX label
5355 * corresponding to the contents of the kauth_cred_t. If the
5356 * policy_posix MACF module is not loaded, it will instead
5357 * substitute a posix_cred_t which GRANTS all access (effectively
5358 * a "root" credential) in order to not prevent NFS from working
5359 * in the case that we are not supporting POSIX credentials.
5360 */
5361 kauth_cred_t
posix_cred_create(posix_cred_t pcred)5362 posix_cred_create(posix_cred_t pcred)
5363 {
5364 struct ucred temp_cred;
5365
5366 bzero(&temp_cred, sizeof(temp_cred));
5367 temp_cred.cr_posix = *pcred;
5368
5369 return kauth_cred_create(&temp_cred);
5370 }
5371
5372
5373 /*
5374 * posix_cred_get
5375 *
5376 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5377 * any, which is associated with it.
5378 *
5379 * Parameters: cred The credential to obtain the label from
5380 *
5381 * Returns: posix_cred_t The POSIX credential label
5382 *
5383 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5384 * this function will return a pointer to a posix_cred_t which
5385 * GRANTS all access (effectively, a "root" credential). This is
5386 * necessary to support legacy code which insists on tightly
5387 * integrating POSIX credentials into its APIs, including, but
5388 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5389 * NFSv3, signals, dtrace, and a large number of kauth routines
5390 * used to implement POSIX permissions related system calls.
5391 *
5392 * In the event that the policy_posix MACF module IS loaded, and
5393 * there is no POSIX label on the kauth_cred_t credential, this
5394 * function will return a pointer to a posix_cred_t which DENIES
5395 * all access (effectively, a "deny rights granted by POSIX"
5396 * credential). This is necessary to support the concept of a
5397 * transiently loaded POSIX policy, or kauth_cred_t credentials
5398 * which can not be used in conjunctions with POSIX permissions
5399 * checks.
5400 *
5401 * This function currently returns the address of the cr_posix
5402 * field of the supplied kauth_cred_t credential, and as such
5403 * currently can not fail. In the future, this will not be the
5404 * case.
5405 */
5406 posix_cred_t
posix_cred_get(kauth_cred_t cred)5407 posix_cred_get(kauth_cred_t cred)
5408 {
5409 return &cred->cr_posix;
5410 }
5411
5412
5413 /*
5414 * posix_cred_label
5415 *
5416 * Description: Label a kauth_cred_t with a POSIX credential label
5417 *
5418 * Parameters: cred The credential to label
5419 * pcred The POSIX credential t label it with
5420 *
5421 * Returns: (void)
5422 *
5423 * Notes: This function is currently void in order to permit it to fit
5424 * in with the current MACF framework label methods which allow
5425 * labeling to fail silently. This is like acceptable for
5426 * mandatory access controls, but not for POSIX, since those
5427 * access controls are advisory. We will need to consider a
5428 * return value in a future version of the MACF API.
5429 *
5430 * This operation currently cannot fail, as currently the POSIX
5431 * credential is a subfield of the kauth_cred_t (ucred), which
5432 * MUST be valid. In the future, this will not be the case.
5433 */
5434 void
posix_cred_label(kauth_cred_t cred,posix_cred_t pcred)5435 posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
5436 {
5437 cred->cr_posix = *pcred; /* structure assign for now */
5438 }
5439
5440
5441 /*
5442 * posix_cred_access
5443 *
5444 * Description: Perform a POSIX access check for a protected object
5445 *
5446 * Parameters: cred The credential to check
5447 * object_uid The POSIX UID of the protected object
5448 * object_gid The POSIX GID of the protected object
5449 * object_mode The POSIX mode of the protected object
5450 * mode_req The requested POSIX access rights
5451 *
5452 * Returns 0 Access is granted
5453 * EACCES Access is denied
5454 *
5455 * Notes: This code optimizes the case where the world and group rights
5456 * would both grant the requested rights to avoid making a group
5457 * membership query. This is a big performance win in the case
5458 * where this is true.
5459 */
5460 int
posix_cred_access(kauth_cred_t cred,id_t object_uid,id_t object_gid,mode_t object_mode,mode_t mode_req)5461 posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
5462 {
5463 int is_member;
5464 mode_t mode_owner = (object_mode & S_IRWXU);
5465 mode_t mode_group = (mode_t)((object_mode & S_IRWXG) << 3);
5466 mode_t mode_world = (mode_t)((object_mode & S_IRWXO) << 6);
5467
5468 /*
5469 * Check first for owner rights
5470 */
5471 if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req) {
5472 return 0;
5473 }
5474
5475 /*
5476 * Combined group and world rights check, if we don't have owner rights
5477 *
5478 * OPTIMIZED: If group and world rights would grant the same bits, and
5479 * they set of requested bits is in both, then we can simply check the
5480 * world rights, avoiding a group membership check, which is expensive.
5481 */
5482 if ((mode_req & mode_group & mode_world) == mode_req) {
5483 return 0;
5484 } else {
5485 /*
5486 * NON-OPTIMIZED: requires group membership check.
5487 */
5488 if ((mode_req & mode_group) != mode_req) {
5489 /*
5490 * exclusion group : treat errors as "is a member"
5491 *
5492 * NON-OPTIMIZED: +group would deny; must check group
5493 */
5494 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5495 /*
5496 * DENY: +group denies
5497 */
5498 return EACCES;
5499 } else {
5500 if ((mode_req & mode_world) != mode_req) {
5501 /*
5502 * DENY: both -group & world would deny
5503 */
5504 return EACCES;
5505 } else {
5506 /*
5507 * ALLOW: allowed by -group and +world
5508 */
5509 return 0;
5510 }
5511 }
5512 } else {
5513 /*
5514 * inclusion group; treat errors as "not a member"
5515 *
5516 * NON-OPTIMIZED: +group allows, world denies; must
5517 * check group
5518 */
5519 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5520 /*
5521 * ALLOW: allowed by +group
5522 */
5523 return 0;
5524 } else {
5525 if ((mode_req & mode_world) != mode_req) {
5526 /*
5527 * DENY: both -group & world would deny
5528 */
5529 return EACCES;
5530 } else {
5531 /*
5532 * ALLOW: allowed by -group and +world
5533 */
5534 return 0;
5535 }
5536 }
5537 }
5538 }
5539 }
5540