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