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