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