]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_credential.c
xnu-3248.40.184.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/*
490019cf 2076 * If there's no resolver, only support a subset of the kauth_cred_x2y() lookups.
316670eb
A
2077 */
2078static __inline int
490019cf 2079kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
316670eb 2080{
490019cf
A
2081 /* NB: These must match the definitions used by Libinfo's mbr_identifier_translate(). */
2082 static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00};
2083 static const uuid_t _group_compat_prefix = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00};
2084#define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t))
2085
2086 assert(from != to);
2087
2088 switch (from) {
2089 case KI_VALID_UID: {
2090 id_t uid = htonl(*(id_t *)src);
2091
2092 if (to == KI_VALID_GUID) {
2093 uint8_t *uu = dst;
2094 memcpy(uu, _user_compat_prefix, sizeof(_user_compat_prefix));
2095 memcpy(&uu[COMPAT_PREFIX_LEN], &uid, sizeof(uid));
2096 return (0);
2097 }
2098 break;
2099 }
2100 case KI_VALID_GID: {
2101 id_t gid = htonl(*(id_t *)src);
2102
2103 if (to == KI_VALID_GUID) {
2104 uint8_t *uu = dst;
2105 memcpy(uu, _group_compat_prefix, sizeof(_group_compat_prefix));
2106 memcpy(&uu[COMPAT_PREFIX_LEN], &gid, sizeof(gid));
2107 return (0);
2108 }
2109 break;
2110 }
2111 case KI_VALID_GUID: {
2112 const uint8_t *uu = src;
2113
2114 if (to == KI_VALID_UID) {
2115 if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
2116 id_t uid;
2117 memcpy(&uid, &uu[COMPAT_PREFIX_LEN], sizeof(uid));
2118 *(id_t *)dst = ntohl(uid);
2119 return (0);
2120 }
2121 } else if (to == KI_VALID_GID) {
2122 if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) {
2123 id_t gid;
2124 memcpy(&gid, &uu[COMPAT_PREFIX_LEN], sizeof(gid));
2125 *(id_t *)dst = ntohl(gid);
2126 return (0);
2127 }
2128 }
2129 break;
2130 }
2131 default:
2132 /* NOT IMPLEMENTED */
2133 break;
2134 }
2135 return (ENOENT);
316670eb
A
2136}
2137#endif
2138
39236c6e
A
2139#if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER)
2140/*
2141 * Structure to hold supplemental groups. Used for impedance matching with
2142 * kauth_cred_cache_lookup below.
2143 */
2144struct supgroups {
2145 int *count;
2146 gid_t *groups;
2147};
2148
2149/*
2150 * kauth_cred_uid2groups
2151 *
2152 * Description: Fetch supplemental GROUPS from UID
2153 *
2154 * Parameters: uid UID to examine
2155 * groups pointer to an array of gid_ts
2156 * gcount pointer to the number of groups wanted/returned
2157 *
2158 * Returns: 0 Success
2159 * kauth_cred_cache_lookup:EINVAL
2160 *
2161 * Implicit returns:
2162 * *groups Modified, if successful
2163 * *gcount Modified, if successful
2164 *
2165 */
2166static int
2167kauth_cred_uid2groups(uid_t *uid, gid_t *groups, int *gcount)
2168{
2169 int rv;
2170
2171 struct supgroups supgroups;
2172 supgroups.count = gcount;
2173 supgroups.groups = groups;
2174
2175 rv = kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GROUPS, uid, &supgroups);
2176
2177 return (rv);
2178}
2179#endif
2180
6d2010ae
A
2181/*
2182 * kauth_cred_guid2pwnam
2183 *
2184 * Description: Fetch PWNAM from GUID
2185 *
2186 * Parameters: guidp Pointer to GUID to examine
2187 * pwnam Pointer to user@domain buffer
2188 *
2189 * Returns: 0 Success
2190 * kauth_cred_cache_lookup:EINVAL
2191 *
2192 * Implicit returns:
2193 * *pwnam Modified, if successful
2194 *
2195 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size
2196 */
2197int
2198kauth_cred_guid2pwnam(guid_t *guidp, char *pwnam)
2199{
2200 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_PWNAM, guidp, pwnam));
2201}
2202
2203
2204/*
2205 * kauth_cred_guid2grnam
2206 *
2207 * Description: Fetch GRNAM from GUID
2208 *
2209 * Parameters: guidp Pointer to GUID to examine
2210 * grnam Pointer to group@domain buffer
2211 *
2212 * Returns: 0 Success
2213 * kauth_cred_cache_lookup:EINVAL
2214 *
2215 * Implicit returns:
2216 * *grnam Modified, if successful
2217 *
2218 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size
2219 */
2220int
2221kauth_cred_guid2grnam(guid_t *guidp, char *grnam)
2222{
2223 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GRNAM, guidp, grnam));
2224}
2225
2226
2227/*
2228 * kauth_cred_pwnam2guid
2229 *
2230 * Description: Fetch PWNAM from GUID
2231 *
2232 * Parameters: pwnam String containing user@domain
2233 * guidp Pointer to buffer for GUID
2234 *
2235 * Returns: 0 Success
2236 * kauth_cred_cache_lookup:EINVAL
2237 *
2238 * Implicit returns:
2239 * *guidp Modified, if successful
2240 *
2241 * Notes: pwnam should not point to a request larger than MAXPATHLEN
2242 * bytes in size, including the NUL termination of the string.
2243 */
2244int
2245kauth_cred_pwnam2guid(char *pwnam, guid_t *guidp)
2246{
2247 return(kauth_cred_cache_lookup(KI_VALID_PWNAM, KI_VALID_GUID, pwnam, guidp));
2248}
2249
2250
2251/*
2252 * kauth_cred_grnam2guid
2253 *
2254 * Description: Fetch GRNAM from GUID
2255 *
2256 * Parameters: grnam String containing group@domain
2257 * guidp Pointer to buffer for GUID
2258 *
2259 * Returns: 0 Success
2260 * kauth_cred_cache_lookup:EINVAL
2261 *
2262 * Implicit returns:
2263 * *guidp Modified, if successful
2264 *
2265 * Notes: grnam should not point to a request larger than MAXPATHLEN
2266 * bytes in size, including the NUL termination of the string.
2267 */
2268int
2269kauth_cred_grnam2guid(char *grnam, guid_t *guidp)
2270{
2271 return(kauth_cred_cache_lookup(KI_VALID_GRNAM, KI_VALID_GUID, grnam, guidp));
91447636
A
2272}
2273
2d21ac55 2274
91447636 2275/*
2d21ac55
A
2276 * kauth_cred_guid2uid
2277 *
2278 * Description: Fetch UID from GUID
2279 *
2280 * Parameters: guidp Pointer to GUID to examine
2281 * uidp Pointer to buffer for UID
2282 *
2283 * Returns: 0 Success
2284 * kauth_cred_cache_lookup:EINVAL
2285 *
2286 * Implicit returns:
2287 * *uidp Modified, if successful
91447636
A
2288 */
2289int
2290kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
2291{
2292 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
2293}
2294
2d21ac55 2295
91447636 2296/*
2d21ac55
A
2297 * kauth_cred_guid2gid
2298 *
2299 * Description: Fetch GID from GUID
2300 *
2301 * Parameters: guidp Pointer to GUID to examine
2302 * gidp Pointer to buffer for GID
2303 *
2304 * Returns: 0 Success
2305 * kauth_cred_cache_lookup:EINVAL
2306 *
2307 * Implicit returns:
2308 * *gidp Modified, if successful
91447636
A
2309 */
2310int
2311kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
2312{
2313 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
2314}
2315
2d21ac55 2316
91447636 2317/*
2d21ac55
A
2318 * kauth_cred_ntsid2uid
2319 *
2320 * Description: Fetch UID from NTSID
2321 *
2322 * Parameters: sidp Pointer to NTSID to examine
2323 * uidp Pointer to buffer for UID
2324 *
2325 * Returns: 0 Success
2326 * kauth_cred_cache_lookup:EINVAL
2327 *
2328 * Implicit returns:
2329 * *uidp Modified, if successful
91447636
A
2330 */
2331int
2332kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
2333{
2334 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
2335}
2336
2d21ac55 2337
91447636 2338/*
2d21ac55
A
2339 * kauth_cred_ntsid2gid
2340 *
2341 * Description: Fetch GID from NTSID
2342 *
2343 * Parameters: sidp Pointer to NTSID to examine
2344 * gidp Pointer to buffer for GID
2345 *
2346 * Returns: 0 Success
2347 * kauth_cred_cache_lookup:EINVAL
2348 *
2349 * Implicit returns:
2350 * *gidp Modified, if successful
91447636
A
2351 */
2352int
2353kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
2354{
2355 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
2356}
2357
2d21ac55 2358
91447636 2359/*
2d21ac55
A
2360 * kauth_cred_ntsid2guid
2361 *
2362 * Description: Fetch GUID from NTSID
2363 *
2364 * Parameters: sidp Pointer to NTSID to examine
2365 * guidp Pointer to buffer for GUID
2366 *
2367 * Returns: 0 Success
2368 * kauth_cred_cache_lookup:EINVAL
2369 *
2370 * Implicit returns:
2371 * *guidp Modified, if successful
91447636
A
2372 */
2373int
2374kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
2375{
2376 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
2377}
2378
2d21ac55 2379
91447636 2380/*
2d21ac55
A
2381 * kauth_cred_uid2guid
2382 *
2383 * Description: Fetch GUID from UID
2384 *
2385 * Parameters: uid UID to examine
2386 * guidp Pointer to buffer for GUID
2387 *
2388 * Returns: 0 Success
2389 * kauth_cred_cache_lookup:EINVAL
2390 *
2391 * Implicit returns:
2392 * *guidp Modified, if successful
91447636
A
2393 */
2394int
2395kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
2396{
2397 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
2398}
2399
2d21ac55 2400
91447636 2401/*
2d21ac55
A
2402 * kauth_cred_getguid
2403 *
2404 * Description: Fetch GUID from credential
2405 *
2406 * Parameters: cred Credential to examine
2407 * guidp Pointer to buffer for GUID
2408 *
2409 * Returns: 0 Success
2410 * kauth_cred_cache_lookup:EINVAL
2411 *
2412 * Implicit returns:
2413 * *guidp Modified, if successful
91447636
A
2414 */
2415int
2416kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
2417{
2418 NULLCRED_CHECK(cred);
2419 return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
2420}
2421
2d21ac55 2422
91447636 2423/*
2d21ac55
A
2424 * kauth_cred_getguid
2425 *
2426 * Description: Fetch GUID from GID
2427 *
2428 * Parameters: gid GID to examine
2429 * guidp Pointer to buffer for GUID
2430 *
2431 * Returns: 0 Success
2432 * kauth_cred_cache_lookup:EINVAL
2433 *
2434 * Implicit returns:
2435 * *guidp Modified, if successful
2436 */
2437int
91447636
A
2438kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
2439{
2440 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
2441}
2442
2d21ac55 2443
91447636 2444/*
2d21ac55
A
2445 * kauth_cred_uid2ntsid
2446 *
2447 * Description: Fetch NTSID from UID
2448 *
2449 * Parameters: uid UID to examine
2450 * sidp Pointer to buffer for NTSID
2451 *
2452 * Returns: 0 Success
2453 * kauth_cred_cache_lookup:EINVAL
2454 *
2455 * Implicit returns:
2456 * *sidp Modified, if successful
91447636
A
2457 */
2458int
2459kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
2460{
2461 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
2462}
2463
2d21ac55 2464
91447636 2465/*
2d21ac55
A
2466 * kauth_cred_getntsid
2467 *
2468 * Description: Fetch NTSID from credential
2469 *
2470 * Parameters: cred Credential to examine
2471 * sidp Pointer to buffer for NTSID
2472 *
2473 * Returns: 0 Success
2474 * kauth_cred_cache_lookup:EINVAL
2475 *
2476 * Implicit returns:
2477 * *sidp Modified, if successful
91447636
A
2478 */
2479int
2480kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
2481{
2482 NULLCRED_CHECK(cred);
2483 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
2484}
2485
2d21ac55 2486
91447636 2487/*
2d21ac55
A
2488 * kauth_cred_gid2ntsid
2489 *
2490 * Description: Fetch NTSID from GID
2491 *
2492 * Parameters: gid GID to examine
2493 * sidp Pointer to buffer for NTSID
2494 *
2495 * Returns: 0 Success
2496 * kauth_cred_cache_lookup:EINVAL
2497 *
2498 * Implicit returns:
2499 * *sidp Modified, if successful
91447636
A
2500 */
2501int
2502kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
2503{
2504 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
2505}
2506
2d21ac55 2507
91447636 2508/*
2d21ac55
A
2509 * kauth_cred_guid2ntsid
2510 *
2511 * Description: Fetch NTSID from GUID
2512 *
2513 * Parameters: guidp Pointer to GUID to examine
2514 * sidp Pointer to buffer for NTSID
2515 *
2516 * Returns: 0 Success
2517 * kauth_cred_cache_lookup:EINVAL
2518 *
2519 * Implicit returns:
2520 * *sidp Modified, if successful
91447636
A
2521 */
2522int
2523kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
2524{
2525 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
2526}
2527
2528
91447636 2529/*
2d21ac55
A
2530 * kauth_cred_cache_lookup
2531 *
2532 * Description: Lookup a translation in the cache; if one is not found, and
2533 * the attempt was not fatal, submit the request to the resolver
2534 * instead, and wait for it to complete or be aborted.
2535 *
2536 * Parameters: from Identity information we have
2537 * to Identity information we want
2538 * src Pointer to buffer containing
2539 * the source identity
2540 * dst Pointer to buffer to receive
2541 * the target identity
2542 *
2543 * Returns: 0 Success
2544 * EINVAL Unknown source identity type
91447636 2545 */
316670eb 2546#if CONFIG_EXT_RESOLVER
91447636
A
2547static int
2548kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
2549{
2550 struct kauth_identity ki;
2551 struct kauth_identity_extlookup el;
2552 int error;
6d2010ae 2553 uint64_t extend_data = 0ULL;
91447636 2554 int (* expired)(struct kauth_identity *kip);
6d2010ae 2555 char *namebuf = NULL;
91447636
A
2556
2557 KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
2558
2559 /*
2560 * Look for an existing cache entry for this association.
2561 * If the entry has not expired, return the cached information.
6d2010ae
A
2562 * We do not cache user@domain translations here; they use too
2563 * much memory to hold onto forever, and can not be updated
2564 * atomically.
91447636 2565 */
6d2010ae
A
2566 if (to == KI_VALID_PWNAM || to == KI_VALID_GRNAM) {
2567 namebuf = dst;
2568 }
91447636
A
2569 ki.ki_valid = 0;
2570 switch(from) {
2571 case KI_VALID_UID:
6d2010ae 2572 error = kauth_identity_find_uid(*(uid_t *)src, &ki, namebuf);
91447636
A
2573 break;
2574 case KI_VALID_GID:
6d2010ae 2575 error = kauth_identity_find_gid(*(gid_t *)src, &ki, namebuf);
91447636
A
2576 break;
2577 case KI_VALID_GUID:
6d2010ae 2578 error = kauth_identity_find_guid((guid_t *)src, &ki, namebuf);
91447636
A
2579 break;
2580 case KI_VALID_NTSID:
6d2010ae
A
2581 error = kauth_identity_find_ntsid((ntsid_t *)src, &ki, namebuf);
2582 break;
2583 case KI_VALID_PWNAM:
2584 case KI_VALID_GRNAM:
2585 /* Names are unique in their 'from' space */
2586 error = kauth_identity_find_nam((char *)src, from, &ki);
91447636
A
2587 break;
2588 default:
2589 return(EINVAL);
2590 }
2591 /* lookup failure or error */
2592 if (error != 0) {
2593 /* any other error is fatal */
2594 if (error != ENOENT) {
2d21ac55 2595 /* XXX bogus check - this is not possible */
91447636
A
2596 KAUTH_DEBUG("CACHE - cache search error %d", error);
2597 return(error);
2598 }
2599 } else {
13f56ec4
A
2600 /* found a valid cached entry, check expiry */
2601 switch(to) {
2602 case KI_VALID_GUID:
2603 expired = kauth_identity_guid_expired;
2604 break;
2605 case KI_VALID_NTSID:
2606 expired = kauth_identity_ntsid_expired;
2607 break;
39236c6e
A
2608 case KI_VALID_GROUPS:
2609 expired = kauth_identity_groups_expired;
2610 break;
13f56ec4
A
2611 default:
2612 switch(from) {
91447636
A
2613 case KI_VALID_GUID:
2614 expired = kauth_identity_guid_expired;
2615 break;
2616 case KI_VALID_NTSID:
2617 expired = kauth_identity_ntsid_expired;
2618 break;
2619 default:
13f56ec4 2620 expired = NULL;
91447636 2621 }
13f56ec4
A
2622 }
2623
2624 /*
2625 * If no expiry function, or not expired, we have found
2626 * a hit.
2627 */
2628 if (expired) {
91447636
A
2629 if (!expired(&ki)) {
2630 KAUTH_DEBUG("CACHE - entry valid, unexpired");
13f56ec4
A
2631 expired = NULL; /* must clear it is used as a flag */
2632 } else {
2633 /*
2634 * We leave ki_valid set here; it contains a
2635 * translation but the TTL has expired. If we can't
2636 * get a result from the resolver, we will use it as
2637 * a better-than nothing alternative.
2638 */
2639
2640 KAUTH_DEBUG("CACHE - expired entry found");
91447636 2641 }
6d2010ae 2642 } else {
13f56ec4
A
2643 KAUTH_DEBUG("CACHE - no expiry function");
2644 }
2645
2646 if (!expired) {
2647 /* do we have a translation? */
2648 if (ki.ki_valid & to) {
2649 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki.ki_valid);
316670eb 2650 DTRACE_PROC4(kauth__identity__cache__hit, int, from, int, to, void *, src, void *, dst);
13f56ec4
A
2651 goto found;
2652 } else {
2653 /*
2654 * GUIDs and NTSIDs map to either a UID or a GID, but not both.
2655 * If we went looking for a translation from GUID or NTSID and
2656 * found a translation that wasn't for our desired type, then
2657 * don't bother calling the resolver. We know that this
2658 * GUID/NTSID can't translate to our desired type.
2659 */
2660 switch(from) {
2661 case KI_VALID_GUID:
2662 case KI_VALID_NTSID:
2663 switch(to) {
2664 case KI_VALID_GID:
2665 if ((ki.ki_valid & KI_VALID_UID)) {
2666 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_GID);
2667 return (ENOENT);
2668 }
2669 break;
2670 case KI_VALID_UID:
2671 if ((ki.ki_valid & KI_VALID_GID)) {
2672 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_UID);
2673 return (ENOENT);
2674 }
2675 break;
2676 }
2677 break;
2678 }
6d2010ae 2679 }
91447636
A
2680 }
2681 }
2682
2683 /*
2d21ac55
A
2684 * We failed to find a cache entry; call the resolver.
2685 *
6d2010ae
A
2686 * Note: We ask for as much non-extended data as we can get,
2687 * and only provide (or ask for) extended information if
2688 * we have a 'from' (or 'to') which requires it. This
2689 * way we don't pay for the extra transfer overhead for
2690 * data we don't need.
91447636 2691 */
b0d623f7
A
2692 bzero(&el, sizeof(el));
2693 el.el_info_pid = current_proc()->p_pid;
91447636
A
2694 switch(from) {
2695 case KI_VALID_UID:
2696 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
2697 el.el_uid = *(uid_t *)src;
2698 break;
2699 case KI_VALID_GID:
2700 el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
2701 el.el_gid = *(gid_t *)src;
2702 break;
2703 case KI_VALID_GUID:
2704 el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
2705 el.el_uguid = *(guid_t *)src;
2706 el.el_gguid = *(guid_t *)src;
2707 break;
2708 case KI_VALID_NTSID:
2709 el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
2710 el.el_usid = *(ntsid_t *)src;
2711 el.el_gsid = *(ntsid_t *)src;
2712 break;
6d2010ae
A
2713 case KI_VALID_PWNAM:
2714 /* extra overhead */
2715 el.el_flags = KAUTH_EXTLOOKUP_VALID_PWNAM;
2716 extend_data = CAST_USER_ADDR_T(src);
2717 break;
2718 case KI_VALID_GRNAM:
2719 /* extra overhead */
2720 el.el_flags = KAUTH_EXTLOOKUP_VALID_GRNAM;
2721 extend_data = CAST_USER_ADDR_T(src);
2722 break;
91447636
A
2723 default:
2724 return(EINVAL);
2725 }
2726 /*
2727 * Here we ask for everything all at once, to avoid having to work
2728 * out what we really want now, or might want soon.
2729 *
2730 * Asking for SID translations when we don't know we need them right
2731 * now is going to cause excess work to be done if we're connected
2732 * to a network that thinks it can translate them. This list needs
2733 * to get smaller/smarter.
2734 */
2735 el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
2736 KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
2737 KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
6d2010ae
A
2738 if (to == KI_VALID_PWNAM) {
2739 /* extra overhead */
2740 el.el_flags |= KAUTH_EXTLOOKUP_WANT_PWNAM;
2741 extend_data = CAST_USER_ADDR_T(dst);
2742 }
2743 if (to == KI_VALID_GRNAM) {
2744 /* extra overhead */
2745 el.el_flags |= KAUTH_EXTLOOKUP_WANT_GRNAM;
2746 extend_data = CAST_USER_ADDR_T(dst);
2747 }
39236c6e
A
2748 if (to == KI_VALID_GROUPS) {
2749 /* Expensive and only useful for an NFS client not using kerberos */
2750 el.el_flags |= KAUTH_EXTLOOKUP_WANT_SUPGRPS;
2751 if (ki.ki_valid & KI_VALID_GROUPS) {
2752 /*
2753 * Copy the current supplemental groups for the resolver.
2754 * The resolver should check these groups first and if
2755 * the user (uid) is still a member it should endeavor to
2756 * keep them in the list. Otherwise NFS clients could get
2757 * changing access to server file system objects on each
2758 * expiration.
2759 */
2760 el.el_sup_grp_cnt = ki.ki_supgrpcnt;
2761
2762 memcpy(el.el_sup_groups, ki.ki_supgrps, sizeof (el.el_sup_groups[0]) * ki.ki_supgrpcnt);
2763 /* Let the resolver know these were the previous valid groups */
2764 el.el_flags |= KAUTH_EXTLOOKUP_VALID_SUPGRPS;
2765 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS");
2766 } else
2767 KAUTH_DEBUG("GROUPS: no valid groups to send");
2768 }
6d2010ae
A
2769
2770 /* Call resolver */
91447636 2771 KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
316670eb
A
2772
2773 DTRACE_PROC3(kauth__id__resolver__submitted, int, from, int, to, uintptr_t, src);
2774
6d2010ae 2775 error = kauth_resolver_submit(&el, extend_data);
316670eb
A
2776
2777 DTRACE_PROC2(kauth__id__resolver__returned, int, error, struct kauth_identity_extlookup *, &el)
2778
91447636 2779 KAUTH_DEBUG("CACHE - resolver returned %d", error);
6d2010ae
A
2780
2781 /* was the external lookup successful? */
91447636
A
2782 if (error == 0) {
2783 /*
6d2010ae
A
2784 * Save the results from the lookup - we may have other
2785 * information, even if we didn't get a guid or the
2786 * extended data.
2787 *
2788 * If we came from a name, we know the extend_data is valid.
2789 */
2790 if (from == KI_VALID_PWNAM)
2791 el.el_flags |= KAUTH_EXTLOOKUP_VALID_PWNAM;
2792 else if (from == KI_VALID_GRNAM)
2793 el.el_flags |= KAUTH_EXTLOOKUP_VALID_GRNAM;
2794
2795 kauth_identity_updatecache(&el, &ki, extend_data);
2796
2797 /*
2798 * Check to see if we have a valid cache entry
2799 * originating from the result.
91447636 2800 */
6d2010ae
A
2801 if (!(ki.ki_valid & to)) {
2802 error = ENOENT;
2803 }
91447636 2804 }
91447636
A
2805 if (error)
2806 return(error);
2807found:
6d2010ae
A
2808 /*
2809 * Copy from the appropriate struct kauth_identity cache entry
2810 * structure into the destination buffer area.
2811 */
91447636
A
2812 switch(to) {
2813 case KI_VALID_UID:
2814 *(uid_t *)dst = ki.ki_uid;
2815 break;
2816 case KI_VALID_GID:
2817 *(gid_t *)dst = ki.ki_gid;
2818 break;
2819 case KI_VALID_GUID:
2820 *(guid_t *)dst = ki.ki_guid;
2821 break;
2822 case KI_VALID_NTSID:
2823 *(ntsid_t *)dst = ki.ki_ntsid;
2824 break;
39236c6e
A
2825 case KI_VALID_GROUPS: {
2826 struct supgroups *gp = (struct supgroups *)dst;
2827 u_int32_t limit = ki.ki_supgrpcnt;
2828
2829 if (gp->count) {
2830 limit = MIN(ki.ki_supgrpcnt, *gp->count);
2831 *gp->count = limit;
2832 }
2833
2834 memcpy(gp->groups, ki.ki_supgrps, sizeof(gid_t) * limit);
2835 }
2836 break;
6d2010ae
A
2837 case KI_VALID_PWNAM:
2838 case KI_VALID_GRNAM:
2839 /* handled in kauth_resolver_complete() */
2840 break;
91447636
A
2841 default:
2842 return(EINVAL);
2843 }
2844 KAUTH_DEBUG("CACHE - returned successfully");
2845 return(0);
2846}
2847
2848
2849/*
2850 * Group membership cache.
2851 *
2852 * XXX the linked-list implementation here needs to be optimized.
2853 */
2854
2d21ac55
A
2855/*
2856 * kauth_groups_init
2857 *
2858 * Description: Initialize the groups cache
2859 *
2860 * Parameters: (void)
2861 *
2862 * Returns: (void)
2863 *
316670eb 2864 * Notes: Initialize the groups cache for use; the group cache is used
2d21ac55
A
2865 * to avoid unnecessary calls out to user space.
2866 *
2867 * This function is called from kauth_init() in the file
2868 * kern_authorization.c.
2869 */
91447636
A
2870void
2871kauth_groups_init(void)
2872{
2873 TAILQ_INIT(&kauth_groups);
2874 kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
2875}
2876
2d21ac55
A
2877
2878/*
2879 * kauth_groups_expired
2880 *
2881 * Description: Handle lazy expiration of group membership cache entries
2882 *
2883 * Parameters: gm group membership entry to
2884 * check for expiration
2885 *
2886 * Returns: 1 Expired
2887 * 0 Not expired
2888 */
91447636
A
2889static int
2890kauth_groups_expired(struct kauth_group_membership *gm)
2891{
2892 struct timeval tv;
2893
316670eb
A
2894 /*
2895 * Expiration time of 0 means this entry is persistent.
2896 */
2897 if (gm->gm_expiry == 0)
2898 return (0);
2899
91447636 2900 microuptime(&tv);
316670eb 2901
91447636
A
2902 return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
2903}
2904
2d21ac55
A
2905
2906/*
2907 * kauth_groups_lru
2908 *
2909 * Description: Promote the entry to the head of the LRU, assumes the cache
2910 * is locked.
2911 *
2912 * Parameters: kip group membership entry to move
2913 * to the head of the LRU list,
2914 * if it's not already there
2915 *
2916 * Returns: (void)
2917 *
2918 * Notes: This is called even if the entry has expired; typically an
2919 * expired entry that's been looked up is about to be revalidated,
2920 * and having it closer to the head of the LRU means finding it
2921 * quickly again when the revalidation comes through.
2922 */
91447636
A
2923static void
2924kauth_groups_lru(struct kauth_group_membership *gm)
2925{
2926 if (gm != TAILQ_FIRST(&kauth_groups)) {
2927 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2928 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2929 }
2930}
2931
2d21ac55
A
2932
2933/*
2934 * kauth_groups_updatecache
2935 *
2936 * Description: Given a lookup result, add any group cache associations that
2937 * we don't currently have.
2938 *
2939 * Parameters: elp External lookup result from
2940 * user space daemon to kernel
2941 * rkip pointer to returned kauth
2942 * identity, or NULL
2943 *
2944 * Returns: (void)
2945 */
91447636
A
2946static void
2947kauth_groups_updatecache(struct kauth_identity_extlookup *el)
2948{
2949 struct kauth_group_membership *gm;
2950 struct timeval tv;
2951
2952 /* need a valid response if we are to cache anything */
2953 if ((el->el_flags &
2954 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
2955 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
2956 return;
2957
2958 microuptime(&tv);
2959
2d21ac55
A
2960 /*
2961 * Search for an existing record for this association before inserting
2962 * a new one; if we find one, update it instead of creating a new one
2963 */
91447636
A
2964 KAUTH_GROUPS_LOCK();
2965 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2966 if ((el->el_uid == gm->gm_uid) &&
2967 (el->el_gid == gm->gm_gid)) {
2968 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2969 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2970 } else {
2971 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2972 }
316670eb 2973 gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
91447636
A
2974 kauth_groups_lru(gm);
2975 break;
2976 }
2977 }
2978 KAUTH_GROUPS_UNLOCK();
2979
2980 /* if we found an entry to update, stop here */
2981 if (gm != NULL)
2982 return;
2983
2984 /* allocate a new record */
2985 MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
2986 if (gm != NULL) {
2987 gm->gm_uid = el->el_uid;
2988 gm->gm_gid = el->el_gid;
2989 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2990 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2991 } else {
2992 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2993 }
316670eb 2994 gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0;
91447636
A
2995 }
2996
2997 /*
2d21ac55
A
2998 * Insert the new entry. Note that it's possible to race ourselves
2999 * here and end up with duplicate entries in the list. Wasteful, but
3000 * harmless since the first into the list will never be looked up,
3001 * and thus will eventually just fall off the end.
91447636
A
3002 */
3003 KAUTH_GROUPS_LOCK();
3004 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
316670eb 3005 if (++kauth_groups_count > kauth_groups_cachemax) {
91447636
A
3006 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
3007 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
3008 kauth_groups_count--;
3009 } else {
3010 gm = NULL;
3011 }
3012 KAUTH_GROUPS_UNLOCK();
3013
3014 /* free expired cache entry */
3015 if (gm != NULL)
3016 FREE(gm, M_KAUTH);
3017}
3018
316670eb
A
3019/*
3020 * Trim older entries from the group membership cache.
3021 *
3022 * Must be called with the group cache lock held.
3023 */
3024static void
3025kauth_groups_trimcache(int new_size) {
3026 struct kauth_group_membership *gm;
3027
3028 lck_mtx_assert(kauth_groups_mtx, LCK_MTX_ASSERT_OWNED);
3029
3030 while (kauth_groups_count > new_size) {
3031 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
3032 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
3033 kauth_groups_count--;
3034 FREE(gm, M_KAUTH);
3035 }
3036}
3037#endif /* CONFIG_EXT_RESOLVER */
2d21ac55 3038
91447636
A
3039/*
3040 * Group membership KPI
3041 */
2d21ac55 3042
91447636 3043/*
2d21ac55
A
3044 * kauth_cred_ismember_gid
3045 *
3046 * Description: Given a credential and a GID, determine if the GID is a member
3047 * of one of the supplementary groups associated with the given
3048 * credential
3049 *
3050 * Parameters: cred Credential to check in
3051 * gid GID to check for membership
3052 * resultp Pointer to int to contain the
3053 * result of the call
3054 *
3055 * Returns: 0 Success
316670eb 3056 * ENOENT Could not perform lookup
2d21ac55
A
3057 * kauth_resolver_submit:EWOULDBLOCK
3058 * kauth_resolver_submit:EINTR
3059 * kauth_resolver_submit:ENOMEM
0b4c1975
A
3060 * kauth_resolver_submit:ENOENT User space daemon did not vend
3061 * this credential.
2d21ac55
A
3062 * kauth_resolver_submit:??? Unlikely error from user space
3063 *
3064 * Implicit returns:
3065 * *resultp (modified) 1 Is member
3066 * 0 Is not member
3067 *
3068 * Notes: This function guarantees not to modify resultp when returning
3069 * an error.
3070 *
316670eb 3071 * This function effectively checks the EGID as well, since the
2d21ac55 3072 * EGID is cr_groups[0] as an implementation detail.
91447636
A
3073 */
3074int
3075kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
3076{
6d2010ae 3077 posix_cred_t pcred = posix_cred_get(cred);
316670eb 3078 int i;
91447636
A
3079
3080 /*
3081 * Check the per-credential list of override groups.
3082 *
3083 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
3084 * the cache should be used for that case.
3085 */
6d2010ae
A
3086 for (i = 0; i < pcred->cr_ngroups; i++) {
3087 if (gid == pcred->cr_groups[i]) {
91447636
A
3088 *resultp = 1;
3089 return(0);
3090 }
3091 }
3092
3093 /*
3094 * If we don't have a UID for group membership checks, the in-cred list
3095 * was authoritative and we can stop here.
3096 */
6d2010ae 3097 if (pcred->cr_gmuid == KAUTH_UID_NONE) {
91447636
A
3098 *resultp = 0;
3099 return(0);
3100 }
3101
316670eb
A
3102#if CONFIG_EXT_RESOLVER
3103 struct kauth_group_membership *gm;
3104 struct kauth_identity_extlookup el;
3105 int error;
3106
91447636 3107 /*
2d21ac55
A
3108 * If the resolver hasn't checked in yet, we are early in the boot
3109 * phase and the local group list is complete and authoritative.
91447636
A
3110 */
3111 if (!kauth_resolver_registered) {
3112 *resultp = 0;
3113 return(0);
3114 }
316670eb 3115
91447636
A
3116 /* TODO: */
3117 /* XXX check supplementary groups */
3118 /* XXX check whiteout groups */
3119 /* XXX nesting of supplementary/whiteout groups? */
3120
3121 /*
3122 * Check the group cache.
3123 */
3124 KAUTH_GROUPS_LOCK();
3125 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
6d2010ae 3126 if ((gm->gm_uid == pcred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
91447636
A
3127 kauth_groups_lru(gm);
3128 break;
3129 }
3130 }
3131
3132 /* did we find a membership entry? */
3133 if (gm != NULL)
3134 *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
3135 KAUTH_GROUPS_UNLOCK();
3136
3137 /* if we did, we can return now */
316670eb
A
3138 if (gm != NULL) {
3139 DTRACE_PROC2(kauth__group__cache__hit, int, pcred->cr_gmuid, int, gid);
91447636 3140 return(0);
316670eb
A
3141 }
3142
91447636 3143 /* nothing in the cache, need to go to userland */
b0d623f7
A
3144 bzero(&el, sizeof(el));
3145 el.el_info_pid = current_proc()->p_pid;
91447636 3146 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
6d2010ae 3147 el.el_uid = pcred->cr_gmuid;
91447636 3148 el.el_gid = gid;
2d21ac55 3149 el.el_member_valid = 0; /* XXX set by resolver? */
316670eb
A
3150
3151 DTRACE_PROC2(kauth__group__resolver__submitted, int, el.el_uid, int, el.el_gid);
3152
6d2010ae 3153 error = kauth_resolver_submit(&el, 0ULL);
316670eb
A
3154
3155 DTRACE_PROC2(kauth__group__resolver__returned, int, error, int, el.el_flags);
3156
91447636
A
3157 if (error != 0)
3158 return(error);
3159 /* save the results from the lookup */
3160 kauth_groups_updatecache(&el);
3161
3162 /* if we successfully ascertained membership, report */
3163 if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
3164 *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
3165 return(0);
3166 }
3167
3168 return(ENOENT);
316670eb
A
3169#else
3170 *resultp = 0;
3171 return(0);
3172#endif
91447636
A
3173}
3174
3175/*
2d21ac55
A
3176 * kauth_cred_ismember_guid
3177 *
3178 * Description: Determine whether the supplied credential is a member of the
3179 * group nominated by GUID.
3180 *
3181 * Parameters: cred Credential to check in
3182 * guidp Pointer to GUID whose group
3183 * we are testing for membership
3184 * resultp Pointer to int to contain the
3185 * result of the call
3186 *
3187 * Returns: 0 Success
3188 * kauth_cred_guid2gid:EINVAL
3189 * kauth_cred_ismember_gid:ENOENT
0b4c1975
A
3190 * kauth_resolver_submit:ENOENT User space daemon did not vend
3191 * this credential.
2d21ac55
A
3192 * kauth_cred_ismember_gid:EWOULDBLOCK
3193 * kauth_cred_ismember_gid:EINTR
3194 * kauth_cred_ismember_gid:ENOMEM
3195 * kauth_cred_ismember_gid:??? Unlikely error from user space
3196 *
3197 * Implicit returns:
3198 * *resultp (modified) 1 Is member
3199 * 0 Is not member
91447636
A
3200 */
3201int
316670eb 3202kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp)
91447636 3203{
316670eb 3204 int error = 0;
91447636 3205
316670eb 3206 switch (kauth_wellknown_guid(guidp)) {
91447636
A
3207 case KAUTH_WKG_NOBODY:
3208 *resultp = 0;
3209 break;
3210 case KAUTH_WKG_EVERYBODY:
3211 *resultp = 1;
3212 break;
3213 default:
316670eb 3214 {
316670eb 3215 gid_t gid;
490019cf
A
3216#if CONFIG_EXT_RESOLVER
3217 struct kauth_identity ki;
3218
b0d623f7
A
3219 /*
3220 * Grovel the identity cache looking for this GUID.
3221 * If we find it, and it is for a user record, return
3222 * false because it's not a group.
3223 *
3224 * This is necessary because we don't have -ve caching
3225 * of group memberships, and we really want to avoid
3226 * calling out to the resolver if at all possible.
3227 *
3228 * Because we're called by the ACL evaluator, and the
3229 * ACL evaluator is likely to encounter ACEs for users,
3230 * this is expected to be a common case.
3231 */
3232 ki.ki_valid = 0;
6d2010ae 3233 if ((error = kauth_identity_find_guid(guidp, &ki, NULL)) == 0 &&
b0d623f7
A
3234 !kauth_identity_guid_expired(&ki)) {
3235 if (ki.ki_valid & KI_VALID_GID) {
3236 /* It's a group after all... */
3237 gid = ki.ki_gid;
3238 goto do_check;
3239 }
3240 if (ki.ki_valid & KI_VALID_UID) {
3241 *resultp = 0;
3242 return (0);
3243 }
3244 }
490019cf 3245#endif /* CONFIG_EXT_RESOLVER */
b0d623f7
A
3246 /*
3247 * Attempt to translate the GUID to a GID. Even if
3248 * this fails, we will have primed the cache if it is
3249 * a user record and we'll see it above the next time
3250 * we're asked.
3251 */
91447636
A
3252 if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
3253 /*
3254 * If we have no guid -> gid translation, it's not a group and
3255 * thus the cred can't be a member.
3256 */
3257 if (error == ENOENT) {
3258 *resultp = 0;
3259 error = 0;
3260 }
3261 } else {
490019cf 3262#if CONFIG_EXT_RESOLVER
b0d623f7 3263 do_check:
490019cf 3264#endif /* CONFIG_EXT_RESOLVER */
91447636
A
3265 error = kauth_cred_ismember_gid(cred, gid, resultp);
3266 }
3267 }
316670eb
A
3268 break;
3269 }
91447636
A
3270 return(error);
3271}
3272
3273/*
2d21ac55
A
3274 * kauth_cred_gid_subset
3275 *
3276 * Description: Given two credentials, determine if all GIDs associated with
3277 * the first are also associated with the second
3278 *
3279 * Parameters: cred1 Credential to check for
3280 * cred2 Credential to check in
3281 * resultp Pointer to int to contain the
3282 * result of the call
3283 *
3284 * Returns: 0 Success
3285 * non-zero See kauth_cred_ismember_gid for
3286 * error codes
3287 *
3288 * Implicit returns:
3289 * *resultp (modified) 1 Is subset
3290 * 0 Is not subset
3291 *
3292 * Notes: This function guarantees not to modify resultp when returning
3293 * an error.
3294 */
3295int
3296kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
3297{
3298 int i, err, res = 1;
3299 gid_t gid;
6d2010ae
A
3300 posix_cred_t pcred1 = posix_cred_get(cred1);
3301 posix_cred_t pcred2 = posix_cred_get(cred2);
2d21ac55
A
3302
3303 /* First, check the local list of groups */
6d2010ae
A
3304 for (i = 0; i < pcred1->cr_ngroups; i++) {
3305 gid = pcred1->cr_groups[i];
2d21ac55
A
3306 if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) {
3307 return err;
3308 }
3309
6d2010ae 3310 if (!res && gid != pcred2->cr_rgid && gid != pcred2->cr_svgid) {
2d21ac55
A
3311 *resultp = 0;
3312 return 0;
3313 }
3314 }
3315
3316 /* Check real gid */
6d2010ae 3317 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_rgid, &res)) != 0) {
2d21ac55
A
3318 return err;
3319 }
3320
6d2010ae
A
3321 if (!res && pcred1->cr_rgid != pcred2->cr_rgid &&
3322 pcred1->cr_rgid != pcred2->cr_svgid) {
2d21ac55
A
3323 *resultp = 0;
3324 return 0;
3325 }
3326
3327 /* Finally, check saved gid */
6d2010ae 3328 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_svgid, &res)) != 0){
2d21ac55
A
3329 return err;
3330 }
3331
6d2010ae
A
3332 if (!res && pcred1->cr_svgid != pcred2->cr_rgid &&
3333 pcred1->cr_svgid != pcred2->cr_svgid) {
2d21ac55
A
3334 *resultp = 0;
3335 return 0;
3336 }
3337
3338 *resultp = 1;
3339 return 0;
3340}
3341
3342
3343/*
3344 * kauth_cred_issuser
3345 *
3346 * Description: Fast replacement for issuser()
3347 *
3348 * Parameters: cred Credential to check for super
3349 * user privileges
3350 *
3351 * Returns: 0 Not super user
3352 * !0 Is super user
3353 *
3354 * Notes: This function uses a magic number which is not a manifest
3355 * constant; this is bad practice.
91447636
A
3356 */
3357int
3358kauth_cred_issuser(kauth_cred_t cred)
3359{
6d2010ae 3360 return(kauth_cred_getuid(cred) == 0);
91447636
A
3361}
3362
2d21ac55 3363
91447636
A
3364/*
3365 * Credential KPI
3366 */
3367
3368/* lock protecting credential hash table */
3369static lck_mtx_t *kauth_cred_hash_mtx;
3370#define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
3371#define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
0c530ab8 3372#if KAUTH_CRED_HASH_DEBUG
b0d623f7 3373#define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
0c530ab8
A
3374#else /* !KAUTH_CRED_HASH_DEBUG */
3375#define KAUTH_CRED_HASH_LOCK_ASSERT()
3376#endif /* !KAUTH_CRED_HASH_DEBUG */
91447636 3377
2d21ac55
A
3378
3379/*
3380 * kauth_cred_init
3381 *
3382 * Description: Initialize the credential hash cache
3383 *
3384 * Parameters: (void)
3385 *
3386 * Returns: (void)
3387 *
3388 * Notes: Intialize the credential hash cache for use; the credential
3389 * hash cache is used convert duplicate credentials into a
3390 * single reference counted credential in order to save wired
3391 * kernel memory. In practice, this generally means a desktop
3392 * system runs with a few tens of credentials, instead of one
3393 * per process, one per thread, one per vnode cache entry, and
3394 * so on. This generally results in savings of 200K or more
3395 * (potentially much more on server systems).
3396 *
3397 * The hash cache internally has a reference on the credential
3398 * for itself as a means of avoiding a reclaim race for a
3399 * credential in the process of having it's last non-hash
3400 * reference released. This would otherwise result in the
3401 * possibility of a freed credential that was still in uses due
3402 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3403 *
3404 * On final release, the hash reference is droped, and the
3405 * credential is freed back to the system.
3406 *
3407 * This function is called from kauth_init() in the file
3408 * kern_authorization.c.
3409 */
91447636
A
3410void
3411kauth_cred_init(void)
3412{
3413 int i;
3414
3415 kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
91447636
A
3416
3417 /*allocate credential hash table */
3418 MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
3e170ce0 3419 (sizeof(struct kauth_cred_entry_head) * KAUTH_CRED_TABLE_SIZE),
91447636 3420 M_KAUTH, M_WAITOK | M_ZERO);
2d21ac55
A
3421 if (kauth_cred_table_anchor == NULL)
3422 panic("startup: kauth_cred_init");
3e170ce0 3423 for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
91447636
A
3424 TAILQ_INIT(&kauth_cred_table_anchor[i]);
3425 }
3426}
3427
2d21ac55 3428
91447636 3429/*
2d21ac55
A
3430 * kauth_getuid
3431 *
3432 * Description: Get the current thread's effective UID.
3433 *
3434 * Parameters: (void)
3435 *
3436 * Returns: (uid_t) The effective UID of the
3437 * current thread
91447636
A
3438 */
3439uid_t
3440kauth_getuid(void)
3441{
6d2010ae 3442 return(kauth_cred_getuid(kauth_cred_get()));
91447636
A
3443}
3444
2d21ac55 3445
91447636 3446/*
2d21ac55
A
3447 * kauth_getruid
3448 *
3449 * Description: Get the current thread's real UID.
3450 *
3451 * Parameters: (void)
3452 *
3453 * Returns: (uid_t) The real UID of the current
3454 * thread
91447636
A
3455 */
3456uid_t
3457kauth_getruid(void)
3458{
6d2010ae 3459 return(kauth_cred_getruid(kauth_cred_get()));
91447636
A
3460}
3461
2d21ac55 3462
91447636 3463/*
2d21ac55
A
3464 * kauth_getgid
3465 *
3466 * Description: Get the current thread's effective GID.
3467 *
3468 * Parameters: (void)
3469 *
3470 * Returns: (gid_t) The effective GID of the
3471 * current thread
91447636
A
3472 */
3473gid_t
3474kauth_getgid(void)
3475{
6d2010ae 3476 return(kauth_cred_getgid(kauth_cred_get()));
91447636
A
3477}
3478
2d21ac55 3479
91447636 3480/*
2d21ac55
A
3481 * kauth_getgid
3482 *
3483 * Description: Get the current thread's real GID.
3484 *
3485 * Parameters: (void)
3486 *
3487 * Returns: (gid_t) The real GID of the current
3488 * thread
91447636
A
3489 */
3490gid_t
3491kauth_getrgid(void)
3492{
6d2010ae 3493 return(kauth_cred_getrgid(kauth_cred_get()));
91447636
A
3494}
3495
2d21ac55 3496
91447636 3497/*
2d21ac55
A
3498 * kauth_cred_get
3499 *
3500 * Description: Returns a pointer to the current thread's credential
3501 *
3502 * Parameters: (void)
3503 *
3504 * Returns: (kauth_cred_t) Pointer to the current thread's
3505 * credential
3506 *
3507 * Notes: This function does not take a reference; because of this, the
3508 * caller MUST NOT do anything that would let the thread's
3509 * credential change while using the returned value, without
3510 * first explicitly taking their own reference.
3511 *
3512 * If a caller intends to take a reference on the resulting
3513 * credential pointer from calling this function, it is strongly
3514 * recommended that the caller use kauth_cred_get_with_ref()
3515 * instead, to protect against any future changes to the cred
3516 * locking protocols; such changes could otherwise potentially
3517 * introduce race windows in the callers code.
91447636
A
3518 */
3519kauth_cred_t
3520kauth_cred_get(void)
3521{
3522 struct proc *p;
3523 struct uthread *uthread;
3524
3525 uthread = get_bsdthread_info(current_thread());
3526 /* sanity */
3527 if (uthread == NULL)
3528 panic("thread wants credential but has no BSD thread info");
3529 /*
2d21ac55
A
3530 * We can lazy-bind credentials to threads, as long as their processes
3531 * have them.
3532 *
3533 * XXX If we later inline this function, the code in this block
3534 * XXX should probably be called out in a function.
91447636
A
3535 */
3536 if (uthread->uu_ucred == NOCRED) {
3537 if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3538 panic("thread wants credential but has no BSD process");
0c530ab8 3539 uthread->uu_ucred = kauth_cred_proc_ref(p);
91447636
A
3540 }
3541 return(uthread->uu_ucred);
3542}
3543
b0d623f7
A
3544void
3545mach_kauth_cred_uthread_update(void)
3546{
3547 uthread_t uthread;
3548 proc_t proc;
3549
3550 uthread = get_bsdthread_info(current_thread());
3551 proc = current_proc();
3552
3553 kauth_cred_uthread_update(uthread, proc);
3554}
2d21ac55
A
3555
3556/*
3557 * kauth_cred_uthread_update
3558 *
3559 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3560 * late-bind the uthread cred to the proc cred.
3561 *
3562 * Parameters: uthread_t The uthread to update
3563 * proc_t The process to update to
3564 *
3565 * Returns: (void)
3566 *
3567 * Notes: This code is common code called from system call or trap entry
3568 * in the case that the process thread may have been changed
3569 * since the last time the thread entered the kernel. It is
3570 * generally only called with the current uthread and process as
3571 * parameters.
3572 */
3573void
3574kauth_cred_uthread_update(uthread_t uthread, proc_t proc)
3575{
3576 if (uthread->uu_ucred != proc->p_ucred &&
3577 (uthread->uu_flag & UT_SETUID) == 0) {
3578 kauth_cred_t old = uthread->uu_ucred;
3579 uthread->uu_ucred = kauth_cred_proc_ref(proc);
3580 if (IS_VALID_CRED(old))
3581 kauth_cred_unref(&old);
3582 }
3583}
3584
3585
91447636 3586/*
2d21ac55
A
3587 * kauth_cred_get_with_ref
3588 *
3589 * Description: Takes a reference on the current thread's credential, and then
3590 * returns a pointer to it to the caller.
3591 *
3592 * Parameters: (void)
3593 *
3594 * Returns: (kauth_cred_t) Pointer to the current thread's
3595 * newly referenced credential
3596 *
3597 * Notes: This function takes a reference on the credential before
3598 * returning it to the caller.
3599 *
3600 * It is the responsibility of the calling code to release this
3601 * reference when the credential is no longer in use.
3602 *
3603 * Since the returned reference may be a persistent reference
3604 * (e.g. one cached in another data structure with a lifetime
3605 * longer than the calling function), this release may be delayed
3606 * until such time as the persistent reference is to be destroyed.
3607 * An example of this would be the per vnode credential cache used
3608 * to accelerate lookup operations.
91447636
A
3609 */
3610kauth_cred_t
3611kauth_cred_get_with_ref(void)
3612{
3613 struct proc *procp;
3614 struct uthread *uthread;
3615
3616 uthread = get_bsdthread_info(current_thread());
3617 /* sanity checks */
3618 if (uthread == NULL)
3619 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
3620 if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3621 panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
3622
3623 /*
2d21ac55
A
3624 * We can lazy-bind credentials to threads, as long as their processes
3625 * have them.
3626 *
3627 * XXX If we later inline this function, the code in this block
3628 * XXX should probably be called out in a function.
91447636 3629 */
91447636
A
3630 if (uthread->uu_ucred == NOCRED) {
3631 /* take reference for new cred in thread */
0c530ab8 3632 uthread->uu_ucred = kauth_cred_proc_ref(procp);
91447636
A
3633 }
3634 /* take a reference for our caller */
3635 kauth_cred_ref(uthread->uu_ucred);
91447636
A
3636 return(uthread->uu_ucred);
3637}
3638
2d21ac55 3639
91447636 3640/*
2d21ac55
A
3641 * kauth_cred_proc_ref
3642 *
3643 * Description: Takes a reference on the current process's credential, and
3644 * then returns a pointer to it to the caller.
3645 *
3646 * Parameters: procp Process whose credential we
3647 * intend to take a reference on
3648 *
3649 * Returns: (kauth_cred_t) Pointer to the process's
3650 * newly referenced credential
3651 *
4bd07ac2 3652 * Locks: PROC_UCRED_LOCK is held before taking the reference and released
2d21ac55
A
3653 * after the refeence is taken to protect the p_ucred field of
3654 * the process referred to by procp.
3655 *
3656 * Notes: This function takes a reference on the credential before
3657 * returning it to the caller.
3658 *
3659 * It is the responsibility of the calling code to release this
3660 * reference when the credential is no longer in use.
3661 *
3662 * Since the returned reference may be a persistent reference
3663 * (e.g. one cached in another data structure with a lifetime
3664 * longer than the calling function), this release may be delayed
3665 * until such time as the persistent reference is to be destroyed.
3666 * An example of this would be the per vnode credential cache used
3667 * to accelerate lookup operations.
91447636
A
3668 */
3669kauth_cred_t
3670kauth_cred_proc_ref(proc_t procp)
3671{
3672 kauth_cred_t cred;
3673
4bd07ac2 3674 proc_ucred_lock(procp);
91447636
A
3675 cred = proc_ucred(procp);
3676 kauth_cred_ref(cred);
4bd07ac2 3677 proc_ucred_unlock(procp);
91447636
A
3678 return(cred);
3679}
3680
2d21ac55 3681
91447636 3682/*
2d21ac55
A
3683 * kauth_cred_alloc
3684 *
3685 * Description: Allocate a new credential
3686 *
3687 * Parameters: (void)
3688 *
3689 * Returns: !NULL Newly allocated credential
3690 * NULL Insufficient memory
3691 *
3692 * Notes: The newly allocated credential is zero'ed as part of the
3693 * allocation process, with the exception of the reference
3694 * count, which is set to 1 to indicate a single reference
3695 * held by the caller.
3696 *
3697 * Since newly allocated credentials have no external pointers
3698 * referencing them, prior to making them visible in an externally
3699 * visible pointer (e.g. by adding them to the credential hash
3700 * cache) is the only legal time in which an existing credential
3701 * can be safely iinitialized or modified directly.
3702 *
3703 * After initialization, the caller is expected to call the
3704 * function kauth_cred_add() to add the credential to the hash
3705 * cache, after which time it's frozen and becomes publically
3706 * visible.
3707 *
3708 * The release protocol depends on kauth_hash_add() being called
3709 * before kauth_cred_rele() (there is a diagnostic panic which
3710 * will trigger if this protocol is not observed).
3711 *
3712 * XXX: This function really ought to be static, rather than being
3713 * exported as KPI, since a failure of kauth_cred_add() can only
3714 * be handled by an explicit free of the credential; such frees
3715 * depend on knowlegdge of the allocation method used, which is
3716 * permitted to change between kernel revisions.
3717 *
3718 * XXX: In the insufficient resource case, this code panic's rather
3719 * than returning a NULL pointer; the code that calls this
3720 * function needs to be audited before this can be changed.
91447636
A
3721 */
3722kauth_cred_t
3723kauth_cred_alloc(void)
3724{
3725 kauth_cred_t newcred;
3726
2d21ac55 3727 MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK);
91447636 3728 if (newcred != 0) {
6d2010ae 3729 posix_cred_t newpcred = posix_cred_get(newcred);
2d21ac55 3730 bzero(newcred, sizeof(*newcred));
91447636 3731 newcred->cr_ref = 1;
6d2010ae 3732 newcred->cr_audit.as_aia_p = audit_default_aia_p;
91447636 3733 /* must do this, or cred has same group membership as uid 0 */
6d2010ae 3734 newpcred->cr_gmuid = KAUTH_UID_NONE;
91447636
A
3735#if CRED_DIAGNOSTIC
3736 } else {
3737 panic("kauth_cred_alloc: couldn't allocate credential");
3738#endif
3739 }
3740
3741#if KAUTH_CRED_HASH_DEBUG
3742 kauth_cred_count++;
3743#endif
3744
2d21ac55
A
3745#if CONFIG_MACF
3746 mac_cred_label_init(newcred);
3747#endif
3748
91447636
A
3749 return(newcred);
3750}
3751
2d21ac55 3752
91447636 3753/*
2d21ac55
A
3754 * kauth_cred_create
3755 *
3756 * Description: Look to see if we already have a known credential in the hash
3757 * cache; if one is found, bump the reference count and return
3758 * it. If there are no credentials that match the given
3759 * credential, then allocate a new credential.
3760 *
3761 * Parameters: cred Template for credential to
3762 * be created
3763 *
3764 * Returns: (kauth_cred_t) The credential that was found
3765 * in the hash or created
3766 * NULL kauth_cred_add() failed, or
3767 * there was not an egid specified
3768 *
3769 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3770 * maintain this field, we can't expect callers to know how it
3771 * needs to be set. Callers should be prepared for this field
3772 * to be overwritten.
91447636 3773 *
2d21ac55
A
3774 * XXX: This code will tight-loop if memory for a new credential is
3775 * persistently unavailable; this is perhaps not the wisest way
3776 * to handle this condition, but current callers do not expect
3777 * a failure.
91447636
A
3778 */
3779kauth_cred_t
3780kauth_cred_create(kauth_cred_t cred)
3781{
3782 kauth_cred_t found_cred, new_cred = NULL;
6d2010ae 3783 posix_cred_t pcred = posix_cred_get(cred);
0b4c1975 3784 int is_member = 0;
91447636 3785
2d21ac55
A
3786 KAUTH_CRED_HASH_LOCK_ASSERT();
3787
6d2010ae
A
3788 if (pcred->cr_flags & CRF_NOMEMBERD) {
3789 pcred->cr_gmuid = KAUTH_UID_NONE;
0b4c1975
A
3790 } else {
3791 /*
3792 * If the template credential is not opting out of external
3793 * group membership resolution, then we need to check that
3794 * the UID we will be using is resolvable by the external
3795 * resolver. If it's not, then we opt it out anyway, since
3796 * all future external resolution requests will be failing
3797 * anyway, and potentially taking a long time to do it. We
3798 * use gid 0 because we always know it will exist and not
3799 * trigger additional lookups. This is OK, because we end up
3800 * precatching the information here as a result.
3801 */
3802 if (!kauth_cred_ismember_gid(cred, 0, &is_member)) {
3803 /*
3804 * It's a recognized value; we don't really care about
3805 * the answer, so long as it's something the external
3806 * resolver could have vended.
3807 */
6d2010ae 3808 pcred->cr_gmuid = pcred->cr_uid;
0b4c1975
A
3809 } else {
3810 /*
3811 * It's not something the external resolver could
3812 * have vended, so we don't want to ask it more
3813 * questions about the credential in the future. This
3814 * speeds up future lookups, as long as the caller
3815 * caches results; otherwise, it the same recurring
3816 * cost. Since most credentials are used multiple
3817 * times, we still get some performance win from this.
3818 */
6d2010ae
A
3819 pcred->cr_gmuid = KAUTH_UID_NONE;
3820 pcred->cr_flags |= CRF_NOMEMBERD;
0b4c1975
A
3821 }
3822 }
2d21ac55
A
3823
3824 /* Caller *must* specify at least the egid in cr_groups[0] */
6d2010ae 3825 if (pcred->cr_ngroups < 1)
2d21ac55 3826 return(NULL);
91447636
A
3827
3828 for (;;) {
3829 KAUTH_CRED_HASH_LOCK();
3830 found_cred = kauth_cred_find(cred);
3831 if (found_cred != NULL) {
2d21ac55
A
3832 /*
3833 * Found an existing credential so we'll bump
3834 * reference count and return
3835 */
91447636
A
3836 kauth_cred_ref(found_cred);
3837 KAUTH_CRED_HASH_UNLOCK();
3838 return(found_cred);
3839 }
3840 KAUTH_CRED_HASH_UNLOCK();
3841
2d21ac55
A
3842 /*
3843 * No existing credential found. Create one and add it to
3844 * our hash table.
3845 */
91447636
A
3846 new_cred = kauth_cred_alloc();
3847 if (new_cred != NULL) {
3848 int err;
6d2010ae
A
3849 posix_cred_t new_pcred = posix_cred_get(new_cred);
3850 new_pcred->cr_uid = pcred->cr_uid;
3851 new_pcred->cr_ruid = pcred->cr_ruid;
3852 new_pcred->cr_svuid = pcred->cr_svuid;
3853 new_pcred->cr_rgid = pcred->cr_rgid;
3854 new_pcred->cr_svgid = pcred->cr_svgid;
3855 new_pcred->cr_gmuid = pcred->cr_gmuid;
3856 new_pcred->cr_ngroups = pcred->cr_ngroups;
3857 bcopy(&pcred->cr_groups[0], &new_pcred->cr_groups[0], sizeof(new_pcred->cr_groups));
b0d623f7
A
3858#if CONFIG_AUDIT
3859 bcopy(&cred->cr_audit, &new_cred->cr_audit,
3860 sizeof(new_cred->cr_audit));
b0d623f7 3861#endif
6d2010ae 3862 new_pcred->cr_flags = pcred->cr_flags;
2d21ac55 3863
91447636
A
3864 KAUTH_CRED_HASH_LOCK();
3865 err = kauth_cred_add(new_cred);
3866 KAUTH_CRED_HASH_UNLOCK();
3867
2d21ac55 3868 /* Retry if kauth_cred_add returns non zero value */
91447636
A
3869 if (err == 0)
3870 break;
2d21ac55
A
3871#if CONFIG_MACF
3872 mac_cred_label_destroy(new_cred);
3873#endif
b0d623f7
A
3874 AUDIT_SESSION_UNREF(new_cred);
3875
2d21ac55 3876 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
91447636
A
3877 new_cred = NULL;
3878 }
3879 }
3880
3881 return(new_cred);
3882}
3883
2d21ac55 3884
91447636 3885/*
2d21ac55
A
3886 * kauth_cred_setresuid
3887 *
3888 * Description: Update the given credential using the UID arguments. The given
3889 * UIDs are used to set the effective UID, real UID, saved UID,
3890 * and GMUID (used for group membership checking).
3891 *
3892 * Parameters: cred The original credential
3893 * ruid The new real UID
3894 * euid The new effective UID
3895 * svuid The new saved UID
3896 * gmuid KAUTH_UID_NONE -or- the new
3897 * group membership UID
3898 *
3899 * Returns: (kauth_cred_t) The updated credential
3900 *
3901 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3902 * setting, so if you don't want it to change, pass it the
3903 * previous value, explicitly.
3904 *
3905 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3906 * if it returns a credential other than the one it is passed,
3907 * will have dropped the reference on the passed credential. All
3908 * callers should be aware of this, and treat this function as an
3909 * unref + ref, potentially on different credentials.
3910 *
3911 * Because of this, the caller is expected to take its own
3912 * reference on the credential passed as the first parameter,
3913 * and be prepared to release the reference on the credential
3914 * that is returned to them, if it is not intended to be a
3915 * persistent reference.
91447636
A
3916 */
3917kauth_cred_t
2d21ac55 3918kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
91447636
A
3919{
3920 struct ucred temp_cred;
6d2010ae
A
3921 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3922 posix_cred_t pcred = posix_cred_get(cred);
91447636
A
3923
3924 NULLCRED_CHECK(cred);
3925
2d21ac55
A
3926 /*
3927 * We don't need to do anything if the UIDs we are changing are
3928 * already the same as the UIDs passed in
91447636 3929 */
6d2010ae
A
3930 if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) &&
3931 (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) &&
3932 (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) &&
3933 (pcred->cr_gmuid == gmuid)) {
91447636
A
3934 /* no change needed */
3935 return(cred);
3936 }
3937
2d21ac55
A
3938 /*
3939 * Look up in cred hash table to see if we have a matching credential
3940 * with the new values; this is done by calling kauth_cred_update().
91447636
A
3941 */
3942 bcopy(cred, &temp_cred, sizeof(temp_cred));
2d21ac55 3943 if (euid != KAUTH_UID_NONE) {
6d2010ae 3944 temp_pcred->cr_uid = euid;
2d21ac55
A
3945 }
3946 if (ruid != KAUTH_UID_NONE) {
6d2010ae 3947 temp_pcred->cr_ruid = ruid;
2d21ac55
A
3948 }
3949 if (svuid != KAUTH_UID_NONE) {
6d2010ae 3950 temp_pcred->cr_svuid = svuid;
2d21ac55
A
3951 }
3952
3953 /*
3954 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3955 * opt out of participation in external group resolution, unless we
3956 * unless we explicitly opt back in later.
3957 */
6d2010ae
A
3958 if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) {
3959 temp_pcred->cr_flags |= CRF_NOMEMBERD;
2d21ac55 3960 }
91447636
A
3961
3962 return(kauth_cred_update(cred, &temp_cred, TRUE));
3963}
3964
2d21ac55 3965
91447636 3966/*
2d21ac55
A
3967 * kauth_cred_setresgid
3968 *
3969 * Description: Update the given credential using the GID arguments. The given
3970 * GIDs are used to set the effective GID, real GID, and saved
3971 * GID.
3972 *
3973 * Parameters: cred The original credential
3974 * rgid The new real GID
3975 * egid The new effective GID
3976 * svgid The new saved GID
3977 *
3978 * Returns: (kauth_cred_t) The updated credential
3979 *
3980 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3981 * if it returns a credential other than the one it is passed,
3982 * will have dropped the reference on the passed credential. All
3983 * callers should be aware of this, and treat this function as an
3984 * unref + ref, potentially on different credentials.
3985 *
3986 * Because of this, the caller is expected to take its own
3987 * reference on the credential passed as the first parameter,
3988 * and be prepared to release the reference on the credential
3989 * that is returned to them, if it is not intended to be a
3990 * persistent reference.
91447636
A
3991 */
3992kauth_cred_t
2d21ac55 3993kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
91447636 3994{
2d21ac55 3995 struct ucred temp_cred;
6d2010ae
A
3996 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3997 posix_cred_t pcred = posix_cred_get(cred);
91447636
A
3998
3999 NULLCRED_CHECK(cred);
2d21ac55 4000 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
91447636 4001
2d21ac55
A
4002 /*
4003 * We don't need to do anything if the given GID are already the
4004 * same as the GIDs in the credential.
91447636 4005 */
6d2010ae
A
4006 if (pcred->cr_groups[0] == egid &&
4007 pcred->cr_rgid == rgid &&
4008 pcred->cr_svgid == svgid) {
91447636
A
4009 /* no change needed */
4010 return(cred);
4011 }
4012
2d21ac55
A
4013 /*
4014 * Look up in cred hash table to see if we have a matching credential
4015 * with the new values; this is done by calling kauth_cred_update().
91447636
A
4016 */
4017 bcopy(cred, &temp_cred, sizeof(temp_cred));
2d21ac55
A
4018 if (egid != KAUTH_GID_NONE) {
4019 /* displacing a supplementary group opts us out of memberd */
4020 if (kauth_cred_change_egid(&temp_cred, egid)) {
4021 DEBUG_CRED_CHANGE("displaced!\n");
6d2010ae
A
4022 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4023 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
2d21ac55
A
4024 } else {
4025 DEBUG_CRED_CHANGE("not displaced\n");
4026 }
4027 }
4028 if (rgid != KAUTH_GID_NONE) {
6d2010ae 4029 temp_pcred->cr_rgid = rgid;
2d21ac55
A
4030 }
4031 if (svgid != KAUTH_GID_NONE) {
6d2010ae 4032 temp_pcred->cr_svgid = svgid;
2d21ac55 4033 }
91447636
A
4034
4035 return(kauth_cred_update(cred, &temp_cred, TRUE));
4036}
4037
2d21ac55 4038
91447636 4039/*
2d21ac55
A
4040 * Update the given credential with the given groups. We only allocate a new
4041 * credential when the given gid actually results in changes to the existing
4042 * credential.
4043 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
4044 * which will be used for group membership checking.
4045 */
4046/*
4047 * kauth_cred_setgroups
4048 *
4049 * Description: Update the given credential using the provide supplementary
4050 * group list and group membership UID
4051 *
4052 * Parameters: cred The original credential
4053 * groups Pointer to gid_t array which
4054 * contains the new group list
316670eb 4055 * groupcount The count of valid groups which
2d21ac55
A
4056 * are contained in 'groups'
4057 * gmuid KAUTH_UID_NONE -or- the new
4058 * group membership UID
4059 *
4060 * Returns: (kauth_cred_t) The updated credential
4061 *
4062 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
4063 * setting, so if you don't want it to change, pass it the
4064 * previous value, explicitly.
4065 *
4066 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4067 * if it returns a credential other than the one it is passed,
4068 * will have dropped the reference on the passed credential. All
4069 * callers should be aware of this, and treat this function as an
4070 * unref + ref, potentially on different credentials.
4071 *
4072 * Because of this, the caller is expected to take its own
4073 * reference on the credential passed as the first parameter,
4074 * and be prepared to release the reference on the credential
4075 * that is returned to them, if it is not intended to be a
4076 * persistent reference.
4077 *
316670eb 4078 * XXX: Changes are determined in ordinal order - if the caller passes
2d21ac55
A
4079 * in the same groups list that is already present in the
4080 * credential, but the members are in a different order, even if
4081 * the EGID is not modified (i.e. cr_groups[0] is the same), it
4082 * is considered a modification to the credential, and a new
4083 * credential is created.
4084 *
4085 * This should perhaps be better optimized, but it is considered
4086 * to be the caller's problem.
91447636
A
4087 */
4088kauth_cred_t
2d21ac55 4089kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
91447636 4090{
2d21ac55
A
4091 int i;
4092 struct ucred temp_cred;
6d2010ae
A
4093 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4094 posix_cred_t pcred;
91447636
A
4095
4096 NULLCRED_CHECK(cred);
4097
6d2010ae
A
4098 pcred = posix_cred_get(cred);
4099
2d21ac55
A
4100 /*
4101 * We don't need to do anything if the given list of groups does not
4102 * change.
91447636 4103 */
6d2010ae 4104 if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) {
91447636 4105 for (i = 0; i < groupcount; i++) {
6d2010ae 4106 if (pcred->cr_groups[i] != groups[i])
91447636
A
4107 break;
4108 }
4109 if (i == groupcount) {
4110 /* no change needed */
4111 return(cred);
4112 }
4113 }
4114
2d21ac55
A
4115 /*
4116 * Look up in cred hash table to see if we have a matching credential
4117 * with new values. If we are setting or clearing the gmuid, then
4118 * update the cr_flags, since clearing it is sticky. This permits an
4119 * opt-out of memberd processing using setgroups(), and an opt-in
4120 * using initgroups(). This is required for POSIX conformance.
91447636
A
4121 */
4122 bcopy(cred, &temp_cred, sizeof(temp_cred));
6d2010ae
A
4123 temp_pcred->cr_ngroups = groupcount;
4124 bcopy(groups, temp_pcred->cr_groups, sizeof(temp_pcred->cr_groups));
4125 temp_pcred->cr_gmuid = gmuid;
2d21ac55 4126 if (gmuid == KAUTH_UID_NONE)
6d2010ae 4127 temp_pcred->cr_flags |= CRF_NOMEMBERD;
2d21ac55 4128 else
6d2010ae 4129 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
91447636
A
4130
4131 return(kauth_cred_update(cred, &temp_cred, TRUE));
4132}
4133
6d2010ae 4134/*
316670eb 4135 * Notes: The return value exists to account for the possibility of a
6d2010ae
A
4136 * kauth_cred_t without a POSIX label. This will be the case in
4137 * the future (see posix_cred_get() below, for more details).
4138 */
39236c6e
A
4139#if CONFIG_EXT_RESOLVER
4140int kauth_external_supplementary_groups_supported = 1;
4141
4142SYSCTL_INT(_kern, OID_AUTO, ds_supgroups_supported, CTLFLAG_RW | CTLFLAG_LOCKED, &kauth_external_supplementary_groups_supported, 0, "");
4143#endif
4144
6d2010ae
A
4145int
4146kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, int *countp)
4147{
4148 int limit = NGROUPS;
39236c6e
A
4149 posix_cred_t pcred;
4150
4151 pcred = posix_cred_get(cred);
4152
4153#if CONFIG_EXT_RESOLVER
4154 /*
4155 * If we've not opted out of using the resolver, then convert the cred to a list
4156 * of supplemental groups. We do this only if there has been a resolver to talk to,
4157 * since we may be too early in boot, or in an environment that isn't using DS.
4158 */
4159 if (kauth_identitysvc_has_registered && kauth_external_supplementary_groups_supported && (pcred->cr_flags & CRF_NOMEMBERD) == 0) {
4160 uid_t uid = kauth_cred_getuid(cred);
4161 int err;
4162
4163 err = kauth_cred_uid2groups(&uid, grouplist, countp);
4164 if (!err)
4165 return 0;
4166
4167 /* On error just fall through */
4168 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err);
4169 }
4170#endif /* CONFIG_EXT_RESOLVER */
6d2010ae
A
4171
4172 /*
4173 * If they just want a copy of the groups list, they may not care
4174 * about the actual count. If they specify an input count, however,
4175 * treat it as an indicator of the buffer size available in grouplist,
4176 * and limit the returned list to that size.
4177 */
4178 if (countp) {
39236c6e 4179 limit = MIN(*countp, pcred->cr_ngroups);
6d2010ae
A
4180 *countp = limit;
4181 }
4182
39236c6e 4183 memcpy(grouplist, pcred->cr_groups, sizeof(gid_t) * limit);
6d2010ae
A
4184
4185 return 0;
4186}
4187
2d21ac55 4188
91447636 4189/*
2d21ac55
A
4190 * kauth_cred_setuidgid
4191 *
4192 * Description: Update the given credential using the UID and GID arguments.
4193 * The given UID is used to set the effective UID, real UID, and
4194 * saved UID. The given GID is used to set the effective GID,
4195 * real GID, and saved GID.
4196 *
4197 * Parameters: cred The original credential
4198 * uid The new UID to use
4199 * gid The new GID to use
4200 *
4201 * Returns: (kauth_cred_t) The updated credential
4202 *
4203 * Notes: We set the gmuid to uid if the credential we are inheriting
4204 * from has not opted out of memberd participation; otherwise
4205 * we set it to KAUTH_UID_NONE
4206 *
4207 * This code is only ever called from the per-thread credential
4208 * code path in the "set per thread credential" case; and in
4209 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
4210 * flag is set.
4211 *
4212 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4213 * if it returns a credential other than the one it is passed,
4214 * will have dropped the reference on the passed credential. All
4215 * callers should be aware of this, and treat this function as an
4216 * unref + ref, potentially on different credentials.
4217 *
4218 * Because of this, the caller is expected to take its own
4219 * reference on the credential passed as the first parameter,
4220 * and be prepared to release the reference on the credential
4221 * that is returned to them, if it is not intended to be a
4222 * persistent reference.
91447636
A
4223 */
4224kauth_cred_t
4225kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4226{
4227 struct ucred temp_cred;
6d2010ae
A
4228 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4229 posix_cred_t pcred;
91447636
A
4230
4231 NULLCRED_CHECK(cred);
4232
6d2010ae
A
4233 pcred = posix_cred_get(cred);
4234
2d21ac55
A
4235 /*
4236 * We don't need to do anything if the effective, real and saved
4237 * user IDs are already the same as the user ID passed into us.
91447636 4238 */
6d2010ae
A
4239 if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid &&
4240 pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) {
91447636
A
4241 /* no change needed */
4242 return(cred);
4243 }
4244
2d21ac55
A
4245 /*
4246 * Look up in cred hash table to see if we have a matching credential
4247 * with the new values.
91447636
A
4248 */
4249 bzero(&temp_cred, sizeof(temp_cred));
6d2010ae
A
4250 temp_pcred->cr_uid = uid;
4251 temp_pcred->cr_ruid = uid;
4252 temp_pcred->cr_svuid = uid;
4253 temp_pcred->cr_flags = pcred->cr_flags;
2d21ac55 4254 /* inherit the opt-out of memberd */
6d2010ae
A
4255 if (pcred->cr_flags & CRF_NOMEMBERD) {
4256 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4257 temp_pcred->cr_flags |= CRF_NOMEMBERD;
2d21ac55 4258 } else {
6d2010ae
A
4259 temp_pcred->cr_gmuid = uid;
4260 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
2d21ac55 4261 }
6d2010ae 4262 temp_pcred->cr_ngroups = 1;
2d21ac55
A
4263 /* displacing a supplementary group opts us out of memberd */
4264 if (kauth_cred_change_egid(&temp_cred, gid)) {
6d2010ae
A
4265 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4266 temp_pcred->cr_flags |= CRF_NOMEMBERD;
2d21ac55 4267 }
6d2010ae
A
4268 temp_pcred->cr_rgid = gid;
4269 temp_pcred->cr_svgid = gid;
2d21ac55
A
4270#if CONFIG_MACF
4271 temp_cred.cr_label = cred->cr_label;
4272#endif
91447636
A
4273
4274 return(kauth_cred_update(cred, &temp_cred, TRUE));
4275}
4276
2d21ac55 4277
91447636 4278/*
2d21ac55
A
4279 * kauth_cred_setsvuidgid
4280 *
4281 * Description: Function used by execve to set the saved uid and gid values
4282 * for suid/sgid programs
4283 *
4284 * Parameters: cred The credential to update
4285 * uid The saved uid to set
4286 * gid The saved gid to set
4287 *
4288 * Returns: (kauth_cred_t) The updated credential
4289 *
4290 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4291 * if it returns a credential other than the one it is passed,
4292 * will have dropped the reference on the passed credential. All
4293 * callers should be aware of this, and treat this function as an
4294 * unref + ref, potentially on different credentials.
4295 *
4296 * Because of this, the caller is expected to take its own
4297 * reference on the credential passed as the first parameter,
4298 * and be prepared to release the reference on the credential
4299 * that is returned to them, if it is not intended to be a
4300 * persistent reference.
91447636
A
4301 */
4302kauth_cred_t
4303kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
4304{
4305 struct ucred temp_cred;
6d2010ae
A
4306 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4307 posix_cred_t pcred;
91447636
A
4308
4309 NULLCRED_CHECK(cred);
6d2010ae
A
4310
4311 pcred = posix_cred_get(cred);
4312
2d21ac55 4313 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
91447636 4314
2d21ac55
A
4315 /*
4316 * We don't need to do anything if the effective, real and saved
4317 * uids are already the same as the uid provided. This check is
4318 * likely insufficient.
91447636 4319 */
6d2010ae 4320 if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) {
91447636
A
4321 /* no change needed */
4322 return(cred);
4323 }
2d21ac55 4324 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
91447636
A
4325
4326 /* look up in cred hash table to see if we have a matching credential
4327 * with new values.
4328 */
4329 bcopy(cred, &temp_cred, sizeof(temp_cred));
6d2010ae
A
4330 temp_pcred->cr_svuid = uid;
4331 temp_pcred->cr_svgid = gid;
91447636
A
4332
4333 return(kauth_cred_update(cred, &temp_cred, TRUE));
4334}
4335
2d21ac55 4336
91447636 4337/*
2d21ac55
A
4338 * kauth_cred_setauditinfo
4339 *
b0d623f7 4340 * Description: Update the given credential using the given au_session_t.
2d21ac55
A
4341 *
4342 * Parameters: cred The original credential
4343 * auditinfo_p Pointer to ne audit information
4344 *
4345 * Returns: (kauth_cred_t) The updated credential
4346 *
4347 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4348 * if it returns a credential other than the one it is passed,
4349 * will have dropped the reference on the passed credential. All
4350 * callers should be aware of this, and treat this function as an
4351 * unref + ref, potentially on different credentials.
4352 *
4353 * Because of this, the caller is expected to take its own
4354 * reference on the credential passed as the first parameter,
4355 * and be prepared to release the reference on the credential
4356 * that is returned to them, if it is not intended to be a
4357 * persistent reference.
91447636
A
4358 */
4359kauth_cred_t
b0d623f7 4360kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
91447636
A
4361{
4362 struct ucred temp_cred;
4363
4364 NULLCRED_CHECK(cred);
4365
2d21ac55
A
4366 /*
4367 * We don't need to do anything if the audit info is already the
4368 * same as the audit info in the credential provided.
91447636 4369 */
b0d623f7 4370 if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) {
91447636
A
4371 /* no change needed */
4372 return(cred);
4373 }
4374
91447636 4375 bcopy(cred, &temp_cred, sizeof(temp_cred));
b0d623f7 4376 bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
91447636
A
4377
4378 return(kauth_cred_update(cred, &temp_cred, FALSE));
4379}
4380
2d21ac55 4381#if CONFIG_MACF
91447636 4382/*
2d21ac55
A
4383 * kauth_cred_label_update
4384 *
4385 * Description: Update the MAC label associated with a credential
4386 *
4387 * Parameters: cred The original credential
4388 * label The MAC label to set
4389 *
4390 * Returns: (kauth_cred_t) The updated credential
4391 *
4392 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4393 * if it returns a credential other than the one it is passed,
4394 * will have dropped the reference on the passed credential. All
4395 * callers should be aware of this, and treat this function as an
4396 * unref + ref, potentially on different credentials.
4397 *
4398 * Because of this, the caller is expected to take its own
4399 * reference on the credential passed as the first parameter,
4400 * and be prepared to release the reference on the credential
4401 * that is returned to them, if it is not intended to be a
4402 * persistent reference.
4403 */
4404kauth_cred_t
4405kauth_cred_label_update(kauth_cred_t cred, struct label *label)
4406{
4407 kauth_cred_t newcred;
4408 struct ucred temp_cred;
4409
4410 bcopy(cred, &temp_cred, sizeof(temp_cred));
4411
4412 mac_cred_label_init(&temp_cred);
4413 mac_cred_label_associate(cred, &temp_cred);
4414 mac_cred_label_update(&temp_cred, label);
4415
4416 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4417 mac_cred_label_destroy(&temp_cred);
4418 return (newcred);
4419}
4420
4421/*
4422 * kauth_cred_label_update_execve
4423 *
4424 * Description: Update the MAC label associated with a credential as
4425 * part of exec
4426 *
4427 * Parameters: cred The original credential
4428 * vp The exec vnode
4429 * scriptl The script MAC label
4430 * execl The executable MAC label
c910b4d9
A
4431 * disjointp Pointer to flag to set if old
4432 * and returned credentials are
4433 * disjoint
2d21ac55
A
4434 *
4435 * Returns: (kauth_cred_t) The updated credential
4436 *
c910b4d9
A
4437 * Implicit returns:
4438 * *disjointp Set to 1 for disjoint creds
4439 *
2d21ac55
A
4440 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4441 * if it returns a credential other than the one it is passed,
4442 * will have dropped the reference on the passed credential. All
4443 * callers should be aware of this, and treat this function as an
4444 * unref + ref, potentially on different credentials.
4445 *
4446 * Because of this, the caller is expected to take its own
4447 * reference on the credential passed as the first parameter,
4448 * and be prepared to release the reference on the credential
4449 * that is returned to them, if it is not intended to be a
4450 * persistent reference.
4451 */
fe8ab488 4452
2d21ac55
A
4453static
4454kauth_cred_t
4455kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
fe8ab488
A
4456 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4457 struct label *execl, unsigned int *csflags, void *macextensions, int *disjointp, int *labelupdateerror)
2d21ac55
A
4458{
4459 kauth_cred_t newcred;
4460 struct ucred temp_cred;
4461
4462 bcopy(cred, &temp_cred, sizeof(temp_cred));
4463
4464 mac_cred_label_init(&temp_cred);
4465 mac_cred_label_associate(cred, &temp_cred);
fe8ab488
A
4466 mac_cred_label_update_execve(ctx, &temp_cred,
4467 vp, offset, scriptvp, scriptl, execl, csflags,
4468 macextensions, disjointp, labelupdateerror);
2d21ac55
A
4469
4470 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4471 mac_cred_label_destroy(&temp_cred);
4472 return (newcred);
4473}
4474
4475/*
4476 * kauth_proc_label_update
4477 *
4478 * Description: Update the label inside the credential associated with the process.
4479 *
4480 * Parameters: p The process to modify
4481 * label The label to place in the process credential
4482 *
4483 * Notes: The credential associated with the process may change as a result
4484 * of this call. The caller should not assume the process reference to
4485 * the old credential still exists.
4486 */
4487int kauth_proc_label_update(struct proc *p, struct label *label)
4488{
4489 kauth_cred_t my_cred, my_new_cred;
4490
4491 my_cred = kauth_cred_proc_ref(p);
4492
4493 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
4494
4495 /* get current credential and take a reference while we muck with it */
4496 for (;;) {
4497
4498 /*
4499 * Set the credential with new info. If there is no change,
4500 * we get back the same credential we passed in; if there is
4501 * a change, we drop the reference on the credential we
4502 * passed in. The subsequent compare is safe, because it is
4503 * a pointer compare rather than a contents compare.
4504 */
4505 my_new_cred = kauth_cred_label_update(my_cred, label);
4506 if (my_cred != my_new_cred) {
4507
4508 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);
4509
4bd07ac2 4510 proc_ucred_lock(p);
2d21ac55
A
4511 /*
4512 * We need to protect for a race where another thread
4513 * also changed the credential after we took our
4514 * reference. If p_ucred has changed then we should
4515 * restart this again with the new cred.
4516 */
4517 if (p->p_ucred != my_cred) {
4bd07ac2 4518 proc_ucred_unlock(p);
2d21ac55
A
4519 kauth_cred_unref(&my_new_cred);
4520 my_cred = kauth_cred_proc_ref(p);
4521 /* try again */
4522 continue;
4523 }
4524 p->p_ucred = my_new_cred;
6d2010ae
A
4525 /* update cred on proc */
4526 PROC_UPDATE_CREDS_ONPROC(p);
4527
2d21ac55 4528 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4bd07ac2 4529 proc_ucred_unlock(p);
2d21ac55
A
4530 }
4531 break;
4532 }
4533 /* Drop old proc reference or our extra reference */
4534 kauth_cred_unref(&my_cred);
4535
4536 return (0);
4537}
4538
4539/*
4540 * kauth_proc_label_update_execve
4541 *
4542 * Description: Update the label inside the credential associated with the
4543 * process as part of a transitioning execve. The label will
4544 * be updated by the policies as part of this processing, not
4545 * provided up front.
4546 *
4547 * Parameters: p The process to modify
4548 * ctx The context of the exec
4549 * vp The vnode being exec'ed
4550 * scriptl The script MAC label
4551 * execl The executable MAC label
fe8ab488
A
4552 * lupdateerror The error place holder for MAC label authority
4553 * to update about possible termination
2d21ac55 4554 *
c910b4d9
A
4555 * Returns: 0 Label update did not make credential
4556 * disjoint
4557 * 1 Label update caused credential to be
4558 * disjoint
4559 *
2d21ac55
A
4560 * Notes: The credential associated with the process WILL change as a
4561 * result of this call. The caller should not assume the process
4562 * reference to the old credential still exists.
4563 */
fe8ab488
A
4564
4565void
c910b4d9 4566kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
fe8ab488
A
4567 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl,
4568 struct label *execl, unsigned int *csflags, void *macextensions, int *disjoint, int *update_return)
2d21ac55
A
4569{
4570 kauth_cred_t my_cred, my_new_cred;
2d21ac55
A
4571 my_cred = kauth_cred_proc_ref(p);
4572
4573 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
4574
4575 /* get current credential and take a reference while we muck with it */
4576 for (;;) {
4577
4578 /*
4579 * Set the credential with new info. If there is no change,
4580 * we get back the same credential we passed in; if there is
4581 * a change, we drop the reference on the credential we
4582 * passed in. The subsequent compare is safe, because it is
4583 * a pointer compare rather than a contents compare.
4584 */
fe8ab488 4585 my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, offset, scriptvp, scriptl, execl, csflags, macextensions, disjoint, update_return);
2d21ac55
A
4586 if (my_cred != my_new_cred) {
4587
4588 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);
4589
4bd07ac2 4590 proc_ucred_lock(p);
2d21ac55
A
4591 /*
4592 * We need to protect for a race where another thread
4593 * also changed the credential after we took our
4594 * reference. If p_ucred has changed then we should
4595 * restart this again with the new cred.
4596 */
4597 if (p->p_ucred != my_cred) {
4bd07ac2 4598 proc_ucred_unlock(p);
2d21ac55
A
4599 kauth_cred_unref(&my_new_cred);
4600 my_cred = kauth_cred_proc_ref(p);
4601 /* try again */
4602 continue;
4603 }
4604 p->p_ucred = my_new_cred;
6d2010ae
A
4605 /* update cred on proc */
4606 PROC_UPDATE_CREDS_ONPROC(p);
2d21ac55 4607 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4bd07ac2 4608 proc_ucred_unlock(p);
2d21ac55
A
4609 }
4610 break;
4611 }
4612 /* Drop old proc reference or our extra reference */
4613 kauth_cred_unref(&my_cred);
2d21ac55
A
4614}
4615
4616#if 1
4617/*
4618 * for temporary binary compatibility
4619 */
4620kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
4621kauth_cred_t
4622kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
4623{
4624 return kauth_cred_label_update(cred, label);
4625}
4626
4627int kauth_proc_setlabel(struct proc *p, struct label *label);
4628int
4629kauth_proc_setlabel(struct proc *p, struct label *label)
4630{
4631 return kauth_proc_label_update(p, label);
4632}
4633#endif
4634
4635#else
4636
4637/* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4638 * Since we cannot build our export lists based on the kernel configuration we need
4639 * to define a stub.
4640 */
4641kauth_cred_t
4642kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
4643{
4644 return(NULL);
4645}
4646
4647int
4648kauth_proc_label_update(__unused struct proc *p, __unused void *label)
4649{
4650 return (0);
4651}
4652
4653#if 1
4654/*
4655 * for temporary binary compatibility
4656 */
4657kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
4658kauth_cred_t
4659kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
4660{
4661 return NULL;
4662}
4663
4664int kauth_proc_setlabel(struct proc *p, void *label);
4665int
4666kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
4667{
4668 return (0);
4669}
4670#endif
4671#endif
4672
4673/*
4674 * kauth_cred_ref
4675 *
4676 * Description: Add a reference to the passed credential
4677 *
4678 * Parameters: cred The credential to reference
4679 *
4680 * Returns: (void)
4681 *
4682 * Notes: This function adds a reference to the provided credential;
4683 * the existing reference on the credential is assumed to be
4684 * held stable over this operation by taking the appropriate
4685 * lock to protect the pointer from which it is being referenced,
4686 * if necessary (e.g. the proc lock is held over the call if the
4687 * credential being referenced is from p_ucred, the vnode lock
4688 * if from the per vnode name cache cred cache, and so on).
4689 *
4690 * This is safe from the kauth_cred_unref() path, since an atomic
4691 * add is used, and the unref path specifically checks to see that
4692 * the value has not been changed to add a reference between the
4693 * time the credential is unreferenced by another pointer and the
4694 * time it is unreferenced from the cred hash cache.
91447636
A
4695 */
4696void
4697kauth_cred_ref(kauth_cred_t cred)
4698{
4699 int old_value;
4700
4701 NULLCRED_CHECK(cred);
4702
b0d623f7 4703 old_value = OSAddAtomicLong(1, (long*)&cred->cr_ref);
91447636
A
4704
4705 if (old_value < 1)
4706 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
2d21ac55
A
4707
4708#if 0 // use this to watch a specific credential
4709 if ( is_target_cred( cred ) != 0 ) {
4710 get_backtrace( );
4711 }
4712#endif
91447636
A
4713
4714 return;
4715}
4716
21362eb3 4717
0c530ab8 4718/*
2d21ac55
A
4719 * kauth_cred_unref_hashlocked
4720 *
4721 * Description: release a credential reference; when the last reference is
4722 * released, the credential will be freed.
4723 *
4724 * Parameters: credp Pointer to address containing
4725 * credential to be freed
4726 *
39236c6e
A
4727 * Returns: TRUE if the credential must be destroyed by the caller.
4728 * FALSE otherwise.
0c530ab8 4729 *
2d21ac55
A
4730 * Implicit returns:
4731 * *credp Set to NOCRED
4732 *
4733 * Notes: This function assumes the credential hash lock is held.
4734 *
4735 * This function is internal use only, since the hash lock is
4736 * scoped to this compilation unit.
4737 *
4738 * This function destroys the contents of the pointer passed by
316670eb 4739 * the caller to prevent the caller accidentally attempting to
2d21ac55
A
4740 * release a given reference twice in error.
4741 *
4742 * The last reference is considered to be released when a release
4743 * of a credential of a reference count of 2 occurs; this is an
316670eb 4744 * intended effect, to take into account the reference held by
2d21ac55 4745 * the credential hash, which is released at the same time.
0c530ab8 4746 */
39236c6e 4747static boolean_t
0c530ab8
A
4748kauth_cred_unref_hashlocked(kauth_cred_t *credp)
4749{
4750 int old_value;
39236c6e 4751 boolean_t destroy_it = FALSE;
6601e61a 4752
0c530ab8
A
4753 KAUTH_CRED_HASH_LOCK_ASSERT();
4754 NULLCRED_CHECK(*credp);
2d21ac55 4755
b0d623f7 4756 old_value = OSAddAtomicLong(-1, (long*)&(*credp)->cr_ref);
91447636
A
4757
4758#if DIAGNOSTIC
4759 if (old_value == 0)
2d21ac55 4760 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
0c530ab8 4761 if (old_value == 1)
2d21ac55
A
4762 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
4763#endif
4764
4765#if 0 // use this to watch a specific credential
4766 if ( is_target_cred( *credp ) != 0 ) {
4767 get_backtrace( );
4768 }
91447636
A
4769#endif
4770
2d21ac55
A
4771 /*
4772 * If the old_value is 2, then we have just released the last external
4773 * reference to this credential
4774 */
91447636 4775 if (old_value < 3) {
2d21ac55 4776 /* The last absolute reference is our credential hash table */
39236c6e
A
4777 destroy_it = kauth_cred_remove(*credp);
4778 }
4779
4780 if (destroy_it == FALSE) {
4781 *credp = NOCRED;
91447636 4782 }
39236c6e
A
4783
4784 return (destroy_it);
0c530ab8
A
4785}
4786
2d21ac55
A
4787
4788/*
4789 * kauth_cred_unref
4790 *
4791 * Description: Release a credential reference while holding the credential
4792 * hash lock; when the last reference is released, the credential
4793 * will be freed.
4794 *
4795 * Parameters: credp Pointer to address containing
4796 * credential to be freed
4797 *
4798 * Returns: (void)
4799 *
4800 * Implicit returns:
4801 * *credp Set to NOCRED
4802 *
4803 * Notes: See kauth_cred_unref_hashlocked() for more information.
4804 *
4805 */
4806void
4807kauth_cred_unref(kauth_cred_t *credp)
4808{
39236c6e
A
4809 boolean_t destroy_it;
4810
2d21ac55 4811 KAUTH_CRED_HASH_LOCK();
39236c6e 4812 destroy_it = kauth_cred_unref_hashlocked(credp);
2d21ac55 4813 KAUTH_CRED_HASH_UNLOCK();
39236c6e
A
4814
4815 if (destroy_it == TRUE) {
4816 assert(*credp != NOCRED);
4817#if CONFIG_MACF
4818 mac_cred_label_destroy(*credp);
4819#endif
4820 AUDIT_SESSION_UNREF(*credp);
4821
4822 (*credp)->cr_ref = 0;
4823 FREE_ZONE(*credp, sizeof(*(*credp)), M_CRED);
4824 *credp = NOCRED;
4825 }
2d21ac55
A
4826}
4827
4828
b0d623f7 4829#ifndef __LP64__
2d21ac55
A
4830/*
4831 * kauth_cred_rele
4832 *
4833 * Description: release a credential reference; when the last reference is
4834 * released, the credential will be freed
4835 *
4836 * Parameters: cred Credential to release
4837 *
4838 * Returns: (void)
4839 *
4840 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4841 * clear the pointer in the caller to avoid multiple releases of
4842 * the same credential. The currently recommended interface is
4843 * kauth_cred_unref().
4844 */
0c530ab8
A
4845void
4846kauth_cred_rele(kauth_cred_t cred)
4847{
4848 kauth_cred_unref(&cred);
91447636 4849}
b0d623f7 4850#endif /* !__LP64__ */
91447636 4851
2d21ac55 4852
91447636 4853/*
2d21ac55
A
4854 * kauth_cred_dup
4855 *
4856 * Description: Duplicate a credential via alloc and copy; the new credential
4857 * has only it's own
4858 *
4859 * Parameters: cred The credential to duplicate
4860 *
4861 * Returns: (kauth_cred_t) The duplicate credential
4862 *
4863 * Notes: The typical value to calling this routine is if you are going
4864 * to modify an existing credential, and expect to need a new one
4865 * from the hash cache.
4866 *
4867 * This should probably not be used in the majority of cases;
4868 * if you are using it instead of kauth_cred_create(), you are
4869 * likely making a mistake.
4870 *
4871 * The newly allocated credential is copied as part of the
4872 * allocation process, with the exception of the reference
4873 * count, which is set to 1 to indicate a single reference
4874 * held by the caller.
4875 *
4876 * Since newly allocated credentials have no external pointers
4877 * referencing them, prior to making them visible in an externally
4878 * visible pointer (e.g. by adding them to the credential hash
4879 * cache) is the only legal time in which an existing credential
316670eb 4880 * can be safely initialized or modified directly.
2d21ac55
A
4881 *
4882 * After initialization, the caller is expected to call the
4883 * function kauth_cred_add() to add the credential to the hash
316670eb 4884 * cache, after which time it's frozen and becomes publicly
2d21ac55
A
4885 * visible.
4886 *
4887 * The release protocol depends on kauth_hash_add() being called
4888 * before kauth_cred_rele() (there is a diagnostic panic which
4889 * will trigger if this protocol is not observed).
4890 *
91447636
A
4891 */
4892kauth_cred_t
4893kauth_cred_dup(kauth_cred_t cred)
4894{
4895 kauth_cred_t newcred;
2d21ac55
A
4896#if CONFIG_MACF
4897 struct label *temp_label;
4898#endif
91447636
A
4899
4900#if CRED_DIAGNOSTIC
4901 if (cred == NOCRED || cred == FSCRED)
4902 panic("kauth_cred_dup: bad credential");
4903#endif
4904 newcred = kauth_cred_alloc();
4905 if (newcred != NULL) {
2d21ac55
A
4906#if CONFIG_MACF
4907 temp_label = newcred->cr_label;
4908#endif
91447636 4909 bcopy(cred, newcred, sizeof(*newcred));
2d21ac55
A
4910#if CONFIG_MACF
4911 newcred->cr_label = temp_label;
4912 mac_cred_label_associate(cred, newcred);
4913#endif
b0d623f7 4914 AUDIT_SESSION_REF(cred);
91447636
A
4915 newcred->cr_ref = 1;
4916 }
4917 return(newcred);
4918}
4919
4920/*
2d21ac55
A
4921 * kauth_cred_copy_real
4922 *
4923 * Description: Returns a credential based on the passed credential but which
4924 * reflects the real rather than effective UID and GID.
4925 *
4926 * Parameters: cred The credential from which to
4927 * derive the new credential
4928 *
4929 * Returns: (kauth_cred_t) The copied credential
4930 *
4931 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4932 * result, the caller is responsible for dropping BOTH the
4933 * additional reference on the passed cred (if any), and the
4934 * credential returned by this function. The drop should be
316670eb 4935 * via the kauth_cred_unref() KPI.
91447636
A
4936 */
4937kauth_cred_t
4938kauth_cred_copy_real(kauth_cred_t cred)
4939{
4940 kauth_cred_t newcred = NULL, found_cred;
4941 struct ucred temp_cred;
6d2010ae
A
4942 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4943 posix_cred_t pcred = posix_cred_get(cred);
91447636
A
4944
4945 /* if the credential is already 'real', just take a reference */
6d2010ae
A
4946 if ((pcred->cr_ruid == pcred->cr_uid) &&
4947 (pcred->cr_rgid == pcred->cr_gid)) {
91447636
A
4948 kauth_cred_ref(cred);
4949 return(cred);
4950 }
4951
2d21ac55
A
4952 /*
4953 * Look up in cred hash table to see if we have a matching credential
4954 * with the new values.
91447636
A
4955 */
4956 bcopy(cred, &temp_cred, sizeof(temp_cred));
6d2010ae 4957 temp_pcred->cr_uid = pcred->cr_ruid;
2d21ac55 4958 /* displacing a supplementary group opts us out of memberd */
6d2010ae
A
4959 if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) {
4960 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4961 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
2d21ac55 4962 }
0c530ab8 4963 /*
2d21ac55 4964 * If the cred is not opted out, make sure we are using the r/euid
0c530ab8
A
4965 * for group checks
4966 */
6d2010ae
A
4967 if (temp_pcred->cr_gmuid != KAUTH_UID_NONE)
4968 temp_pcred->cr_gmuid = pcred->cr_ruid;
91447636
A
4969
4970 for (;;) {
4971 int err;
4972
4973 KAUTH_CRED_HASH_LOCK();
4974 found_cred = kauth_cred_find(&temp_cred);
4975 if (found_cred == cred) {
4976 /* same cred so just bail */
4977 KAUTH_CRED_HASH_UNLOCK();
4978 return(cred);
4979 }
4980 if (found_cred != NULL) {
0c530ab8 4981 /*
2d21ac55
A
4982 * Found a match so we bump reference count on new
4983 * one. We leave the old one alone.
91447636
A
4984 */
4985 kauth_cred_ref(found_cred);
4986 KAUTH_CRED_HASH_UNLOCK();
4987 return(found_cred);
4988 }
4989
2d21ac55
A
4990 /*
4991 * Must allocate a new credential, copy in old credential
4992 * data and update the real user and group IDs.
91447636
A
4993 */
4994 newcred = kauth_cred_dup(&temp_cred);
4995 err = kauth_cred_add(newcred);
4996 KAUTH_CRED_HASH_UNLOCK();
4997
2d21ac55 4998 /* Retry if kauth_cred_add() fails */
91447636
A
4999 if (err == 0)
5000 break;
2d21ac55
A
5001#if CONFIG_MACF
5002 mac_cred_label_destroy(newcred);
5003#endif
b0d623f7
A
5004 AUDIT_SESSION_UNREF(newcred);
5005
2d21ac55 5006 FREE_ZONE(newcred, sizeof(*newcred), M_CRED);
91447636
A
5007 newcred = NULL;
5008 }
5009
5010 return(newcred);
5011}
2d21ac55
A
5012
5013
91447636 5014/*
2d21ac55 5015 * kauth_cred_update
0c530ab8 5016 *
2d21ac55
A
5017 * Description: Common code to update a credential
5018 *
5019 * Parameters: old_cred Reference counted credential
5020 * to update
5021 * model_cred Non-reference counted model
5022 * credential to apply to the
5023 * credential to be updated
5024 * retain_auditinfo Flag as to whether or not the
5025 * audit information should be
5026 * copied from the old_cred into
5027 * the model_cred
5028 *
5029 * Returns: (kauth_cred_t) The updated credential
5030 *
5031 * IMPORTANT: This function will potentially return a credential other than
5032 * the one it is passed, and if so, it will have dropped the
5033 * reference on the passed credential. All callers should be
5034 * aware of this, and treat this function as an unref + ref,
5035 * potentially on different credentials.
5036 *
5037 * Because of this, the caller is expected to take its own
5038 * reference on the credential passed as the first parameter,
5039 * and be prepared to release the reference on the credential
5040 * that is returned to them, if it is not intended to be a
5041 * persistent reference.
91447636 5042 */
0c530ab8 5043static kauth_cred_t
2d21ac55
A
5044kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
5045 boolean_t retain_auditinfo)
91447636
A
5046{
5047 kauth_cred_t found_cred, new_cred = NULL;
5048
2d21ac55
A
5049 /*
5050 * Make sure we carry the auditinfo forward to the new credential
5051 * unless we are actually updating the auditinfo.
91447636 5052 */
b0d623f7
A
5053 if (retain_auditinfo) {
5054 bcopy(&old_cred->cr_audit, &model_cred->cr_audit,
5055 sizeof(model_cred->cr_audit));
b0d623f7 5056 }
91447636
A
5057
5058 for (;;) {
5059 int err;
5060
5061 KAUTH_CRED_HASH_LOCK();
5062 found_cred = kauth_cred_find(model_cred);
5063 if (found_cred == old_cred) {
5064 /* same cred so just bail */
5065 KAUTH_CRED_HASH_UNLOCK();
5066 return(old_cred);
5067 }
5068 if (found_cred != NULL) {
39236c6e
A
5069 boolean_t destroy_it;
5070
2d21ac55 5071 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
0c530ab8 5072 /*
2d21ac55 5073 * Found a match so we bump reference count on new
0c530ab8 5074 * one and decrement reference count on the old one.
91447636
A
5075 */
5076 kauth_cred_ref(found_cred);
39236c6e 5077 destroy_it = kauth_cred_unref_hashlocked(&old_cred);
91447636 5078 KAUTH_CRED_HASH_UNLOCK();
39236c6e
A
5079 if (destroy_it == TRUE) {
5080 assert(old_cred != NOCRED);
5081#if CONFIG_MACF
5082 mac_cred_label_destroy(old_cred);
5083#endif
5084 AUDIT_SESSION_UNREF(old_cred);
5085
5086 old_cred->cr_ref = 0;
5087 FREE_ZONE(old_cred, sizeof(*old_cred), M_CRED);
5088 old_cred = NOCRED;
5089
5090 }
91447636
A
5091 return(found_cred);
5092 }
39236c6e 5093
2d21ac55
A
5094 /*
5095 * Must allocate a new credential using the model. also
91447636
A
5096 * adds the new credential to the credential hash table.
5097 */
5098 new_cred = kauth_cred_dup(model_cred);
5099 err = kauth_cred_add(new_cred);
5100 KAUTH_CRED_HASH_UNLOCK();
5101
5102 /* retry if kauth_cred_add returns non zero value */
5103 if (err == 0)
5104 break;
2d21ac55
A
5105#if CONFIG_MACF
5106 mac_cred_label_destroy(new_cred);
5107#endif
b0d623f7
A
5108 AUDIT_SESSION_UNREF(new_cred);
5109
2d21ac55 5110 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
91447636
A
5111 new_cred = NULL;
5112 }
5113
2d21ac55 5114 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred);
0c530ab8 5115 kauth_cred_unref(&old_cred);
91447636
A
5116 return(new_cred);
5117}
5118
2d21ac55
A
5119
5120/*
5121 * kauth_cred_add
5122 *
5123 * Description: Add the given credential to our credential hash table and
5124 * take an additional reference to account for our use of the
5125 * credential in the hash table
5126 *
5127 * Parameters: new_cred Credential to insert into cred
5128 * hash cache
5129 *
5130 * Returns: 0 Success
5131 * -1 Hash insertion failed: caller
5132 * should retry
5133 *
5134 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5135 *
5136 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
91447636 5137 */
0c530ab8
A
5138static int
5139kauth_cred_add(kauth_cred_t new_cred)
91447636
A
5140{
5141 u_long hash_key;
0c530ab8
A
5142
5143 KAUTH_CRED_HASH_LOCK_ASSERT();
5144
91447636 5145 hash_key = kauth_cred_get_hashkey(new_cred);
3e170ce0 5146 hash_key %= KAUTH_CRED_TABLE_SIZE;
91447636
A
5147
5148 /* race fix - there is a window where another matching credential
5149 * could have been inserted between the time this one was created and we
5150 * got the hash lock. If we find a match return an error and have the
5151 * the caller retry.
5152 */
5153 if (kauth_cred_find(new_cred) != NULL) {
5154 return(-1);
5155 }
5156
5157 /* take a reference for our use in credential hash table */
5158 kauth_cred_ref(new_cred);
5159
5160 /* insert the credential into the hash table */
5161 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
5162
5163 return(0);
5164}
5165
0c530ab8 5166
91447636 5167/*
2d21ac55
A
5168 * kauth_cred_remove
5169 *
5170 * Description: Remove the given credential from our credential hash table
5171 *
5172 * Parameters: cred Credential to remove from cred
5173 * hash cache
5174 *
39236c6e 5175 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not.
2d21ac55
A
5176 *
5177 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
5178 *
5179 * Notes: The check for the reference increment after entry is generally
5180 * agree to be safe, since we use atomic operations, and the
5181 * following code occurs with the hash lock held; in theory, this
5182 * protects us from the 2->1 reference that gets us here.
91447636 5183 */
39236c6e 5184static boolean_t
0c530ab8 5185kauth_cred_remove(kauth_cred_t cred)
91447636
A
5186{
5187 u_long hash_key;
5188 kauth_cred_t found_cred;
5189
5190 hash_key = kauth_cred_get_hashkey(cred);
3e170ce0 5191 hash_key %= KAUTH_CRED_TABLE_SIZE;
91447636 5192
2d21ac55 5193 /* Avoid race */
91447636
A
5194 if (cred->cr_ref < 1)
5195 panic("cred reference underflow");
2d21ac55 5196 if (cred->cr_ref > 1)
39236c6e 5197 return (FALSE); /* someone else got a ref */
2d21ac55
A
5198
5199 /* Find cred in the credential hash table */
91447636
A
5200 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
5201 if (found_cred == cred) {
5202 /* found a match, remove it from the hash table */
5203 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
91447636
A
5204#if KAUTH_CRED_HASH_DEBUG
5205 kauth_cred_count--;
5206#endif
39236c6e 5207 return (TRUE);
91447636
A
5208 }
5209 }
5210
2d21ac55
A
5211 /* Did not find a match... this should not happen! XXX Make panic? */
5212 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
39236c6e 5213 return (FALSE);
91447636
A
5214}
5215
2d21ac55 5216
91447636 5217/*
2d21ac55
A
5218 * kauth_cred_find
5219 *
5220 * Description: Using the given credential data, look for a match in our
5221 * credential hash table
5222 *
5223 * Parameters: cred Credential to lookup in cred
5224 * hash cache
5225 *
5226 * Returns: NULL Not found
316670eb 5227 * !NULL Matching credential already in
2d21ac55
A
5228 * cred hash cache
5229 *
5230 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
91447636 5231 */
0c530ab8
A
5232kauth_cred_t
5233kauth_cred_find(kauth_cred_t cred)
91447636
A
5234{
5235 u_long hash_key;
5236 kauth_cred_t found_cred;
6d2010ae 5237 posix_cred_t pcred = posix_cred_get(cred);
2d21ac55 5238
0c530ab8
A
5239 KAUTH_CRED_HASH_LOCK_ASSERT();
5240
91447636
A
5241#if KAUTH_CRED_HASH_DEBUG
5242 static int test_count = 0;
5243
5244 test_count++;
5245 if ((test_count % 200) == 0) {
5246 kauth_cred_hash_print();
5247 }
5248#endif
5249
5250 hash_key = kauth_cred_get_hashkey(cred);
3e170ce0 5251 hash_key %= KAUTH_CRED_TABLE_SIZE;
91447636 5252
2d21ac55 5253 /* Find cred in the credential hash table */
91447636 5254 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
2d21ac55 5255 boolean_t match;
6d2010ae 5256 posix_cred_t found_pcred = posix_cred_get(found_cred);
2d21ac55
A
5257
5258 /*
5259 * don't worry about the label unless the flags in
5260 * either credential tell us to.
5261 */
316670eb
A
5262 match = (bcmp(found_pcred, pcred, sizeof (*pcred)) == 0) ? TRUE : FALSE;
5263 match = match && ((bcmp(&found_cred->cr_audit, &cred->cr_audit,
5264 sizeof(cred->cr_audit)) == 0) ? TRUE : FALSE);
39236c6e 5265#if CONFIG_MACF
316670eb
A
5266 if (((found_pcred->cr_flags & CRF_MAC_ENFORCE) != 0) ||
5267 ((pcred->cr_flags & CRF_MAC_ENFORCE) != 0)) {
5268 match = match && mac_cred_label_compare(found_cred->cr_label,
5269 cred->cr_label);
2d21ac55 5270 }
39236c6e 5271#endif
2d21ac55 5272 if (match) {
91447636
A
5273 /* found a match */
5274 return(found_cred);
5275 }
5276 }
2d21ac55
A
5277 /* No match found */
5278
91447636
A
5279 return(NULL);
5280}
5281
91447636
A
5282
5283/*
2d21ac55
A
5284 * kauth_cred_hash
5285 *
5286 * Description: Generates a hash key using data that makes up a credential;
5287 * based on ElfHash
5288 *
5289 * Parameters: datap Pointer to data to hash
5290 * data_len Count of bytes to hash
5291 * start_key Start key value
5292 *
5293 * Returns: (u_long) Returned hash key
91447636 5294 */
2d21ac55
A
5295static inline u_long
5296kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
91447636
A
5297{
5298 u_long hash_key = start_key;
5299 u_long temp;
5300
5301 while (data_len > 0) {
5302 hash_key = (hash_key << 4) + *datap++;
5303 temp = hash_key & 0xF0000000;
5304 if (temp) {
5305 hash_key ^= temp >> 24;
5306 }
5307 hash_key &= ~temp;
5308 data_len--;
5309 }
5310 return(hash_key);
5311}
5312
2d21ac55
A
5313
5314/*
5315 * kauth_cred_get_hashkey
5316 *
5317 * Description: Generate a hash key using data that makes up a credential;
5318 * based on ElfHash. We hash on the entire credential data,
5319 * not including the ref count or the TAILQ, which are mutable;
5320 * everything else isn't.
5321 *
2d21ac55
A
5322 * Parameters: cred Credential for which hash is
5323 * desired
5324 *
5325 * Returns: (u_long) Returned hash key
6d2010ae
A
5326 *
5327 * Notes: When actually moving the POSIX credential into a real label,
5328 * remember to update this hash computation.
2d21ac55
A
5329 */
5330static u_long
5331kauth_cred_get_hashkey(kauth_cred_t cred)
5332{
39236c6e 5333#if CONFIG_MACF
6d2010ae 5334 posix_cred_t pcred = posix_cred_get(cred);
39236c6e 5335#endif
2d21ac55 5336 u_long hash_key = 0;
6d2010ae 5337
316670eb
A
5338 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix,
5339 sizeof (struct posix_cred),
5340 hash_key);
5341 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_audit,
5342 sizeof(struct au_session),
5343 hash_key);
39236c6e 5344#if CONFIG_MACF
6d2010ae 5345 if (pcred->cr_flags & CRF_MAC_ENFORCE) {
316670eb
A
5346 hash_key = kauth_cred_hash((uint8_t *)cred->cr_label,
5347 sizeof (struct label),
6d2010ae
A
5348 hash_key);
5349 }
39236c6e 5350#endif
2d21ac55
A
5351 return(hash_key);
5352}
5353
5354
91447636 5355#if KAUTH_CRED_HASH_DEBUG
2d21ac55
A
5356/*
5357 * kauth_cred_hash_print
5358 *
5359 * Description: Print out cred hash cache table information for debugging
5360 * purposes, including the credential contents
5361 *
5362 * Parameters: (void)
5363 *
5364 * Returns: (void)
5365 *
5366 * Implicit returns: Results in console output
5367 */
5368static void
5369kauth_cred_hash_print(void)
91447636
A
5370{
5371 int i, j;
5372 kauth_cred_t found_cred;
5373
5374 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
5375 /* count slot hits, misses, collisions, and max depth */
3e170ce0 5376 for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
91447636
A
5377 printf("[%02d] ", i);
5378 j = 0;
5379 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5380 if (j > 0) {
5381 printf("---- ");
5382 }
5383 j++;
5384 kauth_cred_print(found_cred);
5385 printf("\n");
5386 }
5387 if (j == 0) {
5388 printf("NOCRED \n");
5389 }
5390 }
5391}
2d21ac55 5392#endif /* KAUTH_CRED_HASH_DEBUG */
91447636
A
5393
5394
2d21ac55
A
5395#if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
5396/*
5397 * kauth_cred_print
5398 *
5399 * Description: Print out an individual credential's contents for debugging
5400 * purposes
5401 *
5402 * Parameters: cred The credential to print out
5403 *
5404 * Returns: (void)
5405 *
5406 * Implicit returns: Results in console output
5407 */
5408void
5409kauth_cred_print(kauth_cred_t cred)
91447636
A
5410{
5411 int i;
2d21ac55
A
5412
5413 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
5414 printf("group count %d gids ", cred->cr_ngroups);
5415 for (i = 0; i < NGROUPS; i++) {
2d21ac55
A
5416 if (i == 0)
5417 printf("e");
91447636
A
5418 printf("%d ", cred->cr_groups[i]);
5419 }
2d21ac55 5420 printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid);
b0d623f7
A
5421 printf("auditinfo_addr %d %d %d %d %d %d\n",
5422 cred->cr_audit.s_aia_p->ai_auid,
5423 cred->cr_audit.as_mask.am_success,
5424 cred->cr_audit.as_mask.am_failure,
5425 cred->cr_audit.as_aia_p->ai_termid.at_port,
5426 cred->cr_audit.as_aia_p->ai_termid.at_addr[0],
5427 cred->cr_audit.as_aia_p->ai_asid);
2d21ac55
A
5428}
5429
5430int is_target_cred( kauth_cred_t the_cred )
5431{
5432 if ( the_cred->cr_uid != 0 )
5433 return( 0 );
5434 if ( the_cred->cr_ruid != 0 )
5435 return( 0 );
5436 if ( the_cred->cr_svuid != 0 )
5437 return( 0 );
5438 if ( the_cred->cr_ngroups != 11 )
5439 return( 0 );
5440 if ( the_cred->cr_groups[0] != 11 )
5441 return( 0 );
5442 if ( the_cred->cr_groups[1] != 81 )
5443 return( 0 );
5444 if ( the_cred->cr_groups[2] != 63947 )
5445 return( 0 );
5446 if ( the_cred->cr_groups[3] != 80288 )
5447 return( 0 );
5448 if ( the_cred->cr_groups[4] != 89006 )
5449 return( 0 );
5450 if ( the_cred->cr_groups[5] != 52173 )
5451 return( 0 );
5452 if ( the_cred->cr_groups[6] != 84524 )
5453 return( 0 );
5454 if ( the_cred->cr_groups[7] != 79 )
5455 return( 0 );
5456 if ( the_cred->cr_groups[8] != 80292 )
5457 return( 0 );
5458 if ( the_cred->cr_groups[9] != 80 )
5459 return( 0 );
5460 if ( the_cred->cr_groups[10] != 90824 )
5461 return( 0 );
5462 if ( the_cred->cr_rgid != 11 )
5463 return( 0 );
5464 if ( the_cred->cr_svgid != 11 )
5465 return( 0 );
5466 if ( the_cred->cr_gmuid != 3475 )
5467 return( 0 );
b0d623f7 5468 if ( the_cred->cr_audit.as_aia_p->ai_auid != 3475 )
2d21ac55
A
5469 return( 0 );
5470/*
b0d623f7 5471 if ( the_cred->cr_audit.as_mask.am_success != 0 )
2d21ac55 5472 return( 0 );
b0d623f7 5473 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
2d21ac55 5474 return( 0 );
b0d623f7 5475 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
2d21ac55 5476 return( 0 );
b0d623f7 5477 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
2d21ac55 5478 return( 0 );
b0d623f7 5479 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
2d21ac55
A
5480 return( 0 );
5481 if ( the_cred->cr_flags != 0 )
5482 return( 0 );
5483*/
5484 return( -1 ); // found target cred
5485}
5486
5487void get_backtrace( void )
5488{
5489 int my_slot;
5490 void * my_stack[ MAX_STACK_DEPTH ];
5491 int i, my_depth;
5492
5493 if ( cred_debug_buf_p == NULL ) {
5494 MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK);
5495 bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p));
5496 }
5497
5498 if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) {
5499 /* buffer is full */
5500 return;
5501 }
5502
5503 my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
5504 if ( my_depth == 0 ) {
5505 printf("%s - OSBacktrace failed \n", __FUNCTION__);
5506 return;
5507 }
5508
5509 /* fill new backtrace */
5510 my_slot = cred_debug_buf_p->next_slot;
5511 cred_debug_buf_p->next_slot++;
5512 cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
5513 for ( i = 0; i < my_depth; i++ ) {
5514 cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
5515 }
5516
5517 return;
5518}
5519
5520
5521/* subset of struct ucred for use in sysctl_dump_creds */
5522struct debug_ucred {
5523 void *credp;
5524 u_long cr_ref; /* reference count */
5525 uid_t cr_uid; /* effective user id */
5526 uid_t cr_ruid; /* real user id */
5527 uid_t cr_svuid; /* saved user id */
5528 short cr_ngroups; /* number of groups in advisory list */
5529 gid_t cr_groups[NGROUPS]; /* advisory group list */
5530 gid_t cr_rgid; /* real group id */
5531 gid_t cr_svgid; /* saved group id */
5532 uid_t cr_gmuid; /* UID for group membership purposes */
b0d623f7 5533 struct auditinfo_addr cr_audit; /* user auditing data. */
2d21ac55
A
5534 void *cr_label; /* MACF label */
5535 int cr_flags; /* flags on credential */
5536};
5537typedef struct debug_ucred debug_ucred;
5538
5539SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
5540 NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
5541
5542/* accessed by:
5543 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5544 */
5545
5546static int
5547sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5548{
5549 int i, j, counter = 0;
5550 int error;
5551 size_t space;
5552 kauth_cred_t found_cred;
5553 debug_ucred * cred_listp;
5554 debug_ucred * nextp;
5555
5556 /* This is a readonly node. */
5557 if (req->newptr != USER_ADDR_NULL)
5558 return (EPERM);
5559
5560 /* calculate space needed */
3e170ce0 5561 for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
2d21ac55
A
5562 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5563 counter++;
5564 }
5565 }
5566
5567 /* they are querying us so just return the space required. */
5568 if (req->oldptr == USER_ADDR_NULL) {
5569 counter += 10; // add in some padding;
5570 req->oldidx = counter * sizeof(debug_ucred);
5571 return 0;
5572 }
5573
5574 MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK );
5575 if ( cred_listp == NULL ) {
5576 return (ENOMEM);
5577 }
5578
5579 /* fill in creds to send back */
5580 nextp = cred_listp;
5581 space = 0;
3e170ce0 5582 for (i = 0; i < KAUTH_CRED_TABLE_SIZE; i++) {
2d21ac55
A
5583 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5584 nextp->credp = found_cred;
5585 nextp->cr_ref = found_cred->cr_ref;
5586 nextp->cr_uid = found_cred->cr_uid;
5587 nextp->cr_ruid = found_cred->cr_ruid;
5588 nextp->cr_svuid = found_cred->cr_svuid;
5589 nextp->cr_ngroups = found_cred->cr_ngroups;
5590 for ( j = 0; j < nextp->cr_ngroups; j++ ) {
5591 nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
5592 }
5593 nextp->cr_rgid = found_cred->cr_rgid;
5594 nextp->cr_svgid = found_cred->cr_svgid;
5595 nextp->cr_gmuid = found_cred->cr_gmuid;
b0d623f7
A
5596 nextp->cr_audit.ai_auid =
5597 found_cred->cr_audit.as_aia_p->ai_auid;
5598 nextp->cr_audit.ai_mask.am_success =
5599 found_cred->cr_audit.as_mask.am_success;
5600 nextp->cr_audit.ai_mask.am_failure =
5601 found_cred->cr_audit.as_mask.am_failure;
5602 nextp->cr_audit.ai_termid.at_port =
5603 found_cred->cr_audit.as_aia_p->ai_termid.at_port;
5604 nextp->cr_audit.ai_termid.at_type =
5605 found_cred->cr_audit.as_aia_p->ai_termid.at_type;
5606 nextp->cr_audit.ai_termid.at_addr[0] =
5607 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0];
5608 nextp->cr_audit.ai_termid.at_addr[1] =
5609 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1];
5610 nextp->cr_audit.ai_termid.at_addr[2] =
5611 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2];
5612 nextp->cr_audit.ai_termid.at_addr[3] =
5613 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3];
5614 nextp->cr_audit.ai_asid =
5615 found_cred->cr_audit.as_aia_p->ai_asid;
5616 nextp->cr_audit.ai_flags =
5617 found_cred->cr_audit.as_aia_p->ai_flags;
2d21ac55
A
5618 nextp->cr_label = found_cred->cr_label;
5619 nextp->cr_flags = found_cred->cr_flags;
5620 nextp++;
5621 space += sizeof(debug_ucred);
5622 if ( space > req->oldlen ) {
5623 FREE(cred_listp, M_TEMP);
5624 return (ENOMEM);
5625 }
5626 }
5627 }
5628 req->oldlen = space;
5629 error = SYSCTL_OUT(req, cred_listp, req->oldlen);
5630 FREE(cred_listp, M_TEMP);
5631 return (error);
5632}
5633
5634
5635SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
5636 NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
5637
5638/* accessed by:
5639 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5640 */
5641
5642static int
5643sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5644{
5645 int i, j;
5646 int error;
5647 size_t space;
5648 cred_debug_buffer * bt_bufp;
5649 cred_backtrace * nextp;
5650
5651 /* This is a readonly node. */
5652 if (req->newptr != USER_ADDR_NULL)
5653 return (EPERM);
5654
5655 if ( cred_debug_buf_p == NULL ) {
5656 return (EAGAIN);
5657 }
5658
5659 /* calculate space needed */
5660 space = sizeof( cred_debug_buf_p->next_slot );
5661 space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
5662
5663 /* they are querying us so just return the space required. */
5664 if (req->oldptr == USER_ADDR_NULL) {
5665 req->oldidx = space;
5666 return 0;
5667 }
5668
5669 if ( space > req->oldlen ) {
5670 return (ENOMEM);
5671 }
5672
5673 MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK );
5674 if ( bt_bufp == NULL ) {
5675 return (ENOMEM);
5676 }
5677
5678 /* fill in backtrace info to send back */
5679 bt_bufp->next_slot = cred_debug_buf_p->next_slot;
5680 space = sizeof(bt_bufp->next_slot);
91447636 5681
2d21ac55
A
5682 nextp = &bt_bufp->stack_buffer[ 0 ];
5683 for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
5684 nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
5685 for ( j = 0; j < nextp->depth; j++ ) {
5686 nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
5687 }
5688 space += sizeof(*nextp);
5689 nextp++;
5690 }
5691 req->oldlen = space;
5692 error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
5693 FREE(bt_bufp, M_TEMP);
5694 return (error);
91447636 5695}
2d21ac55
A
5696
5697#endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
6d2010ae
A
5698
5699
5700/*
5701 **********************************************************************
5702 * The following routines will be moved to a policy_posix.c module at
5703 * some future point.
5704 **********************************************************************
5705 */
5706
5707/*
5708 * posix_cred_create
5709 *
5710 * Description: Helper function to create a kauth_cred_t credential that is
5711 * initally labelled with a specific POSIX credential label
5712 *
5713 * Parameters: pcred The posix_cred_t to use as the initial
5714 * label value
5715 *
5716 * Returns: (kauth_cred_t) The credential that was found in the
5717 * hash or creates
5718 * NULL kauth_cred_add() failed, or there was
5719 * no egid specified, or we failed to
5720 * attach a label to the new credential
5721 *
5722 * Notes: This function currently wraps kauth_cred_create(), and is the
316670eb 5723 * only consumer of that ill-fated function, apart from bsd_init().
6d2010ae 5724 * It exists solely to support the NFS server code creation of
316670eb 5725 * credentials based on the over-the-wire RPC calls containing
6d2010ae
A
5726 * traditional POSIX credential information being tunneled to
5727 * the server host from the client machine.
5728 *
5729 * In the future, we hope this function goes away.
5730 *
5731 * In the short term, it creates a temporary credential, puts
5732 * the POSIX information from NFS into it, and then calls
316670eb 5733 * kauth_cred_create(), as an internal implementation detail.
6d2010ae
A
5734 *
5735 * If we have to keep it around in the medium term, it will
5736 * create a new kauth_cred_t, then label it with a POSIX label
5737 * corresponding to the contents of the kauth_cred_t. If the
5738 * policy_posix MACF module is not loaded, it will instead
5739 * substitute a posix_cred_t which GRANTS all access (effectively
5740 * a "root" credential) in order to not prevent NFS from working
5741 * in the case that we are not supporting POSIX credentials.
5742 */
5743kauth_cred_t
5744posix_cred_create(posix_cred_t pcred)
5745{
5746 struct ucred temp_cred;
5747
5748 bzero(&temp_cred, sizeof(temp_cred));
5749 temp_cred.cr_posix = *pcred;
5750
5751 return kauth_cred_create(&temp_cred);
5752}
5753
5754
5755/*
5756 * posix_cred_get
5757 *
5758 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5759 * any, which is associated with it.
5760 *
5761 * Parameters: cred The credential to obtain the label from
5762 *
5763 * Returns: posix_cred_t The POSIX credential label
5764 *
5765 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5766 * this function will return a pointer to a posix_cred_t which
5767 * GRANTS all access (effectively, a "root" credential). This is
5768 * necessary to support legacy code which insists on tightly
316670eb 5769 * integrating POSIX credentials into its APIs, including, but
6d2010ae
A
5770 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5771 * NFSv3, signals, dtrace, and a large number of kauth routines
5772 * used to implement POSIX permissions related system calls.
5773 *
5774 * In the event that the policy_posix MACF module IS loaded, and
5775 * there is no POSIX label on the kauth_cred_t credential, this
5776 * function will return a pointer to a posix_cred_t which DENIES
5777 * all access (effectively, a "deny rights granted by POSIX"
5778 * credential). This is necessary to support the concept of a
5779 * transiently loaded POSIX policy, or kauth_cred_t credentials
5780 * which can not be used in conjunctions with POSIX permissions
5781 * checks.
5782 *
5783 * This function currently returns the address of the cr_posix
5784 * field of the supplied kauth_cred_t credential, and as such
5785 * currently can not fail. In the future, this will not be the
5786 * case.
5787 */
5788posix_cred_t
5789posix_cred_get(kauth_cred_t cred)
5790{
5791 return(&cred->cr_posix);
5792}
5793
5794
5795/*
5796 * posix_cred_label
5797 *
5798 * Description: Label a kauth_cred_t with a POSIX credential label
5799 *
5800 * Parameters: cred The credential to label
5801 * pcred The POSIX credential t label it with
5802 *
5803 * Returns: (void)
5804 *
5805 * Notes: This function is currently void in order to permit it to fit
316670eb
A
5806 * in with the current MACF framework label methods which allow
5807 * labeling to fail silently. This is like acceptable for
6d2010ae
A
5808 * mandatory access controls, but not for POSIX, since those
5809 * access controls are advisory. We will need to consider a
5810 * return value in a future version of the MACF API.
5811 *
316670eb 5812 * This operation currently cannot fail, as currently the POSIX
6d2010ae
A
5813 * credential is a subfield of the kauth_cred_t (ucred), which
5814 * MUST be valid. In the future, this will not be the case.
5815 */
5816void
5817posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
5818{
5819 cred->cr_posix = *pcred; /* structure assign for now */
5820}
5821
5822
5823/*
5824 * posix_cred_access
5825 *
5826 * Description: Perform a POSIX access check for a protected object
5827 *
5828 * Parameters: cred The credential to check
5829 * object_uid The POSIX UID of the protected object
5830 * object_gid The POSIX GID of the protected object
5831 * object_mode The POSIX mode of the protected object
5832 * mode_req The requested POSIX access rights
5833 *
5834 * Returns 0 Access is granted
5835 * EACCES Access is denied
5836 *
5837 * Notes: This code optimizes the case where the world and group rights
5838 * would both grant the requested rights to avoid making a group
5839 * membership query. This is a big performance win in the case
5840 * where this is true.
5841 */
5842int
5843posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
5844{
5845 int is_member;
5846 mode_t mode_owner = (object_mode & S_IRWXU);
5847 mode_t mode_group = (object_mode & S_IRWXG) << 3;
5848 mode_t mode_world = (object_mode & S_IRWXO) << 6;
5849
5850 /*
5851 * Check first for owner rights
5852 */
5853 if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req)
5854 return (0);
5855
5856 /*
5857 * Combined group and world rights check, if we don't have owner rights
5858 *
5859 * OPTIMIZED: If group and world rights would grant the same bits, and
5860 * they set of requested bits is in both, then we can simply check the
5861 * world rights, avoiding a group membership check, which is expensive.
5862 */
5863 if ((mode_req & mode_group & mode_world) == mode_req) {
5864 return (0);
5865 } else {
5866 /*
5867 * NON-OPTIMIZED: requires group membership check.
5868 */
5869 if ((mode_req & mode_group) != mode_req) {
5870 /*
5871 * exclusion group : treat errors as "is a member"
5872 *
5873 * NON-OPTIMIZED: +group would deny; must check group
5874 */
5875 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5876 /*
5877 * DENY: +group denies
5878 */
5879 return (EACCES);
5880 } else {
5881 if ((mode_req & mode_world) != mode_req) {
5882 /*
5883 * DENY: both -group & world would deny
5884 */
5885 return (EACCES);
5886 } else {
5887 /*
5888 * ALLOW: allowed by -group and +world
5889 */
5890 return (0);
5891 }
5892 }
5893 } else {
5894 /*
5895 * inclusion group; treat errors as "not a member"
5896 *
5897 * NON-OPTIMIZED: +group allows, world denies; must
5898 * check group
5899 */
5900 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5901 /*
5902 * ALLOW: allowed by +group
5903 */
5904 return (0);
5905 } else {
5906 if ((mode_req & mode_world) != mode_req) {
5907 /*
5908 * DENY: both -group & world would deny
5909 */
5910 return (EACCES);
5911 } else {
5912 /*
5913 * ALLOW: allowed by -group and +world
5914 */
5915 return (0);
5916 }
5917 }
5918 }
5919 }
5920}