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