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