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