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