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