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