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