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