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