]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_credential.c
xnu-1504.15.3.tar.gz
[apple/xnu.git] / bsd / kern / kern_credential.c
1 /*
2 * Copyright (c) 2004-2007 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
52 #include <security/audit/audit.h>
53
54 #include <sys/mount.h>
55 #include <sys/sysproto.h>
56 #include <sys/kern_callout.h>
57 #include <mach/message.h>
58 #include <mach/host_security.h>
59
60 /* mach_absolute_time() */
61 #include <mach/clock_types.h>
62 #include <mach/mach_types.h>
63 #include <mach/mach_time.h>
64
65 #include <libkern/OSAtomic.h>
66
67 #include <kern/task.h>
68 #include <kern/lock.h>
69 #ifdef MACH_ASSERT
70 # undef MACH_ASSERT
71 #endif
72 #define MACH_ASSERT 1 /* XXX so bogus */
73 #include <kern/assert.h>
74
75 #if CONFIG_MACF
76 #include <security/mac.h>
77 #include <security/mac_framework.h>
78 #endif
79
80 void mach_kauth_cred_uthread_update( void );
81
82 #define CRED_DIAGNOSTIC 0
83
84 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0)
85
86 /*
87 * Credential debugging; we can track entry into a function that might
88 * change a credential, and we can track actual credential changes that
89 * result.
90 *
91 * Note: Does *NOT* currently include per-thread credential changes
92 */
93
94 #if DEBUG_CRED
95 #define DEBUG_CRED_ENTER printf
96 #define DEBUG_CRED_CHANGE printf
97 extern void kauth_cred_print(kauth_cred_t cred);
98
99 #include <libkern/OSDebug.h> /* needed for get_backtrace( ) */
100
101 int is_target_cred( kauth_cred_t the_cred );
102 void get_backtrace( void );
103
104 static int sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1,
105 __unused int arg2, struct sysctl_req *req );
106 static int
107 sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1,
108 __unused int arg2, struct sysctl_req *req );
109
110 #define MAX_STACK_DEPTH 8
111 struct cred_backtrace {
112 int depth;
113 void * stack[ MAX_STACK_DEPTH ];
114 };
115 typedef struct cred_backtrace cred_backtrace;
116
117 #define MAX_CRED_BUFFER_SLOTS 200
118 struct cred_debug_buffer {
119 int next_slot;
120 cred_backtrace stack_buffer[ MAX_CRED_BUFFER_SLOTS ];
121 };
122 typedef struct cred_debug_buffer cred_debug_buffer;
123 cred_debug_buffer * cred_debug_buf_p = NULL;
124
125 #else /* !DEBUG_CRED */
126
127 #define DEBUG_CRED_ENTER(fmt, ...) do {} while (0)
128 #define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0)
129
130 #endif /* !DEBUG_CRED */
131
132 /*
133 * Interface to external identity resolver.
134 *
135 * The architecture of the interface is simple; the external resolver calls
136 * in to get work, then calls back with completed work. It also calls us
137 * to let us know that it's (re)started, so that we can resubmit work if it
138 * times out.
139 */
140
141 static lck_mtx_t *kauth_resolver_mtx;
142 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
143 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
144
145 static volatile pid_t kauth_resolver_identity;
146 static int kauth_resolver_registered;
147 static uint32_t kauth_resolver_sequence;
148 static int kauth_resolver_timeout = 30; /* default: 30 seconds */
149
150 struct kauth_resolver_work {
151 TAILQ_ENTRY(kauth_resolver_work) kr_link;
152 struct kauth_identity_extlookup kr_work;
153 uint32_t kr_seqno;
154 uint64_t kr_subtime; /* submission time */
155 int kr_refs;
156 int kr_flags;
157 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
158 #define KAUTH_REQUEST_SUBMITTED (1<<1)
159 #define KAUTH_REQUEST_DONE (1<<2)
160 int kr_result;
161 };
162
163 TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
164 TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted;
165 TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done;
166
167 static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
168 static int kauth_resolver_complete(user_addr_t message);
169 static int kauth_resolver_getwork(user_addr_t message);
170 static int kauth_resolver_getwork2(user_addr_t message);
171
172 static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = KAUTH_CRED_PRIMES;
173 static int kauth_cred_primes_index = 0;
174 static int kauth_cred_table_size = 0;
175
176 TAILQ_HEAD(kauth_cred_entry_head, ucred);
177 static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
178
179 /* Weighted moving average for resolver response time */
180 static struct kco_moving_average resolver_ma;
181
182 #define KAUTH_CRED_HASH_DEBUG 0
183
184 static int kauth_cred_add(kauth_cred_t new_cred);
185 static void kauth_cred_remove(kauth_cred_t cred);
186 static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
187 static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
188 static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
189 static void kauth_cred_unref_hashlocked(kauth_cred_t *credp);
190
191 #if KAUTH_CRED_HASH_DEBUG
192 static int kauth_cred_count = 0;
193 static void kauth_cred_hash_print(void);
194 static void kauth_cred_print(kauth_cred_t cred);
195 #endif
196
197
198 /*
199 * kauth_resolver_init
200 *
201 * Description: Initialize the daemon side of the credential identity resolver
202 *
203 * Parameters: (void)
204 *
205 * Returns: (void)
206 *
207 * Notes: Intialize the credential identity resolver for use; the
208 * credential identity resolver is the KPI used by the user
209 * space credential identity resolver daemon to communicate
210 * with the kernel via the identitysvc() system call..
211 *
212 * This is how membership in more than 16 groups (1 effective
213 * and 15 supplementary) is supported, and also how UID's,
214 * UUID's, and so on, are translated to/from POSIX credential
215 * values.
216 *
217 * The credential identity resolver operates by attempting to
218 * determine identity first from the credential, then from
219 * the kernel credential identity cache, and finally by
220 * enqueueing a request to a user space daemon.
221 *
222 * This function is called from kauth_init() in the file
223 * kern_authorization.c.
224 */
225 void
226 kauth_resolver_init(void)
227 {
228 TAILQ_INIT(&kauth_resolver_unsubmitted);
229 TAILQ_INIT(&kauth_resolver_submitted);
230 TAILQ_INIT(&kauth_resolver_done);
231 kauth_resolver_sequence = 31337;
232 kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
233
234 /*
235 * 110% of average response time is "too long" and should be reported
236 */
237 kco_ma_init(&resolver_ma, 110, KCO_MA_F_WMA);
238 }
239
240
241 /*
242 * kauth_resolver_submit
243 *
244 * Description: Submit an external credential identity resolution request to
245 * the user space daemon.
246 *
247 * Parameters: lkp A pointer to an external
248 * lookup request
249 *
250 * Returns: 0 Success
251 * EWOULDBLOCK No resolver registered
252 * EINTR Operation interrupted (e.g. by
253 * a signal)
254 * ENOMEM Could not allocate work item
255 * workp->kr_result:??? An error from the user space
256 * daemon (includes ENOENT!)
257 *
258 * Notes: Allocate a work queue entry, submit the work and wait for
259 * the operation to either complete or time out. Outstanding
260 * operations may also be cancelled.
261 */
262 static int
263 kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
264 {
265 struct kauth_resolver_work *workp, *killp;
266 struct timespec ts;
267 int error, shouldfree;
268 uint64_t duration;
269
270 /* no point actually blocking if the resolver isn't up yet */
271 if (kauth_resolver_identity == 0) {
272 /*
273 * We've already waited an initial <kauth_resolver_timeout>
274 * seconds with no result.
275 *
276 * Sleep on a stack address so no one wakes us before timeout;
277 * we sleep a half a second in case we are a high priority
278 * process, so that memberd doesn't starve while we are in a
279 * tight loop between user and kernel, eating all the CPU.
280 */
281 error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2);
282 if (kauth_resolver_identity == 0) {
283 /*
284 * if things haven't changed while we were asleep,
285 * tell the caller we couldn't get an authoritative
286 * answer.
287 */
288 return(EWOULDBLOCK);
289 }
290 }
291
292 MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK);
293 if (workp == NULL)
294 return(ENOMEM);
295
296 workp->kr_work = *lkp;
297 workp->kr_refs = 1;
298 workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
299 workp->kr_result = 0;
300
301 /*
302 * We insert the request onto the unsubmitted queue, the call in from
303 * the resolver will it to the submitted thread when appropriate.
304 */
305 KAUTH_RESOLVER_LOCK();
306 workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++;
307 workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
308
309 /*
310 * XXX As an optimisation, we could check the queue for identical
311 * XXX items and coalesce them
312 */
313 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
314
315 wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
316 for (;;) {
317 /* we could compute a better timeout here */
318 ts.tv_sec = kauth_resolver_timeout;
319 ts.tv_nsec = 0;
320 error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
321 /* request has been completed? */
322 if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
323 break;
324 /* woken because the resolver has died? */
325 if (kauth_resolver_identity == 0) {
326 error = EIO;
327 break;
328 }
329 /* an error? */
330 if (error != 0)
331 break;
332 }
333
334 /*
335 * Update the moving average of how long it took; if it took longer
336 * than the time threshold, then we complain about it being slow.
337 */
338 duration = mach_absolute_time() - workp->kr_subtime;
339 if (kco_ma_addsample(&resolver_ma, duration)) {
340 uint64_t average;
341 uint64_t old_average;
342 int32_t threshold;
343 int count;
344
345 /* If we can't get information, don't log anything */
346 if (kco_ma_info(&resolver_ma, KCO_MA_F_WMA, &average, &old_average, &threshold, &count)) {
347 char pname[MAXCOMLEN+1] = "(NULL)";
348 proc_name(kauth_resolver_identity, pname, sizeof(pname));
349 // <rdar://6276265> printf("kauth_resolver_submit: External resolver pid %d (name %s) response time %lld, average %lld new %lld threshold %d%% actual %d%% count %d\n", kauth_resolver_identity, pname, duration, old_average, average, threshold, (int)((duration * 100) / old_average), count);
350 }
351 }
352
353 /* if the request was processed, copy the result */
354 if (error == 0)
355 *lkp = workp->kr_work;
356
357 /*
358 * If the request timed out and was never collected, the resolver
359 * is dead and probably not coming back anytime soon. In this
360 * case we revert to no-resolver behaviour, and punt all the other
361 * sleeping requests to clear the backlog.
362 */
363 if ((error == EWOULDBLOCK) && (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED)) {
364 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
365
366 /*
367 * Make the current resolver non-authoritative, and mark it as
368 * no longer registered to prevent kauth_cred_ismember_gid()
369 * enqueueing more work until a new one is registered. This
370 * mitigates the damage a crashing resolver may inflict.
371 */
372 kauth_resolver_identity = 0;
373 kauth_resolver_registered = 0;
374
375 /* kill all the other requestes that are waiting as well */
376 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
377 wakeup(killp);
378 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
379 wakeup(killp);
380 /* Cause all waiting-for-work threads to return EIO */
381 wakeup((caddr_t)&kauth_resolver_unsubmitted);
382 }
383
384 /*
385 * drop our reference on the work item, and note whether we should
386 * free it or not
387 */
388 if (--workp->kr_refs <= 0) {
389 /* work out which list we have to remove it from */
390 if (workp->kr_flags & KAUTH_REQUEST_DONE) {
391 TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link);
392 } else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) {
393 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
394 } else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
395 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
396 } else {
397 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
398 }
399 shouldfree = 1;
400 } else {
401 /* someone else still has a reference on this request */
402 shouldfree = 0;
403 }
404 /* collect request result */
405 if (error == 0)
406 error = workp->kr_result;
407 KAUTH_RESOLVER_UNLOCK();
408 /*
409 * If we dropped the last reference, free the request.
410 */
411 if (shouldfree)
412 FREE(workp, M_KAUTH);
413
414 KAUTH_DEBUG("RESOLVER - returning %d", error);
415 return(error);
416 }
417
418
419 /*
420 * identitysvc
421 *
422 * Description: System call interface for the external identity resolver.
423 *
424 * Parameters: uap->message Message from daemon to kernel
425 *
426 * Returns: 0 Successfully became resolver
427 * EPERM Not the resolver process
428 * kauth_authorize_generic:EPERM Not root user
429 * kauth_resolver_complete:EIO
430 * kauth_resolver_complete:EFAULT
431 * kauth_resolver_getwork:EINTR
432 * kauth_resolver_getwork:EFAULT
433 *
434 * Notes: This system call blocks until there is work enqueued, at
435 * which time the kernel wakes it up, and a message from the
436 * kernel is copied out to the identity resolution daemon, which
437 * proceed to attempt to resolve it. When the resolution has
438 * completed (successfully or not), the daemon called back into
439 * this system call to give the result to the kernel, and wait
440 * for the next request.
441 */
442 int
443 identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int32_t *retval)
444 {
445 int opcode = uap->opcode;
446 user_addr_t message = uap->message;
447 struct kauth_resolver_work *workp;
448 int error;
449 pid_t new_id;
450
451 /*
452 * New server registering itself.
453 */
454 if (opcode == KAUTH_EXTLOOKUP_REGISTER) {
455 new_id = current_proc()->p_pid;
456 if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) {
457 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id);
458 return(error);
459 }
460 KAUTH_RESOLVER_LOCK();
461 if (kauth_resolver_identity != new_id) {
462 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity);
463 /*
464 * We have a new server, so assume that all the old requests have been lost.
465 */
466 while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) {
467 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
468 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
469 workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED;
470 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link);
471 }
472 /*
473 * Allow user space resolver to override the
474 * external resolution timeout
475 */
476 if (message >= 30 && message <= 10000) {
477 kauth_resolver_timeout = message;
478 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message);
479 }
480 kauth_resolver_identity = new_id;
481 kauth_resolver_registered = 1;
482 wakeup(&kauth_resolver_unsubmitted);
483 }
484 KAUTH_RESOLVER_UNLOCK();
485 return(0);
486 }
487
488 /*
489 * Beyond this point, we must be the resolver process.
490 */
491 if (current_proc()->p_pid != kauth_resolver_identity) {
492 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
493 return(EPERM);
494 }
495
496 if (opcode == KAUTH_EXTLOOKUP_DEREGISTER) {
497 /*
498 * Terminate outstanding requests; without an authoritative
499 * resolver, we are now back on our own authority.
500 */
501 struct kauth_resolver_work *killp;
502
503 KAUTH_RESOLVER_LOCK();
504
505 /*
506 * Clear the identity, but also mark it as unregistered so
507 * there is no explicit future expectation of us getting a
508 * new resolver any time soon.
509 */
510 kauth_resolver_identity = 0;
511 kauth_resolver_registered = 0;
512
513 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
514 wakeup(killp);
515 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
516 wakeup(killp);
517 /* Cause all waiting-for-work threads to return EIO */
518 wakeup((caddr_t)&kauth_resolver_unsubmitted);
519 KAUTH_RESOLVER_UNLOCK();
520 }
521
522 /*
523 * Got a result returning?
524 */
525 if (opcode & KAUTH_EXTLOOKUP_RESULT) {
526 if ((error = kauth_resolver_complete(message)) != 0)
527 return(error);
528 }
529
530 /*
531 * Caller wants to take more work?
532 */
533 if (opcode & KAUTH_EXTLOOKUP_WORKER) {
534 if ((error = kauth_resolver_getwork(message)) != 0)
535 return(error);
536 }
537
538 return(0);
539 }
540
541
542 /*
543 * kauth_resolver_getwork_continue
544 *
545 * Description: Continuation for kauth_resolver_getwork
546 *
547 * Parameters: result Error code or 0 for the sleep
548 * that got us to this function
549 *
550 * Returns: 0 Success
551 * EINTR Interrupted (e.g. by signal)
552 * kauth_resolver_getwork2:EFAULT
553 *
554 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for
555 * more information.
556 */
557 static int
558 kauth_resolver_getwork_continue(int result)
559 {
560 thread_t thread;
561 struct uthread *ut;
562 user_addr_t message;
563
564 if (result) {
565 KAUTH_RESOLVER_UNLOCK();
566 return(result);
567 }
568
569 /*
570 * If we lost a race with another thread/memberd restarting, then we
571 * need to go back to sleep to look for more work. If it was memberd
572 * restarting, then the msleep0() will error out here, as our thread
573 * will already be "dead".
574 */
575 if (TAILQ_FIRST(&kauth_resolver_unsubmitted) == NULL) {
576 int error;
577
578 error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue);
579 /*
580 * If this is a wakeup from another thread in the resolver
581 * deregistering it, error out the request-for-work thread
582 */
583 if (!kauth_resolver_identity)
584 error = EIO;
585 KAUTH_RESOLVER_UNLOCK();
586 return(error);
587 }
588
589 thread = current_thread();
590 ut = get_bsdthread_info(thread);
591 message = ut->uu_kauth.message;
592 return(kauth_resolver_getwork2(message));
593 }
594
595
596 /*
597 * kauth_resolver_getwork2
598 *
599 * Decription: Common utility function to copy out a identity resolver work
600 * item from the kernel to user space as part of the user space
601 * identity resolver requesting work.
602 *
603 * Parameters: message message to user space
604 *
605 * Returns: 0 Success
606 * EFAULT Bad user space message address
607 *
608 * Notes: This common function exists to permit the use of continuations
609 * in the identity resoultion process. This frees up the stack
610 * while we are waiting for the user space resolver to complete
611 * a request. This is specifically used so that our per thread
612 * cost can be small, and we will therefore be willing to run a
613 * larger number of threads in the user space identity resolver.
614 */
615 static int
616 kauth_resolver_getwork2(user_addr_t message)
617 {
618 struct kauth_resolver_work *workp;
619 int error;
620
621 /*
622 * Note: We depend on the caller protecting us from a NULL work item
623 * queue, since we must have the kauth resolver lock on entry to this
624 * function.
625 */
626 workp = TAILQ_FIRST(&kauth_resolver_unsubmitted);
627
628 if ((error = copyout(&workp->kr_work, message, sizeof(workp->kr_work))) != 0) {
629 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
630 goto out;
631 }
632 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
633 workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
634 workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
635 workp->kr_subtime = mach_absolute_time();
636 TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
637
638 out:
639 KAUTH_RESOLVER_UNLOCK();
640 return(error);
641 }
642
643
644 /*
645 * kauth_resolver_getwork
646 *
647 * Description: Get a work item from the enqueued requests from the kernel and
648 * give it to the user space daemon.
649 *
650 * Parameters: message message to user space
651 *
652 * Returns: 0 Success
653 * EINTR Interrupted (e.g. by signal)
654 * kauth_resolver_getwork2:EFAULT
655 *
656 * Notes: This function blocks in a continuation if there are no work
657 * items available for processing at the time the user space
658 * identity resolution daemon makes a request for work. This
659 * permits a large number of threads to be used by the daemon,
660 * without using a lot of wired kernel memory when there are no
661 * acctual request outstanding.
662 */
663 static int
664 kauth_resolver_getwork(user_addr_t message)
665 {
666 struct kauth_resolver_work *workp;
667 int error;
668
669 KAUTH_RESOLVER_LOCK();
670 error = 0;
671 while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) {
672 thread_t thread = current_thread();
673 struct uthread *ut = get_bsdthread_info(thread);
674
675 ut->uu_kauth.message = message;
676 error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue);
677 KAUTH_RESOLVER_UNLOCK();
678 /*
679 * If this is a wakeup from another thread in the resolver
680 * deregistering it, error out the request-for-work thread
681 */
682 if (!kauth_resolver_identity)
683 error = EIO;
684 return(error);
685 }
686 return kauth_resolver_getwork2(message);
687 }
688
689
690 /*
691 * kauth_resolver_complete
692 *
693 * Description: Return a result from userspace.
694 *
695 * Parameters: message message from user space
696 *
697 * Returns: 0 Success
698 * EIO The resolver is dead
699 * copyin:EFAULT Bad message from user space
700 */
701 static int
702 kauth_resolver_complete(user_addr_t message)
703 {
704 struct kauth_identity_extlookup extl;
705 struct kauth_resolver_work *workp;
706 struct kauth_resolver_work *killp;
707 int error, result;
708
709 if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
710 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
711 return(error);
712 }
713
714 KAUTH_RESOLVER_LOCK();
715
716 error = 0;
717 result = 0;
718 switch (extl.el_result) {
719 case KAUTH_EXTLOOKUP_INPROG:
720 {
721 static int once = 0;
722
723 /* XXX this should go away once memberd is updated */
724 if (!once) {
725 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
726 once = 1;
727 }
728 }
729 /* FALLTHROUGH */
730
731 case KAUTH_EXTLOOKUP_SUCCESS:
732 break;
733
734 case KAUTH_EXTLOOKUP_FATAL:
735 /* fatal error means the resolver is dead */
736 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity);
737 /*
738 * Terminate outstanding requests; without an authoritative
739 * resolver, we are now back on our own authority. Tag the
740 * resolver unregistered to prevent kauth_cred_ismember_gid()
741 * enqueueing more work until a new one is registered. This
742 * mitigates the damage a crashing resolver may inflict.
743 */
744 kauth_resolver_identity = 0;
745 kauth_resolver_registered = 0;
746
747 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
748 wakeup(killp);
749 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
750 wakeup(killp);
751 /* Cause all waiting-for-work threads to return EIO */
752 wakeup((caddr_t)&kauth_resolver_unsubmitted);
753 /* and return EIO to the caller */
754 error = EIO;
755 break;
756
757 case KAUTH_EXTLOOKUP_BADRQ:
758 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno);
759 result = EINVAL;
760 break;
761
762 case KAUTH_EXTLOOKUP_FAILURE:
763 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno);
764 result = EIO;
765 break;
766
767 default:
768 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result);
769 result = EIO;
770 break;
771 }
772
773 /*
774 * In the case of a fatal error, we assume that the resolver will restart
775 * quickly and re-collect all of the outstanding requests. Thus, we don't
776 * complete the request which returned the fatal error status.
777 */
778 if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
779 /* scan our list for this request */
780 TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
781 /* found it? */
782 if (workp->kr_seqno == extl.el_seqno) {
783 /* copy result */
784 workp->kr_work = extl;
785 /* move onto completed list and wake up requester(s) */
786 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
787 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
788 workp->kr_flags |= KAUTH_REQUEST_DONE;
789 workp->kr_result = result;
790 TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
791 wakeup(workp);
792 break;
793 }
794 }
795 }
796 /*
797 * Note that it's OK for us not to find anything; if the request has
798 * timed out the work record will be gone.
799 */
800 KAUTH_RESOLVER_UNLOCK();
801
802 return(error);
803 }
804
805
806 /*
807 * Identity cache.
808 */
809
810 struct kauth_identity {
811 TAILQ_ENTRY(kauth_identity) ki_link;
812 int ki_valid;
813 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
814 #define KI_VALID_GID (1<<1)
815 #define KI_VALID_GUID (1<<2)
816 #define KI_VALID_NTSID (1<<3)
817 uid_t ki_uid;
818 gid_t ki_gid;
819 guid_t ki_guid;
820 ntsid_t ki_ntsid;
821 /*
822 * Expiry times are the earliest time at which we will disregard the cached state and go to
823 * userland. Before then if the valid bit is set, we will return the cached value. If it's
824 * not set, we will not go to userland to resolve, just assume that there is no answer
825 * available.
826 */
827 time_t ki_guid_expiry;
828 time_t ki_ntsid_expiry;
829 };
830
831 static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
832 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
833 static int kauth_identity_count;
834
835 static lck_mtx_t *kauth_identity_mtx;
836 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
837 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
838
839
840 static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
841 ntsid_t *ntsidp, time_t ntsid_expiry);
842 static void kauth_identity_register_and_free(struct kauth_identity *kip);
843 static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
844 static void kauth_identity_lru(struct kauth_identity *kip);
845 static int kauth_identity_guid_expired(struct kauth_identity *kip);
846 static int kauth_identity_ntsid_expired(struct kauth_identity *kip);
847 static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
848 static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
849 static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
850 static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
851
852
853 /*
854 * kauth_identity_init
855 *
856 * Description: Initialize the kernel side of the credential identity resolver
857 *
858 * Parameters: (void)
859 *
860 * Returns: (void)
861 *
862 * Notes: Intialize the credential identity resolver for use; the
863 * credential identity resolver is the KPI used to communicate
864 * with a user space credential identity resolver daemon.
865 *
866 * This function is called from kauth_init() in the file
867 * kern_authorization.c.
868 */
869 void
870 kauth_identity_init(void)
871 {
872 TAILQ_INIT(&kauth_identities);
873 kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
874 }
875
876
877 /*
878 * kauth_identity_alloc
879 *
880 * Description: Allocate and fill out a kauth_identity structure for
881 * translation between {UID|GID}/GUID/NTSID
882 *
883 * Parameters: uid
884 *
885 * Returns: NULL Insufficient memory to satisfy
886 * the request
887 * !NULL A pointer to the applocated
888 * structure, filled in
889 *
890 * Notes: It is illegal to translate between UID and GID; any given UUID
891 * or NTSID can oly refer to an NTSIDE or UUID (respectively),
892 * and *either* a UID *or* a GID, but not both.
893 */
894 static struct kauth_identity *
895 kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, ntsid_t *ntsidp, time_t ntsid_expiry)
896 {
897 struct kauth_identity *kip;
898
899 /* get and fill in a new identity */
900 MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO);
901 if (kip != NULL) {
902 if (gid != KAUTH_GID_NONE) {
903 kip->ki_gid = gid;
904 kip->ki_valid = KI_VALID_GID;
905 }
906 if (uid != KAUTH_UID_NONE) {
907 if (kip->ki_valid & KI_VALID_GID)
908 panic("can't allocate kauth identity with both uid and gid");
909 kip->ki_uid = uid;
910 kip->ki_valid = KI_VALID_UID;
911 }
912 if (guidp != NULL) {
913 kip->ki_guid = *guidp;
914 kip->ki_valid |= KI_VALID_GUID;
915 }
916 kip->ki_guid_expiry = guid_expiry;
917 if (ntsidp != NULL) {
918 kip->ki_ntsid = *ntsidp;
919 kip->ki_valid |= KI_VALID_NTSID;
920 }
921 kip->ki_ntsid_expiry = ntsid_expiry;
922 }
923 return(kip);
924 }
925
926
927 /*
928 * kauth_identity_register_and_free
929 *
930 * Description: Register an association between identity tokens. The passed
931 * 'kip' is freed by this function.
932 *
933 * Parameters: kip Pointer to kauth_identity
934 * structure to register
935 *
936 * Returns: (void)
937 *
938 * Notes: The memory pointer to by 'kip' is assumed to have been
939 * previously allocated via kauth_identity_alloc().
940 */
941 static void
942 kauth_identity_register_and_free(struct kauth_identity *kip)
943 {
944 struct kauth_identity *ip;
945
946 /*
947 * We search the cache for the UID listed in the incoming association.
948 * If we already have an entry, the new information is merged.
949 */
950 ip = NULL;
951 KAUTH_IDENTITY_LOCK();
952 if (kip->ki_valid & KI_VALID_UID) {
953 if (kip->ki_valid & KI_VALID_GID)
954 panic("kauth_identity: can't insert record with both UID and GID as key");
955 TAILQ_FOREACH(ip, &kauth_identities, ki_link)
956 if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid))
957 break;
958 } else if (kip->ki_valid & KI_VALID_GID) {
959 TAILQ_FOREACH(ip, &kauth_identities, ki_link)
960 if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid))
961 break;
962 } else {
963 panic("kauth_identity: can't insert record without UID or GID as key");
964 }
965
966 if (ip != NULL) {
967 /* we already have an entry, merge/overwrite */
968 if (kip->ki_valid & KI_VALID_GUID) {
969 ip->ki_guid = kip->ki_guid;
970 ip->ki_valid |= KI_VALID_GUID;
971 }
972 ip->ki_guid_expiry = kip->ki_guid_expiry;
973 if (kip->ki_valid & KI_VALID_NTSID) {
974 ip->ki_ntsid = kip->ki_ntsid;
975 ip->ki_valid |= KI_VALID_NTSID;
976 }
977 ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
978 /* and discard the incoming identity */
979 FREE(kip, M_KAUTH);
980 ip = NULL;
981 } else {
982 /* don't have any information on this identity, so just add it */
983 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
984 if (++kauth_identity_count > KAUTH_IDENTITY_CACHEMAX) {
985 ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
986 TAILQ_REMOVE(&kauth_identities, ip, ki_link);
987 kauth_identity_count--;
988 }
989 }
990 KAUTH_IDENTITY_UNLOCK();
991 /* have to drop lock before freeing expired entry */
992 if (ip != NULL)
993 FREE(ip, M_KAUTH);
994 }
995
996
997 /*
998 * kauth_identity_updatecache
999 *
1000 * Description: Given a lookup result, add any associations that we don't
1001 * currently have.
1002 *
1003 * Parameters: elp External lookup result from
1004 * user space daemon to kernel
1005 * rkip pointer to returned kauth
1006 * identity, or NULL
1007 *
1008 * Returns: (void)
1009 *
1010 * Implicit returns:
1011 * *rkip Modified (if non-NULL)
1012 */
1013 static void
1014 kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip)
1015 {
1016 struct timeval tv;
1017 struct kauth_identity *kip;
1018
1019 microuptime(&tv);
1020
1021 /* user identity? */
1022 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
1023 KAUTH_IDENTITY_LOCK();
1024 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1025 /* matching record */
1026 if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
1027 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
1028 kip->ki_guid = elp->el_uguid;
1029 kip->ki_valid |= KI_VALID_GUID;
1030 }
1031 kip->ki_guid_expiry = tv.tv_sec + elp->el_uguid_valid;
1032 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
1033 kip->ki_ntsid = elp->el_usid;
1034 kip->ki_valid |= KI_VALID_NTSID;
1035 }
1036 kip->ki_ntsid_expiry = tv.tv_sec + elp->el_usid_valid;
1037 kauth_identity_lru(kip);
1038 if (rkip != NULL)
1039 *rkip = *kip;
1040 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1041 break;
1042 }
1043 }
1044 KAUTH_IDENTITY_UNLOCK();
1045 /* not found in cache, add new record */
1046 if (kip == NULL) {
1047 kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
1048 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
1049 tv.tv_sec + elp->el_uguid_valid,
1050 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
1051 tv.tv_sec + elp->el_usid_valid);
1052 if (kip != NULL) {
1053 if (rkip != NULL)
1054 *rkip = *kip;
1055 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1056 kauth_identity_register_and_free(kip);
1057 }
1058 }
1059 }
1060
1061 /* group identity? */
1062 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID) {
1063 KAUTH_IDENTITY_LOCK();
1064 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1065 /* matching record */
1066 if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) {
1067 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) {
1068 kip->ki_guid = elp->el_gguid;
1069 kip->ki_valid |= KI_VALID_GUID;
1070 }
1071 kip->ki_guid_expiry = tv.tv_sec + elp->el_gguid_valid;
1072 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
1073 kip->ki_ntsid = elp->el_gsid;
1074 kip->ki_valid |= KI_VALID_NTSID;
1075 }
1076 kip->ki_ntsid_expiry = tv.tv_sec + elp->el_gsid_valid;
1077 kauth_identity_lru(kip);
1078 if (rkip != NULL)
1079 *rkip = *kip;
1080 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1081 break;
1082 }
1083 }
1084 KAUTH_IDENTITY_UNLOCK();
1085 /* not found in cache, add new record */
1086 if (kip == NULL) {
1087 kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
1088 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
1089 tv.tv_sec + elp->el_gguid_valid,
1090 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
1091 tv.tv_sec + elp->el_gsid_valid);
1092 if (kip != NULL) {
1093 if (rkip != NULL)
1094 *rkip = *kip;
1095 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
1096 kauth_identity_register_and_free(kip);
1097 }
1098 }
1099 }
1100
1101 }
1102
1103
1104 /*
1105 * kauth_identity_lru
1106 *
1107 * Description: Promote the entry to the head of the LRU, assumes the cache
1108 * is locked.
1109 *
1110 * Parameters: kip kauth identity to move to the
1111 * head of the LRU list, if it's
1112 * not already there
1113 *
1114 * Returns: (void)
1115 *
1116 * Notes: This is called even if the entry has expired; typically an
1117 * expired entry that's been looked up is about to be revalidated,
1118 * and having it closer to the head of the LRU means finding it
1119 * quickly again when the revalidation comes through.
1120 */
1121 static void
1122 kauth_identity_lru(struct kauth_identity *kip)
1123 {
1124 if (kip != TAILQ_FIRST(&kauth_identities)) {
1125 TAILQ_REMOVE(&kauth_identities, kip, ki_link);
1126 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
1127 }
1128 }
1129
1130
1131 /*
1132 * kauth_identity_guid_expired
1133 *
1134 * Description: Handle lazy expiration of GUID translations.
1135 *
1136 * Parameters: kip kauth identity to check for
1137 * GUID expiration
1138 *
1139 * Returns: 1 Expired
1140 * 0 Not expired
1141 */
1142 static int
1143 kauth_identity_guid_expired(struct kauth_identity *kip)
1144 {
1145 struct timeval tv;
1146
1147 microuptime(&tv);
1148 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip->ki_guid_expiry, tv.tv_sec);
1149 return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
1150 }
1151
1152
1153 /*
1154 * kauth_identity_ntsid_expired
1155 *
1156 * Description: Handle lazy expiration of NTSID translations.
1157 *
1158 * Parameters: kip kauth identity to check for
1159 * NTSID expiration
1160 *
1161 * Returns: 1 Expired
1162 * 0 Not expired
1163 */
1164 static int
1165 kauth_identity_ntsid_expired(struct kauth_identity *kip)
1166 {
1167 struct timeval tv;
1168
1169 microuptime(&tv);
1170 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip->ki_ntsid_expiry, tv.tv_sec);
1171 return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
1172 }
1173
1174
1175 /*
1176 * kauth_identity_find_uid
1177 *
1178 * Description: Search for an entry by UID
1179 *
1180 * Parameters: uid UID to find
1181 * kir Pointer to return area
1182 *
1183 * Returns: 0 Found
1184 * ENOENT Not found
1185 *
1186 * Implicit returns:
1187 * *klr Modified, if found
1188 */
1189 static int
1190 kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
1191 {
1192 struct kauth_identity *kip;
1193
1194 KAUTH_IDENTITY_LOCK();
1195 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1196 if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) {
1197 kauth_identity_lru(kip);
1198 /* Copy via structure assignment */
1199 *kir = *kip;
1200 break;
1201 }
1202 }
1203 KAUTH_IDENTITY_UNLOCK();
1204 return((kip == NULL) ? ENOENT : 0);
1205 }
1206
1207
1208 /*
1209 * kauth_identity_find_uid
1210 *
1211 * Description: Search for an entry by GID
1212 *
1213 * Parameters: gid GID to find
1214 * kir Pointer to return area
1215 *
1216 * Returns: 0 Found
1217 * ENOENT Not found
1218 *
1219 * Implicit returns:
1220 * *klr Modified, if found
1221 */
1222 static int
1223 kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
1224 {
1225 struct kauth_identity *kip;
1226
1227 KAUTH_IDENTITY_LOCK();
1228 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1229 if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) {
1230 kauth_identity_lru(kip);
1231 /* Copy via structure assignment */
1232 *kir = *kip;
1233 break;
1234 }
1235 }
1236 KAUTH_IDENTITY_UNLOCK();
1237 return((kip == NULL) ? ENOENT : 0);
1238 }
1239
1240
1241 /*
1242 * kauth_identity_find_guid
1243 *
1244 * Description: Search for an entry by GUID
1245 *
1246 * Parameters: guidp Pointer to GUID to find
1247 * kir Pointer to return area
1248 *
1249 * Returns: 0 Found
1250 * ENOENT Not found
1251 *
1252 * Implicit returns:
1253 * *klr Modified, if found
1254 *
1255 * Note: The association may be expired, in which case the caller
1256 * may elect to call out to userland to revalidate.
1257 */
1258 static int
1259 kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
1260 {
1261 struct kauth_identity *kip;
1262
1263 KAUTH_IDENTITY_LOCK();
1264 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1265 if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
1266 kauth_identity_lru(kip);
1267 /* Copy via structure assignment */
1268 *kir = *kip;
1269 break;
1270 }
1271 }
1272 KAUTH_IDENTITY_UNLOCK();
1273 return((kip == NULL) ? ENOENT : 0);
1274 }
1275
1276
1277 /*
1278 * kauth_identity_find_ntsid
1279 *
1280 * Description: Search for an entry by NTSID
1281 *
1282 * Parameters: ntsid Pointer to NTSID to find
1283 * kir Pointer to return area
1284 *
1285 * Returns: 0 Found
1286 * ENOENT Not found
1287 *
1288 * Implicit returns:
1289 * *klr Modified, if found
1290 *
1291 * Note: The association may be expired, in which case the caller
1292 * may elect to call out to userland to revalidate.
1293 */
1294 static int
1295 kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
1296 {
1297 struct kauth_identity *kip;
1298
1299 KAUTH_IDENTITY_LOCK();
1300 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
1301 if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) {
1302 kauth_identity_lru(kip);
1303 /* Copy via structure assignment */
1304 *kir = *kip;
1305 break;
1306 }
1307 }
1308 KAUTH_IDENTITY_UNLOCK();
1309 return((kip == NULL) ? ENOENT : 0);
1310 }
1311
1312
1313 /*
1314 * GUID handling.
1315 */
1316 guid_t kauth_null_guid;
1317
1318
1319 /*
1320 * kauth_guid_equal
1321 *
1322 * Description: Determine the equality of two GUIDs
1323 *
1324 * Parameters: guid1 Pointer to first GUID
1325 * guid2 Pointer to second GUID
1326 *
1327 * Returns: 0 If GUIDs are inequal
1328 * !0 If GUIDs are equal
1329 */
1330 int
1331 kauth_guid_equal(guid_t *guid1, guid_t *guid2)
1332 {
1333 return(bcmp(guid1, guid2, sizeof(*guid1)) == 0);
1334 }
1335
1336
1337 /*
1338 * kauth_wellknown_guid
1339 *
1340 * Description: Determine if a GUID is a well-known GUID
1341 *
1342 * Parameters: guid Pointer to GUID to check
1343 *
1344 * Returns: KAUTH_WKG_NOT Not a wel known GUID
1345 * KAUTH_WKG_EVERYBODY "Everybody"
1346 * KAUTH_WKG_NOBODY "Nobody"
1347 * KAUTH_WKG_OWNER "Other"
1348 * KAUTH_WKG_GROUP "Group"
1349 */
1350 int
1351 kauth_wellknown_guid(guid_t *guid)
1352 {
1353 static char fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
1354 int code;
1355 /*
1356 * All WKGs begin with the same 12 bytes.
1357 */
1358 if (bcmp((void *)guid, fingerprint, 12) == 0) {
1359 /*
1360 * The final 4 bytes are our code (in network byte order).
1361 */
1362 code = OSSwapHostToBigInt32(*(u_int32_t *)&guid->g_guid[12]);
1363 switch(code) {
1364 case 0x0000000c:
1365 return(KAUTH_WKG_EVERYBODY);
1366 case 0xfffffffe:
1367 return(KAUTH_WKG_NOBODY);
1368 case 0x0000000a:
1369 return(KAUTH_WKG_OWNER);
1370 case 0x00000010:
1371 return(KAUTH_WKG_GROUP);
1372 }
1373 }
1374 return(KAUTH_WKG_NOT);
1375 }
1376
1377
1378 /*
1379 * kauth_ntsid_equal
1380 *
1381 * Description: Determine the equality of two NTSIDs (NT Security Identifiers)
1382 *
1383 * Paramters: sid1 Pointer to first NTSID
1384 * sid2 Pointer to second NTSID
1385 *
1386 * Returns: 0 If GUIDs are inequal
1387 * !0 If GUIDs are equal
1388 */
1389 int
1390 kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
1391 {
1392 /* check sizes for equality, also sanity-check size while we're at it */
1393 if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) &&
1394 (KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) &&
1395 bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)) == 0)
1396 return(1);
1397 return(0);
1398 }
1399
1400
1401 /*
1402 * Identity KPI
1403 *
1404 * We support four tokens representing identity:
1405 * - Credential reference
1406 * - UID
1407 * - GUID
1408 * - NT security identifier
1409 *
1410 * Of these, the UID is the ubiquitous identifier; cross-referencing should
1411 * be done using it.
1412 */
1413
1414 static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
1415
1416
1417 /*
1418 * kauth_cred_change_egid
1419 *
1420 * Description: Set EGID by changing the first element of cr_groups for the
1421 * passed credential; if the new EGID exists in the list of
1422 * groups already, then rotate the old EGID into its position,
1423 * otherwise replace it
1424 *
1425 * Parameters: cred Pointer to the credential to modify
1426 * new_egid The new EGID to set
1427 *
1428 * Returns: 0 The egid did not displace a member of
1429 * the supplementary group list
1430 * 1 The egid being set displaced a member
1431 * of the supplementary groups list
1432 *
1433 * Note: Utility function; internal use only because of locking.
1434 *
1435 * This function operates on the credential passed; the caller
1436 * must operate either on a newly allocated credential (one for
1437 * which there is no hash cache reference and no externally
1438 * visible pointer reference), or a template credential.
1439 */
1440 static int
1441 kauth_cred_change_egid(kauth_cred_t cred, gid_t new_egid)
1442 {
1443 int i;
1444 int displaced = 1;
1445 #if radar_4600026
1446 int is_member;
1447 #endif /* radar_4600026 */
1448 gid_t old_egid = cred->cr_groups[0];
1449
1450 /* Ignoring the first entry, scan for a match for the new egid */
1451 for (i = 1; i < cred->cr_ngroups; i++) {
1452 /*
1453 * If we find a match, swap them so we don't lose overall
1454 * group information
1455 */
1456 if (cred->cr_groups[i] == new_egid) {
1457 cred->cr_groups[i] = old_egid;
1458 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n");
1459 displaced = 0;
1460 break;
1461 }
1462 }
1463
1464 #if radar_4600026
1465 #error Fix radar 4600026 first!!!
1466
1467 /*
1468 This is correct for memberd behaviour, but incorrect for POSIX; to address
1469 this, we would need to automatically opt-out any SUID/SGID binary, and force
1470 it to use initgroups to opt back in. We take the approach of considering it
1471 opt'ed out in any group of 16 displacement instead, since it's a much more
1472 conservative approach (i.e. less likely to cause things to break).
1473 */
1474
1475 /*
1476 * If we displaced a member of the supplementary groups list of the
1477 * credential, and we have not opted out of memberd, then if memberd
1478 * says that the credential is a member of the group, then it has not
1479 * actually been displaced.
1480 *
1481 * NB: This is typically a cold code path.
1482 */
1483 if (displaced && !(cred->cr_flags & CRF_NOMEMBERD) &&
1484 kauth_cred_ismember_gid(cred, new_egid, &is_member) == 0 &&
1485 is_member) {
1486 displaced = 0;
1487 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n");
1488 }
1489 #endif /* radar_4600026 */
1490
1491 /* set the new EGID into the old spot */
1492 cred->cr_groups[0] = new_egid;
1493
1494 return (displaced);
1495 }
1496
1497
1498 /*
1499 * kauth_cred_getuid
1500 *
1501 * Description: Fetch UID from credential
1502 *
1503 * Parameters: cred Credential to examine
1504 *
1505 * Returns: (uid_t) UID associated with credential
1506 */
1507 uid_t
1508 kauth_cred_getuid(kauth_cred_t cred)
1509 {
1510 NULLCRED_CHECK(cred);
1511 return(cred->cr_uid);
1512 }
1513
1514
1515 /*
1516 * kauth_cred_getgid
1517 *
1518 * Description: Fetch GID from credential
1519 *
1520 * Parameters: cred Credential to examine
1521 *
1522 * Returns: (gid_t) GID associated with credential
1523 */
1524 uid_t
1525 kauth_cred_getgid(kauth_cred_t cred)
1526 {
1527 NULLCRED_CHECK(cred);
1528 return(cred->cr_gid);
1529 }
1530
1531
1532 /*
1533 * kauth_cred_guid2uid
1534 *
1535 * Description: Fetch UID from GUID
1536 *
1537 * Parameters: guidp Pointer to GUID to examine
1538 * uidp Pointer to buffer for UID
1539 *
1540 * Returns: 0 Success
1541 * kauth_cred_cache_lookup:EINVAL
1542 *
1543 * Implicit returns:
1544 * *uidp Modified, if successful
1545 */
1546 int
1547 kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
1548 {
1549 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
1550 }
1551
1552
1553 /*
1554 * kauth_cred_guid2gid
1555 *
1556 * Description: Fetch GID from GUID
1557 *
1558 * Parameters: guidp Pointer to GUID to examine
1559 * gidp Pointer to buffer for GID
1560 *
1561 * Returns: 0 Success
1562 * kauth_cred_cache_lookup:EINVAL
1563 *
1564 * Implicit returns:
1565 * *gidp Modified, if successful
1566 */
1567 int
1568 kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
1569 {
1570 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
1571 }
1572
1573
1574 /*
1575 * kauth_cred_ntsid2uid
1576 *
1577 * Description: Fetch UID from NTSID
1578 *
1579 * Parameters: sidp Pointer to NTSID to examine
1580 * uidp Pointer to buffer for UID
1581 *
1582 * Returns: 0 Success
1583 * kauth_cred_cache_lookup:EINVAL
1584 *
1585 * Implicit returns:
1586 * *uidp Modified, if successful
1587 */
1588 int
1589 kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
1590 {
1591 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
1592 }
1593
1594
1595 /*
1596 * kauth_cred_ntsid2gid
1597 *
1598 * Description: Fetch GID from NTSID
1599 *
1600 * Parameters: sidp Pointer to NTSID to examine
1601 * gidp Pointer to buffer for GID
1602 *
1603 * Returns: 0 Success
1604 * kauth_cred_cache_lookup:EINVAL
1605 *
1606 * Implicit returns:
1607 * *gidp Modified, if successful
1608 */
1609 int
1610 kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
1611 {
1612 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
1613 }
1614
1615
1616 /*
1617 * kauth_cred_ntsid2guid
1618 *
1619 * Description: Fetch GUID from NTSID
1620 *
1621 * Parameters: sidp Pointer to NTSID to examine
1622 * guidp Pointer to buffer for GUID
1623 *
1624 * Returns: 0 Success
1625 * kauth_cred_cache_lookup:EINVAL
1626 *
1627 * Implicit returns:
1628 * *guidp Modified, if successful
1629 */
1630 int
1631 kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
1632 {
1633 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
1634 }
1635
1636
1637 /*
1638 * kauth_cred_uid2guid
1639 *
1640 * Description: Fetch GUID from UID
1641 *
1642 * Parameters: uid UID to examine
1643 * guidp Pointer to buffer for GUID
1644 *
1645 * Returns: 0 Success
1646 * kauth_cred_cache_lookup:EINVAL
1647 *
1648 * Implicit returns:
1649 * *guidp Modified, if successful
1650 */
1651 int
1652 kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
1653 {
1654 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
1655 }
1656
1657
1658 /*
1659 * kauth_cred_getguid
1660 *
1661 * Description: Fetch GUID from credential
1662 *
1663 * Parameters: cred Credential to examine
1664 * guidp Pointer to buffer for GUID
1665 *
1666 * Returns: 0 Success
1667 * kauth_cred_cache_lookup:EINVAL
1668 *
1669 * Implicit returns:
1670 * *guidp Modified, if successful
1671 */
1672 int
1673 kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
1674 {
1675 NULLCRED_CHECK(cred);
1676 return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
1677 }
1678
1679
1680 /*
1681 * kauth_cred_getguid
1682 *
1683 * Description: Fetch GUID from GID
1684 *
1685 * Parameters: gid GID to examine
1686 * guidp Pointer to buffer for GUID
1687 *
1688 * Returns: 0 Success
1689 * kauth_cred_cache_lookup:EINVAL
1690 *
1691 * Implicit returns:
1692 * *guidp Modified, if successful
1693 */
1694 int
1695 kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
1696 {
1697 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
1698 }
1699
1700
1701 /*
1702 * kauth_cred_uid2ntsid
1703 *
1704 * Description: Fetch NTSID from UID
1705 *
1706 * Parameters: uid UID to examine
1707 * sidp Pointer to buffer for NTSID
1708 *
1709 * Returns: 0 Success
1710 * kauth_cred_cache_lookup:EINVAL
1711 *
1712 * Implicit returns:
1713 * *sidp Modified, if successful
1714 */
1715 int
1716 kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
1717 {
1718 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
1719 }
1720
1721
1722 /*
1723 * kauth_cred_getntsid
1724 *
1725 * Description: Fetch NTSID from credential
1726 *
1727 * Parameters: cred Credential to examine
1728 * sidp Pointer to buffer for NTSID
1729 *
1730 * Returns: 0 Success
1731 * kauth_cred_cache_lookup:EINVAL
1732 *
1733 * Implicit returns:
1734 * *sidp Modified, if successful
1735 */
1736 int
1737 kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
1738 {
1739 NULLCRED_CHECK(cred);
1740 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
1741 }
1742
1743
1744 /*
1745 * kauth_cred_gid2ntsid
1746 *
1747 * Description: Fetch NTSID from GID
1748 *
1749 * Parameters: gid GID to examine
1750 * sidp Pointer to buffer for NTSID
1751 *
1752 * Returns: 0 Success
1753 * kauth_cred_cache_lookup:EINVAL
1754 *
1755 * Implicit returns:
1756 * *sidp Modified, if successful
1757 */
1758 int
1759 kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
1760 {
1761 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
1762 }
1763
1764
1765 /*
1766 * kauth_cred_guid2ntsid
1767 *
1768 * Description: Fetch NTSID from GUID
1769 *
1770 * Parameters: guidp Pointer to GUID to examine
1771 * sidp Pointer to buffer for NTSID
1772 *
1773 * Returns: 0 Success
1774 * kauth_cred_cache_lookup:EINVAL
1775 *
1776 * Implicit returns:
1777 * *sidp Modified, if successful
1778 */
1779 int
1780 kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
1781 {
1782 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
1783 }
1784
1785
1786 /*
1787 * kauth_cred_cache_lookup
1788 *
1789 * Description: Lookup a translation in the cache; if one is not found, and
1790 * the attempt was not fatal, submit the request to the resolver
1791 * instead, and wait for it to complete or be aborted.
1792 *
1793 * Parameters: from Identity information we have
1794 * to Identity information we want
1795 * src Pointer to buffer containing
1796 * the source identity
1797 * dst Pointer to buffer to receive
1798 * the target identity
1799 *
1800 * Returns: 0 Success
1801 * EINVAL Unknown source identity type
1802 */
1803 static int
1804 kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
1805 {
1806 struct kauth_identity ki;
1807 struct kauth_identity_extlookup el;
1808 int error;
1809 int (* expired)(struct kauth_identity *kip);
1810
1811 KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
1812
1813 /*
1814 * Look for an existing cache entry for this association.
1815 * If the entry has not expired, return the cached information.
1816 */
1817 ki.ki_valid = 0;
1818 switch(from) {
1819 case KI_VALID_UID:
1820 error = kauth_identity_find_uid(*(uid_t *)src, &ki);
1821 break;
1822 case KI_VALID_GID:
1823 error = kauth_identity_find_gid(*(gid_t *)src, &ki);
1824 break;
1825 case KI_VALID_GUID:
1826 error = kauth_identity_find_guid((guid_t *)src, &ki);
1827 break;
1828 case KI_VALID_NTSID:
1829 error = kauth_identity_find_ntsid((ntsid_t *)src, &ki);
1830 break;
1831 default:
1832 return(EINVAL);
1833 }
1834 /* lookup failure or error */
1835 if (error != 0) {
1836 /* any other error is fatal */
1837 if (error != ENOENT) {
1838 /* XXX bogus check - this is not possible */
1839 KAUTH_DEBUG("CACHE - cache search error %d", error);
1840 return(error);
1841 }
1842 } else {
1843 /* do we have a translation? */
1844 if (ki.ki_valid & to) {
1845 /* found a valid cached entry, check expiry */
1846 switch(to) {
1847 case KI_VALID_GUID:
1848 expired = kauth_identity_guid_expired;
1849 break;
1850 case KI_VALID_NTSID:
1851 expired = kauth_identity_ntsid_expired;
1852 break;
1853 default:
1854 switch(from) {
1855 case KI_VALID_GUID:
1856 expired = kauth_identity_guid_expired;
1857 break;
1858 case KI_VALID_NTSID:
1859 expired = kauth_identity_ntsid_expired;
1860 break;
1861 default:
1862 expired = NULL;
1863 }
1864 }
1865 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki.ki_valid);
1866 /*
1867 * If no expiry function, or not expired, we have found
1868 * a hit.
1869 */
1870 if (!expired) {
1871 KAUTH_DEBUG("CACHE - no expiry function");
1872 goto found;
1873 }
1874 if (!expired(&ki)) {
1875 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1876 goto found;
1877 }
1878 /*
1879 * We leave ki_valid set here; it contains a
1880 * translation but the TTL has expired. If we can't
1881 * get a result from the resolver, we will use it as
1882 * a better-than nothing alternative.
1883 */
1884 KAUTH_DEBUG("CACHE - expired entry found");
1885 }
1886 }
1887
1888 /*
1889 * We failed to find a cache entry; call the resolver.
1890 *
1891 * Note: We ask for as much data as we can get.
1892 */
1893 bzero(&el, sizeof(el));
1894 el.el_info_pid = current_proc()->p_pid;
1895 switch(from) {
1896 case KI_VALID_UID:
1897 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
1898 el.el_uid = *(uid_t *)src;
1899 break;
1900 case KI_VALID_GID:
1901 el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
1902 el.el_gid = *(gid_t *)src;
1903 break;
1904 case KI_VALID_GUID:
1905 el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
1906 el.el_uguid = *(guid_t *)src;
1907 el.el_gguid = *(guid_t *)src;
1908 break;
1909 case KI_VALID_NTSID:
1910 el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
1911 el.el_usid = *(ntsid_t *)src;
1912 el.el_gsid = *(ntsid_t *)src;
1913 break;
1914 default:
1915 return(EINVAL);
1916 }
1917 /*
1918 * Here we ask for everything all at once, to avoid having to work
1919 * out what we really want now, or might want soon.
1920 *
1921 * Asking for SID translations when we don't know we need them right
1922 * now is going to cause excess work to be done if we're connected
1923 * to a network that thinks it can translate them. This list needs
1924 * to get smaller/smarter.
1925 */
1926 el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
1927 KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
1928 KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
1929 KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
1930 error = kauth_resolver_submit(&el);
1931 KAUTH_DEBUG("CACHE - resolver returned %d", error);
1932 /* was the lookup successful? */
1933 if (error == 0) {
1934 /*
1935 * Save the results from the lookup - may have other
1936 * information even if we didn't get a guid.
1937 */
1938 kauth_identity_updatecache(&el, &ki);
1939 }
1940 /*
1941 * Check to see if we have a valid result.
1942 */
1943 if (!error && !(ki.ki_valid & to))
1944 error = ENOENT;
1945 if (error)
1946 return(error);
1947 found:
1948 switch(to) {
1949 case KI_VALID_UID:
1950 *(uid_t *)dst = ki.ki_uid;
1951 break;
1952 case KI_VALID_GID:
1953 *(gid_t *)dst = ki.ki_gid;
1954 break;
1955 case KI_VALID_GUID:
1956 *(guid_t *)dst = ki.ki_guid;
1957 break;
1958 case KI_VALID_NTSID:
1959 *(ntsid_t *)dst = ki.ki_ntsid;
1960 break;
1961 default:
1962 return(EINVAL);
1963 }
1964 KAUTH_DEBUG("CACHE - returned successfully");
1965 return(0);
1966 }
1967
1968
1969 /*
1970 * Group membership cache.
1971 *
1972 * XXX the linked-list implementation here needs to be optimized.
1973 */
1974
1975 struct kauth_group_membership {
1976 TAILQ_ENTRY(kauth_group_membership) gm_link;
1977 uid_t gm_uid; /* the identity whose membership we're recording */
1978 gid_t gm_gid; /* group of which they are a member */
1979 time_t gm_expiry; /* TTL for the membership */
1980 int gm_flags;
1981 #define KAUTH_GROUP_ISMEMBER (1<<0)
1982 };
1983
1984 TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
1985 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1986 static int kauth_groups_count;
1987
1988 static lck_mtx_t *kauth_groups_mtx;
1989 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1990 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1991
1992 static int kauth_groups_expired(struct kauth_group_membership *gm);
1993 static void kauth_groups_lru(struct kauth_group_membership *gm);
1994 static void kauth_groups_updatecache(struct kauth_identity_extlookup *el);
1995
1996
1997 /*
1998 * kauth_groups_init
1999 *
2000 * Description: Initialize the groups cache
2001 *
2002 * Parameters: (void)
2003 *
2004 * Returns: (void)
2005 *
2006 * Notes: Intialize the groups cache for use; the group cache is used
2007 * to avoid unnecessary calls out to user space.
2008 *
2009 * This function is called from kauth_init() in the file
2010 * kern_authorization.c.
2011 */
2012 void
2013 kauth_groups_init(void)
2014 {
2015 TAILQ_INIT(&kauth_groups);
2016 kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
2017 }
2018
2019
2020 /*
2021 * kauth_groups_expired
2022 *
2023 * Description: Handle lazy expiration of group membership cache entries
2024 *
2025 * Parameters: gm group membership entry to
2026 * check for expiration
2027 *
2028 * Returns: 1 Expired
2029 * 0 Not expired
2030 */
2031 static int
2032 kauth_groups_expired(struct kauth_group_membership *gm)
2033 {
2034 struct timeval tv;
2035
2036 microuptime(&tv);
2037 return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
2038 }
2039
2040
2041 /*
2042 * kauth_groups_lru
2043 *
2044 * Description: Promote the entry to the head of the LRU, assumes the cache
2045 * is locked.
2046 *
2047 * Parameters: kip group membership entry to move
2048 * to the head of the LRU list,
2049 * if it's not already there
2050 *
2051 * Returns: (void)
2052 *
2053 * Notes: This is called even if the entry has expired; typically an
2054 * expired entry that's been looked up is about to be revalidated,
2055 * and having it closer to the head of the LRU means finding it
2056 * quickly again when the revalidation comes through.
2057 */
2058 static void
2059 kauth_groups_lru(struct kauth_group_membership *gm)
2060 {
2061 if (gm != TAILQ_FIRST(&kauth_groups)) {
2062 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2063 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2064 }
2065 }
2066
2067
2068 /*
2069 * kauth_groups_updatecache
2070 *
2071 * Description: Given a lookup result, add any group cache associations that
2072 * we don't currently have.
2073 *
2074 * Parameters: elp External lookup result from
2075 * user space daemon to kernel
2076 * rkip pointer to returned kauth
2077 * identity, or NULL
2078 *
2079 * Returns: (void)
2080 */
2081 static void
2082 kauth_groups_updatecache(struct kauth_identity_extlookup *el)
2083 {
2084 struct kauth_group_membership *gm;
2085 struct timeval tv;
2086
2087 /* need a valid response if we are to cache anything */
2088 if ((el->el_flags &
2089 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
2090 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
2091 return;
2092
2093 microuptime(&tv);
2094
2095 /*
2096 * Search for an existing record for this association before inserting
2097 * a new one; if we find one, update it instead of creating a new one
2098 */
2099 KAUTH_GROUPS_LOCK();
2100 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2101 if ((el->el_uid == gm->gm_uid) &&
2102 (el->el_gid == gm->gm_gid)) {
2103 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2104 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2105 } else {
2106 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2107 }
2108 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
2109 kauth_groups_lru(gm);
2110 break;
2111 }
2112 }
2113 KAUTH_GROUPS_UNLOCK();
2114
2115 /* if we found an entry to update, stop here */
2116 if (gm != NULL)
2117 return;
2118
2119 /* allocate a new record */
2120 MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
2121 if (gm != NULL) {
2122 gm->gm_uid = el->el_uid;
2123 gm->gm_gid = el->el_gid;
2124 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2125 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2126 } else {
2127 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2128 }
2129 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
2130 }
2131
2132 /*
2133 * Insert the new entry. Note that it's possible to race ourselves
2134 * here and end up with duplicate entries in the list. Wasteful, but
2135 * harmless since the first into the list will never be looked up,
2136 * and thus will eventually just fall off the end.
2137 */
2138 KAUTH_GROUPS_LOCK();
2139 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2140 if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
2141 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
2142 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2143 kauth_groups_count--;
2144 } else {
2145 gm = NULL;
2146 }
2147 KAUTH_GROUPS_UNLOCK();
2148
2149 /* free expired cache entry */
2150 if (gm != NULL)
2151 FREE(gm, M_KAUTH);
2152 }
2153
2154
2155 /*
2156 * Group membership KPI
2157 */
2158
2159 /*
2160 * kauth_cred_ismember_gid
2161 *
2162 * Description: Given a credential and a GID, determine if the GID is a member
2163 * of one of the supplementary groups associated with the given
2164 * credential
2165 *
2166 * Parameters: cred Credential to check in
2167 * gid GID to check for membership
2168 * resultp Pointer to int to contain the
2169 * result of the call
2170 *
2171 * Returns: 0 Success
2172 * ENOENT Could not proform lookup
2173 * kauth_resolver_submit:EWOULDBLOCK
2174 * kauth_resolver_submit:EINTR
2175 * kauth_resolver_submit:ENOMEM
2176 * kauth_resolver_submit:ENOENT User space daemon did not vend
2177 * this credential.
2178 * kauth_resolver_submit:??? Unlikely error from user space
2179 *
2180 * Implicit returns:
2181 * *resultp (modified) 1 Is member
2182 * 0 Is not member
2183 *
2184 * Notes: This function guarantees not to modify resultp when returning
2185 * an error.
2186 *
2187 * This function effectively checkes the EGID as well, since the
2188 * EGID is cr_groups[0] as an implementation detail.
2189 */
2190 int
2191 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
2192 {
2193 struct kauth_group_membership *gm;
2194 struct kauth_identity_extlookup el;
2195 int i, error;
2196
2197 /*
2198 * Check the per-credential list of override groups.
2199 *
2200 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2201 * the cache should be used for that case.
2202 */
2203 for (i = 0; i < cred->cr_ngroups; i++) {
2204 if (gid == cred->cr_groups[i]) {
2205 *resultp = 1;
2206 return(0);
2207 }
2208 }
2209
2210 /*
2211 * If we don't have a UID for group membership checks, the in-cred list
2212 * was authoritative and we can stop here.
2213 */
2214 if (cred->cr_gmuid == KAUTH_UID_NONE) {
2215 *resultp = 0;
2216 return(0);
2217 }
2218
2219
2220 /*
2221 * If the resolver hasn't checked in yet, we are early in the boot
2222 * phase and the local group list is complete and authoritative.
2223 */
2224 if (!kauth_resolver_registered) {
2225 *resultp = 0;
2226 return(0);
2227 }
2228
2229 /* TODO: */
2230 /* XXX check supplementary groups */
2231 /* XXX check whiteout groups */
2232 /* XXX nesting of supplementary/whiteout groups? */
2233
2234 /*
2235 * Check the group cache.
2236 */
2237 KAUTH_GROUPS_LOCK();
2238 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2239 if ((gm->gm_uid == cred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
2240 kauth_groups_lru(gm);
2241 break;
2242 }
2243 }
2244
2245 /* did we find a membership entry? */
2246 if (gm != NULL)
2247 *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
2248 KAUTH_GROUPS_UNLOCK();
2249
2250 /* if we did, we can return now */
2251 if (gm != NULL)
2252 return(0);
2253
2254 /* nothing in the cache, need to go to userland */
2255 bzero(&el, sizeof(el));
2256 el.el_info_pid = current_proc()->p_pid;
2257 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
2258 el.el_uid = cred->cr_gmuid;
2259 el.el_gid = gid;
2260 el.el_member_valid = 0; /* XXX set by resolver? */
2261 error = kauth_resolver_submit(&el);
2262 if (error != 0)
2263 return(error);
2264 /* save the results from the lookup */
2265 kauth_groups_updatecache(&el);
2266
2267 /* if we successfully ascertained membership, report */
2268 if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
2269 *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
2270 return(0);
2271 }
2272
2273 return(ENOENT);
2274 }
2275
2276
2277 /*
2278 * kauth_cred_ismember_guid
2279 *
2280 * Description: Determine whether the supplied credential is a member of the
2281 * group nominated by GUID.
2282 *
2283 * Parameters: cred Credential to check in
2284 * guidp Pointer to GUID whose group
2285 * we are testing for membership
2286 * resultp Pointer to int to contain the
2287 * result of the call
2288 *
2289 * Returns: 0 Success
2290 * kauth_cred_guid2gid:EINVAL
2291 * kauth_cred_ismember_gid:ENOENT
2292 * kauth_resolver_submit:ENOENT User space daemon did not vend
2293 * this credential.
2294 * kauth_cred_ismember_gid:EWOULDBLOCK
2295 * kauth_cred_ismember_gid:EINTR
2296 * kauth_cred_ismember_gid:ENOMEM
2297 * kauth_cred_ismember_gid:??? Unlikely error from user space
2298 *
2299 * Implicit returns:
2300 * *resultp (modified) 1 Is member
2301 * 0 Is not member
2302 */
2303 int
2304 kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
2305 {
2306 struct kauth_identity ki;
2307 gid_t gid;
2308 int error, wkg;
2309
2310 error = 0;
2311 wkg = kauth_wellknown_guid(guidp);
2312 switch(wkg) {
2313 case KAUTH_WKG_NOBODY:
2314 *resultp = 0;
2315 break;
2316 case KAUTH_WKG_EVERYBODY:
2317 *resultp = 1;
2318 break;
2319 default:
2320 #if 6603280
2321 /*
2322 * Grovel the identity cache looking for this GUID.
2323 * If we find it, and it is for a user record, return
2324 * false because it's not a group.
2325 *
2326 * This is necessary because we don't have -ve caching
2327 * of group memberships, and we really want to avoid
2328 * calling out to the resolver if at all possible.
2329 *
2330 * Because we're called by the ACL evaluator, and the
2331 * ACL evaluator is likely to encounter ACEs for users,
2332 * this is expected to be a common case.
2333 */
2334 ki.ki_valid = 0;
2335 if ((error = kauth_identity_find_guid(guidp, &ki)) == 0 &&
2336 !kauth_identity_guid_expired(&ki)) {
2337 if (ki.ki_valid & KI_VALID_GID) {
2338 /* It's a group after all... */
2339 gid = ki.ki_gid;
2340 goto do_check;
2341 }
2342 if (ki.ki_valid & KI_VALID_UID) {
2343 *resultp = 0;
2344 return (0);
2345 }
2346 }
2347 #endif /* 6603280 */
2348 /*
2349 * Attempt to translate the GUID to a GID. Even if
2350 * this fails, we will have primed the cache if it is
2351 * a user record and we'll see it above the next time
2352 * we're asked.
2353 */
2354 if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
2355 /*
2356 * If we have no guid -> gid translation, it's not a group and
2357 * thus the cred can't be a member.
2358 */
2359 if (error == ENOENT) {
2360 *resultp = 0;
2361 error = 0;
2362 }
2363 } else {
2364 do_check:
2365 error = kauth_cred_ismember_gid(cred, gid, resultp);
2366 }
2367 }
2368 return(error);
2369 }
2370
2371 /*
2372 * kauth_cred_gid_subset
2373 *
2374 * Description: Given two credentials, determine if all GIDs associated with
2375 * the first are also associated with the second
2376 *
2377 * Parameters: cred1 Credential to check for
2378 * cred2 Credential to check in
2379 * resultp Pointer to int to contain the
2380 * result of the call
2381 *
2382 * Returns: 0 Success
2383 * non-zero See kauth_cred_ismember_gid for
2384 * error codes
2385 *
2386 * Implicit returns:
2387 * *resultp (modified) 1 Is subset
2388 * 0 Is not subset
2389 *
2390 * Notes: This function guarantees not to modify resultp when returning
2391 * an error.
2392 */
2393 int
2394 kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
2395 {
2396 int i, err, res = 1;
2397 gid_t gid;
2398
2399 /* First, check the local list of groups */
2400 for (i = 0; i < cred1->cr_ngroups; i++) {
2401 gid = cred1->cr_groups[i];
2402 if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) {
2403 return err;
2404 }
2405
2406 if (!res && gid != cred2->cr_rgid && gid != cred2->cr_svgid) {
2407 *resultp = 0;
2408 return 0;
2409 }
2410 }
2411
2412 /* Check real gid */
2413 if ((err = kauth_cred_ismember_gid(cred2, cred1->cr_rgid, &res)) != 0) {
2414 return err;
2415 }
2416
2417 if (!res && cred1->cr_rgid != cred2->cr_rgid &&
2418 cred1->cr_rgid != cred2->cr_svgid) {
2419 *resultp = 0;
2420 return 0;
2421 }
2422
2423 /* Finally, check saved gid */
2424 if ((err = kauth_cred_ismember_gid(cred2, cred1->cr_svgid, &res)) != 0){
2425 return err;
2426 }
2427
2428 if (!res && cred1->cr_svgid != cred2->cr_rgid &&
2429 cred1->cr_svgid != cred2->cr_svgid) {
2430 *resultp = 0;
2431 return 0;
2432 }
2433
2434 *resultp = 1;
2435 return 0;
2436 }
2437
2438
2439 /*
2440 * kauth_cred_issuser
2441 *
2442 * Description: Fast replacement for issuser()
2443 *
2444 * Parameters: cred Credential to check for super
2445 * user privileges
2446 *
2447 * Returns: 0 Not super user
2448 * !0 Is super user
2449 *
2450 * Notes: This function uses a magic number which is not a manifest
2451 * constant; this is bad practice.
2452 */
2453 int
2454 kauth_cred_issuser(kauth_cred_t cred)
2455 {
2456 return(cred->cr_uid == 0);
2457 }
2458
2459
2460 /*
2461 * Credential KPI
2462 */
2463
2464 /* lock protecting credential hash table */
2465 static lck_mtx_t *kauth_cred_hash_mtx;
2466 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2467 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2468 #if KAUTH_CRED_HASH_DEBUG
2469 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2470 #else /* !KAUTH_CRED_HASH_DEBUG */
2471 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2472 #endif /* !KAUTH_CRED_HASH_DEBUG */
2473
2474
2475 /*
2476 * kauth_cred_init
2477 *
2478 * Description: Initialize the credential hash cache
2479 *
2480 * Parameters: (void)
2481 *
2482 * Returns: (void)
2483 *
2484 * Notes: Intialize the credential hash cache for use; the credential
2485 * hash cache is used convert duplicate credentials into a
2486 * single reference counted credential in order to save wired
2487 * kernel memory. In practice, this generally means a desktop
2488 * system runs with a few tens of credentials, instead of one
2489 * per process, one per thread, one per vnode cache entry, and
2490 * so on. This generally results in savings of 200K or more
2491 * (potentially much more on server systems).
2492 *
2493 * The hash cache internally has a reference on the credential
2494 * for itself as a means of avoiding a reclaim race for a
2495 * credential in the process of having it's last non-hash
2496 * reference released. This would otherwise result in the
2497 * possibility of a freed credential that was still in uses due
2498 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
2499 *
2500 * On final release, the hash reference is droped, and the
2501 * credential is freed back to the system.
2502 *
2503 * This function is called from kauth_init() in the file
2504 * kern_authorization.c.
2505 */
2506 void
2507 kauth_cred_init(void)
2508 {
2509 int i;
2510
2511 kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
2512 kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
2513
2514 /*allocate credential hash table */
2515 MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
2516 (sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size),
2517 M_KAUTH, M_WAITOK | M_ZERO);
2518 if (kauth_cred_table_anchor == NULL)
2519 panic("startup: kauth_cred_init");
2520 for (i = 0; i < kauth_cred_table_size; i++) {
2521 TAILQ_INIT(&kauth_cred_table_anchor[i]);
2522 }
2523 }
2524
2525
2526 /*
2527 * kauth_getuid
2528 *
2529 * Description: Get the current thread's effective UID.
2530 *
2531 * Parameters: (void)
2532 *
2533 * Returns: (uid_t) The effective UID of the
2534 * current thread
2535 */
2536 uid_t
2537 kauth_getuid(void)
2538 {
2539 return(kauth_cred_get()->cr_uid);
2540 }
2541
2542
2543 /*
2544 * kauth_getruid
2545 *
2546 * Description: Get the current thread's real UID.
2547 *
2548 * Parameters: (void)
2549 *
2550 * Returns: (uid_t) The real UID of the current
2551 * thread
2552 */
2553 uid_t
2554 kauth_getruid(void)
2555 {
2556 return(kauth_cred_get()->cr_ruid);
2557 }
2558
2559
2560 /*
2561 * kauth_getgid
2562 *
2563 * Description: Get the current thread's effective GID.
2564 *
2565 * Parameters: (void)
2566 *
2567 * Returns: (gid_t) The effective GID of the
2568 * current thread
2569 */
2570 gid_t
2571 kauth_getgid(void)
2572 {
2573 return(kauth_cred_get()->cr_groups[0]);
2574 }
2575
2576
2577 /*
2578 * kauth_getgid
2579 *
2580 * Description: Get the current thread's real GID.
2581 *
2582 * Parameters: (void)
2583 *
2584 * Returns: (gid_t) The real GID of the current
2585 * thread
2586 */
2587 gid_t
2588 kauth_getrgid(void)
2589 {
2590 return(kauth_cred_get()->cr_rgid);
2591 }
2592
2593
2594 /*
2595 * kauth_cred_get
2596 *
2597 * Description: Returns a pointer to the current thread's credential
2598 *
2599 * Parameters: (void)
2600 *
2601 * Returns: (kauth_cred_t) Pointer to the current thread's
2602 * credential
2603 *
2604 * Notes: This function does not take a reference; because of this, the
2605 * caller MUST NOT do anything that would let the thread's
2606 * credential change while using the returned value, without
2607 * first explicitly taking their own reference.
2608 *
2609 * If a caller intends to take a reference on the resulting
2610 * credential pointer from calling this function, it is strongly
2611 * recommended that the caller use kauth_cred_get_with_ref()
2612 * instead, to protect against any future changes to the cred
2613 * locking protocols; such changes could otherwise potentially
2614 * introduce race windows in the callers code.
2615 */
2616 kauth_cred_t
2617 kauth_cred_get(void)
2618 {
2619 struct proc *p;
2620 struct uthread *uthread;
2621
2622 uthread = get_bsdthread_info(current_thread());
2623 /* sanity */
2624 if (uthread == NULL)
2625 panic("thread wants credential but has no BSD thread info");
2626 /*
2627 * We can lazy-bind credentials to threads, as long as their processes
2628 * have them.
2629 *
2630 * XXX If we later inline this function, the code in this block
2631 * XXX should probably be called out in a function.
2632 */
2633 if (uthread->uu_ucred == NOCRED) {
2634 if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
2635 panic("thread wants credential but has no BSD process");
2636 uthread->uu_ucred = kauth_cred_proc_ref(p);
2637 }
2638 return(uthread->uu_ucred);
2639 }
2640
2641 void
2642 mach_kauth_cred_uthread_update(void)
2643 {
2644 uthread_t uthread;
2645 proc_t proc;
2646
2647 uthread = get_bsdthread_info(current_thread());
2648 proc = current_proc();
2649
2650 kauth_cred_uthread_update(uthread, proc);
2651 }
2652
2653 /*
2654 * kauth_cred_uthread_update
2655 *
2656 * Description: Given a uthread, a proc, and whether or not the proc is locked,
2657 * late-bind the uthread cred to the proc cred.
2658 *
2659 * Parameters: uthread_t The uthread to update
2660 * proc_t The process to update to
2661 *
2662 * Returns: (void)
2663 *
2664 * Notes: This code is common code called from system call or trap entry
2665 * in the case that the process thread may have been changed
2666 * since the last time the thread entered the kernel. It is
2667 * generally only called with the current uthread and process as
2668 * parameters.
2669 */
2670 void
2671 kauth_cred_uthread_update(uthread_t uthread, proc_t proc)
2672 {
2673 if (uthread->uu_ucred != proc->p_ucred &&
2674 (uthread->uu_flag & UT_SETUID) == 0) {
2675 kauth_cred_t old = uthread->uu_ucred;
2676 uthread->uu_ucred = kauth_cred_proc_ref(proc);
2677 if (IS_VALID_CRED(old))
2678 kauth_cred_unref(&old);
2679 }
2680 }
2681
2682
2683 /*
2684 * kauth_cred_get_with_ref
2685 *
2686 * Description: Takes a reference on the current thread's credential, and then
2687 * returns a pointer to it to the caller.
2688 *
2689 * Parameters: (void)
2690 *
2691 * Returns: (kauth_cred_t) Pointer to the current thread's
2692 * newly referenced credential
2693 *
2694 * Notes: This function takes a reference on the credential before
2695 * returning it to the caller.
2696 *
2697 * It is the responsibility of the calling code to release this
2698 * reference when the credential is no longer in use.
2699 *
2700 * Since the returned reference may be a persistent reference
2701 * (e.g. one cached in another data structure with a lifetime
2702 * longer than the calling function), this release may be delayed
2703 * until such time as the persistent reference is to be destroyed.
2704 * An example of this would be the per vnode credential cache used
2705 * to accelerate lookup operations.
2706 */
2707 kauth_cred_t
2708 kauth_cred_get_with_ref(void)
2709 {
2710 struct proc *procp;
2711 struct uthread *uthread;
2712
2713 uthread = get_bsdthread_info(current_thread());
2714 /* sanity checks */
2715 if (uthread == NULL)
2716 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
2717 if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
2718 panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
2719
2720 /*
2721 * We can lazy-bind credentials to threads, as long as their processes
2722 * have them.
2723 *
2724 * XXX If we later inline this function, the code in this block
2725 * XXX should probably be called out in a function.
2726 */
2727 if (uthread->uu_ucred == NOCRED) {
2728 /* take reference for new cred in thread */
2729 uthread->uu_ucred = kauth_cred_proc_ref(procp);
2730 }
2731 /* take a reference for our caller */
2732 kauth_cred_ref(uthread->uu_ucred);
2733 return(uthread->uu_ucred);
2734 }
2735
2736
2737 /*
2738 * kauth_cred_proc_ref
2739 *
2740 * Description: Takes a reference on the current process's credential, and
2741 * then returns a pointer to it to the caller.
2742 *
2743 * Parameters: procp Process whose credential we
2744 * intend to take a reference on
2745 *
2746 * Returns: (kauth_cred_t) Pointer to the process's
2747 * newly referenced credential
2748 *
2749 * Locks: PROC_LOCK is held before taking the reference and released
2750 * after the refeence is taken to protect the p_ucred field of
2751 * the process referred to by procp.
2752 *
2753 * Notes: This function takes a reference on the credential before
2754 * returning it to the caller.
2755 *
2756 * It is the responsibility of the calling code to release this
2757 * reference when the credential is no longer in use.
2758 *
2759 * Since the returned reference may be a persistent reference
2760 * (e.g. one cached in another data structure with a lifetime
2761 * longer than the calling function), this release may be delayed
2762 * until such time as the persistent reference is to be destroyed.
2763 * An example of this would be the per vnode credential cache used
2764 * to accelerate lookup operations.
2765 */
2766 kauth_cred_t
2767 kauth_cred_proc_ref(proc_t procp)
2768 {
2769 kauth_cred_t cred;
2770
2771 proc_lock(procp);
2772 cred = proc_ucred(procp);
2773 kauth_cred_ref(cred);
2774 proc_unlock(procp);
2775 return(cred);
2776 }
2777
2778
2779 /*
2780 * kauth_cred_alloc
2781 *
2782 * Description: Allocate a new credential
2783 *
2784 * Parameters: (void)
2785 *
2786 * Returns: !NULL Newly allocated credential
2787 * NULL Insufficient memory
2788 *
2789 * Notes: The newly allocated credential is zero'ed as part of the
2790 * allocation process, with the exception of the reference
2791 * count, which is set to 1 to indicate a single reference
2792 * held by the caller.
2793 *
2794 * Since newly allocated credentials have no external pointers
2795 * referencing them, prior to making them visible in an externally
2796 * visible pointer (e.g. by adding them to the credential hash
2797 * cache) is the only legal time in which an existing credential
2798 * can be safely iinitialized or modified directly.
2799 *
2800 * After initialization, the caller is expected to call the
2801 * function kauth_cred_add() to add the credential to the hash
2802 * cache, after which time it's frozen and becomes publically
2803 * visible.
2804 *
2805 * The release protocol depends on kauth_hash_add() being called
2806 * before kauth_cred_rele() (there is a diagnostic panic which
2807 * will trigger if this protocol is not observed).
2808 *
2809 * XXX: This function really ought to be static, rather than being
2810 * exported as KPI, since a failure of kauth_cred_add() can only
2811 * be handled by an explicit free of the credential; such frees
2812 * depend on knowlegdge of the allocation method used, which is
2813 * permitted to change between kernel revisions.
2814 *
2815 * XXX: In the insufficient resource case, this code panic's rather
2816 * than returning a NULL pointer; the code that calls this
2817 * function needs to be audited before this can be changed.
2818 */
2819 kauth_cred_t
2820 kauth_cred_alloc(void)
2821 {
2822 kauth_cred_t newcred;
2823
2824 MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK);
2825 if (newcred != 0) {
2826 bzero(newcred, sizeof(*newcred));
2827 newcred->cr_ref = 1;
2828 newcred->cr_audit.as_aia_p = &audit_default_aia;
2829 /* XXX the following will go away with cr_au */
2830 newcred->cr_au.ai_auid = AU_DEFAUDITID;
2831 /* must do this, or cred has same group membership as uid 0 */
2832 newcred->cr_gmuid = KAUTH_UID_NONE;
2833 #if CRED_DIAGNOSTIC
2834 } else {
2835 panic("kauth_cred_alloc: couldn't allocate credential");
2836 #endif
2837 }
2838
2839 #if KAUTH_CRED_HASH_DEBUG
2840 kauth_cred_count++;
2841 #endif
2842
2843 #if CONFIG_MACF
2844 mac_cred_label_init(newcred);
2845 #endif
2846
2847 return(newcred);
2848 }
2849
2850
2851 /*
2852 * kauth_cred_create
2853 *
2854 * Description: Look to see if we already have a known credential in the hash
2855 * cache; if one is found, bump the reference count and return
2856 * it. If there are no credentials that match the given
2857 * credential, then allocate a new credential.
2858 *
2859 * Parameters: cred Template for credential to
2860 * be created
2861 *
2862 * Returns: (kauth_cred_t) The credential that was found
2863 * in the hash or created
2864 * NULL kauth_cred_add() failed, or
2865 * there was not an egid specified
2866 *
2867 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
2868 * maintain this field, we can't expect callers to know how it
2869 * needs to be set. Callers should be prepared for this field
2870 * to be overwritten.
2871 *
2872 * XXX: This code will tight-loop if memory for a new credential is
2873 * persistently unavailable; this is perhaps not the wisest way
2874 * to handle this condition, but current callers do not expect
2875 * a failure.
2876 */
2877 kauth_cred_t
2878 kauth_cred_create(kauth_cred_t cred)
2879 {
2880 kauth_cred_t found_cred, new_cred = NULL;
2881 int is_member = 0;
2882
2883 KAUTH_CRED_HASH_LOCK_ASSERT();
2884
2885 if (cred->cr_flags & CRF_NOMEMBERD) {
2886 cred->cr_gmuid = KAUTH_UID_NONE;
2887 } else {
2888 /*
2889 * If the template credential is not opting out of external
2890 * group membership resolution, then we need to check that
2891 * the UID we will be using is resolvable by the external
2892 * resolver. If it's not, then we opt it out anyway, since
2893 * all future external resolution requests will be failing
2894 * anyway, and potentially taking a long time to do it. We
2895 * use gid 0 because we always know it will exist and not
2896 * trigger additional lookups. This is OK, because we end up
2897 * precatching the information here as a result.
2898 */
2899 if (!kauth_cred_ismember_gid(cred, 0, &is_member)) {
2900 /*
2901 * It's a recognized value; we don't really care about
2902 * the answer, so long as it's something the external
2903 * resolver could have vended.
2904 */
2905 cred->cr_gmuid = cred->cr_uid;
2906 } else {
2907 /*
2908 * It's not something the external resolver could
2909 * have vended, so we don't want to ask it more
2910 * questions about the credential in the future. This
2911 * speeds up future lookups, as long as the caller
2912 * caches results; otherwise, it the same recurring
2913 * cost. Since most credentials are used multiple
2914 * times, we still get some performance win from this.
2915 */
2916 cred->cr_gmuid = KAUTH_UID_NONE;
2917 cred->cr_flags |= CRF_NOMEMBERD;
2918 }
2919 }
2920
2921 /* Caller *must* specify at least the egid in cr_groups[0] */
2922 if (cred->cr_ngroups < 1)
2923 return(NULL);
2924
2925 for (;;) {
2926 KAUTH_CRED_HASH_LOCK();
2927 found_cred = kauth_cred_find(cred);
2928 if (found_cred != NULL) {
2929 /*
2930 * Found an existing credential so we'll bump
2931 * reference count and return
2932 */
2933 kauth_cred_ref(found_cred);
2934 KAUTH_CRED_HASH_UNLOCK();
2935 return(found_cred);
2936 }
2937 KAUTH_CRED_HASH_UNLOCK();
2938
2939 /*
2940 * No existing credential found. Create one and add it to
2941 * our hash table.
2942 */
2943 new_cred = kauth_cred_alloc();
2944 if (new_cred != NULL) {
2945 int err;
2946 new_cred->cr_uid = cred->cr_uid;
2947 new_cred->cr_ruid = cred->cr_ruid;
2948 new_cred->cr_svuid = cred->cr_svuid;
2949 new_cred->cr_rgid = cred->cr_rgid;
2950 new_cred->cr_svgid = cred->cr_svgid;
2951 new_cred->cr_gmuid = cred->cr_gmuid;
2952 new_cred->cr_ngroups = cred->cr_ngroups;
2953 bcopy(&cred->cr_groups[0], &new_cred->cr_groups[0], sizeof(new_cred->cr_groups));
2954 #if CONFIG_AUDIT
2955 bcopy(&cred->cr_audit, &new_cred->cr_audit,
2956 sizeof(new_cred->cr_audit));
2957 /* XXX the following bcopy() will go away with cr_au */
2958 bcopy(&cred->cr_au, &new_cred->cr_au,
2959 sizeof(new_cred->cr_au));
2960 #endif
2961 new_cred->cr_flags = cred->cr_flags;
2962
2963 KAUTH_CRED_HASH_LOCK();
2964 err = kauth_cred_add(new_cred);
2965 KAUTH_CRED_HASH_UNLOCK();
2966
2967 /* Retry if kauth_cred_add returns non zero value */
2968 if (err == 0)
2969 break;
2970 #if CONFIG_MACF
2971 mac_cred_label_destroy(new_cred);
2972 #endif
2973 AUDIT_SESSION_UNREF(new_cred);
2974
2975 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
2976 new_cred = NULL;
2977 }
2978 }
2979
2980 return(new_cred);
2981 }
2982
2983
2984 /*
2985 * kauth_cred_setresuid
2986 *
2987 * Description: Update the given credential using the UID arguments. The given
2988 * UIDs are used to set the effective UID, real UID, saved UID,
2989 * and GMUID (used for group membership checking).
2990 *
2991 * Parameters: cred The original credential
2992 * ruid The new real UID
2993 * euid The new effective UID
2994 * svuid The new saved UID
2995 * gmuid KAUTH_UID_NONE -or- the new
2996 * group membership UID
2997 *
2998 * Returns: (kauth_cred_t) The updated credential
2999 *
3000 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3001 * setting, so if you don't want it to change, pass it the
3002 * previous value, explicitly.
3003 *
3004 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3005 * if it returns a credential other than the one it is passed,
3006 * will have dropped the reference on the passed credential. All
3007 * callers should be aware of this, and treat this function as an
3008 * unref + ref, potentially on different credentials.
3009 *
3010 * Because of this, the caller is expected to take its own
3011 * reference on the credential passed as the first parameter,
3012 * and be prepared to release the reference on the credential
3013 * that is returned to them, if it is not intended to be a
3014 * persistent reference.
3015 */
3016 kauth_cred_t
3017 kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
3018 {
3019 struct ucred temp_cred;
3020
3021 NULLCRED_CHECK(cred);
3022
3023 /*
3024 * We don't need to do anything if the UIDs we are changing are
3025 * already the same as the UIDs passed in
3026 */
3027 if ((euid == KAUTH_UID_NONE || cred->cr_uid == euid) &&
3028 (ruid == KAUTH_UID_NONE || cred->cr_ruid == ruid) &&
3029 (svuid == KAUTH_UID_NONE || cred->cr_svuid == svuid) &&
3030 (cred->cr_gmuid == gmuid)) {
3031 /* no change needed */
3032 return(cred);
3033 }
3034
3035 /*
3036 * Look up in cred hash table to see if we have a matching credential
3037 * with the new values; this is done by calling kauth_cred_update().
3038 */
3039 bcopy(cred, &temp_cred, sizeof(temp_cred));
3040 if (euid != KAUTH_UID_NONE) {
3041 temp_cred.cr_uid = euid;
3042 }
3043 if (ruid != KAUTH_UID_NONE) {
3044 temp_cred.cr_ruid = ruid;
3045 }
3046 if (svuid != KAUTH_UID_NONE) {
3047 temp_cred.cr_svuid = svuid;
3048 }
3049
3050 /*
3051 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3052 * opt out of participation in external group resolution, unless we
3053 * unless we explicitly opt back in later.
3054 */
3055 if ((temp_cred.cr_gmuid = gmuid) == KAUTH_UID_NONE) {
3056 temp_cred.cr_flags |= CRF_NOMEMBERD;
3057 }
3058
3059 return(kauth_cred_update(cred, &temp_cred, TRUE));
3060 }
3061
3062
3063 /*
3064 * kauth_cred_setresgid
3065 *
3066 * Description: Update the given credential using the GID arguments. The given
3067 * GIDs are used to set the effective GID, real GID, and saved
3068 * GID.
3069 *
3070 * Parameters: cred The original credential
3071 * rgid The new real GID
3072 * egid The new effective GID
3073 * svgid The new saved GID
3074 *
3075 * Returns: (kauth_cred_t) The updated credential
3076 *
3077 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3078 * if it returns a credential other than the one it is passed,
3079 * will have dropped the reference on the passed credential. All
3080 * callers should be aware of this, and treat this function as an
3081 * unref + ref, potentially on different credentials.
3082 *
3083 * Because of this, the caller is expected to take its own
3084 * reference on the credential passed as the first parameter,
3085 * and be prepared to release the reference on the credential
3086 * that is returned to them, if it is not intended to be a
3087 * persistent reference.
3088 */
3089 kauth_cred_t
3090 kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
3091 {
3092 struct ucred temp_cred;
3093
3094 NULLCRED_CHECK(cred);
3095 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
3096
3097 /*
3098 * We don't need to do anything if the given GID are already the
3099 * same as the GIDs in the credential.
3100 */
3101 if (cred->cr_groups[0] == egid &&
3102 cred->cr_rgid == rgid &&
3103 cred->cr_svgid == svgid) {
3104 /* no change needed */
3105 return(cred);
3106 }
3107
3108 /*
3109 * Look up in cred hash table to see if we have a matching credential
3110 * with the new values; this is done by calling kauth_cred_update().
3111 */
3112 bcopy(cred, &temp_cred, sizeof(temp_cred));
3113 if (egid != KAUTH_GID_NONE) {
3114 /* displacing a supplementary group opts us out of memberd */
3115 if (kauth_cred_change_egid(&temp_cred, egid)) {
3116 DEBUG_CRED_CHANGE("displaced!\n");
3117 temp_cred.cr_flags |= CRF_NOMEMBERD;
3118 temp_cred.cr_gmuid = KAUTH_UID_NONE;
3119 } else {
3120 DEBUG_CRED_CHANGE("not displaced\n");
3121 }
3122 }
3123 if (rgid != KAUTH_GID_NONE) {
3124 temp_cred.cr_rgid = rgid;
3125 }
3126 if (svgid != KAUTH_GID_NONE) {
3127 temp_cred.cr_svgid = svgid;
3128 }
3129
3130 return(kauth_cred_update(cred, &temp_cred, TRUE));
3131 }
3132
3133
3134 /*
3135 * Update the given credential with the given groups. We only allocate a new
3136 * credential when the given gid actually results in changes to the existing
3137 * credential.
3138 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3139 * which will be used for group membership checking.
3140 */
3141 /*
3142 * kauth_cred_setgroups
3143 *
3144 * Description: Update the given credential using the provide supplementary
3145 * group list and group membership UID
3146 *
3147 * Parameters: cred The original credential
3148 * groups Pointer to gid_t array which
3149 * contains the new group list
3150 * groupcount The cound of valid groups which
3151 * are contained in 'groups'
3152 * gmuid KAUTH_UID_NONE -or- the new
3153 * group membership UID
3154 *
3155 * Returns: (kauth_cred_t) The updated credential
3156 *
3157 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3158 * setting, so if you don't want it to change, pass it the
3159 * previous value, explicitly.
3160 *
3161 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3162 * if it returns a credential other than the one it is passed,
3163 * will have dropped the reference on the passed credential. All
3164 * callers should be aware of this, and treat this function as an
3165 * unref + ref, potentially on different credentials.
3166 *
3167 * Because of this, the caller is expected to take its own
3168 * reference on the credential passed as the first parameter,
3169 * and be prepared to release the reference on the credential
3170 * that is returned to them, if it is not intended to be a
3171 * persistent reference.
3172 *
3173 * XXX: Changes are determined in ordinal order - if the caller pasess
3174 * in the same groups list that is already present in the
3175 * credential, but the members are in a different order, even if
3176 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3177 * is considered a modification to the credential, and a new
3178 * credential is created.
3179 *
3180 * This should perhaps be better optimized, but it is considered
3181 * to be the caller's problem.
3182 */
3183 kauth_cred_t
3184 kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
3185 {
3186 int i;
3187 struct ucred temp_cred;
3188
3189 NULLCRED_CHECK(cred);
3190
3191 /*
3192 * We don't need to do anything if the given list of groups does not
3193 * change.
3194 */
3195 if ((cred->cr_gmuid == gmuid) && (cred->cr_ngroups == groupcount)) {
3196 for (i = 0; i < groupcount; i++) {
3197 if (cred->cr_groups[i] != groups[i])
3198 break;
3199 }
3200 if (i == groupcount) {
3201 /* no change needed */
3202 return(cred);
3203 }
3204 }
3205
3206 /*
3207 * Look up in cred hash table to see if we have a matching credential
3208 * with new values. If we are setting or clearing the gmuid, then
3209 * update the cr_flags, since clearing it is sticky. This permits an
3210 * opt-out of memberd processing using setgroups(), and an opt-in
3211 * using initgroups(). This is required for POSIX conformance.
3212 */
3213 bcopy(cred, &temp_cred, sizeof(temp_cred));
3214 temp_cred.cr_ngroups = groupcount;
3215 bcopy(groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
3216 temp_cred.cr_gmuid = gmuid;
3217 if (gmuid == KAUTH_UID_NONE)
3218 temp_cred.cr_flags |= CRF_NOMEMBERD;
3219 else
3220 temp_cred.cr_flags &= ~CRF_NOMEMBERD;
3221
3222 return(kauth_cred_update(cred, &temp_cred, TRUE));
3223 }
3224
3225
3226 /*
3227 * kauth_cred_setuidgid
3228 *
3229 * Description: Update the given credential using the UID and GID arguments.
3230 * The given UID is used to set the effective UID, real UID, and
3231 * saved UID. The given GID is used to set the effective GID,
3232 * real GID, and saved GID.
3233 *
3234 * Parameters: cred The original credential
3235 * uid The new UID to use
3236 * gid The new GID to use
3237 *
3238 * Returns: (kauth_cred_t) The updated credential
3239 *
3240 * Notes: We set the gmuid to uid if the credential we are inheriting
3241 * from has not opted out of memberd participation; otherwise
3242 * we set it to KAUTH_UID_NONE
3243 *
3244 * This code is only ever called from the per-thread credential
3245 * code path in the "set per thread credential" case; and in
3246 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3247 * flag is set.
3248 *
3249 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3250 * if it returns a credential other than the one it is passed,
3251 * will have dropped the reference on the passed credential. All
3252 * callers should be aware of this, and treat this function as an
3253 * unref + ref, potentially on different credentials.
3254 *
3255 * Because of this, the caller is expected to take its own
3256 * reference on the credential passed as the first parameter,
3257 * and be prepared to release the reference on the credential
3258 * that is returned to them, if it is not intended to be a
3259 * persistent reference.
3260 */
3261 kauth_cred_t
3262 kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
3263 {
3264 struct ucred temp_cred;
3265
3266 NULLCRED_CHECK(cred);
3267
3268 /*
3269 * We don't need to do anything if the effective, real and saved
3270 * user IDs are already the same as the user ID passed into us.
3271 */
3272 if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid &&
3273 cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
3274 /* no change needed */
3275 return(cred);
3276 }
3277
3278 /*
3279 * Look up in cred hash table to see if we have a matching credential
3280 * with the new values.
3281 */
3282 bzero(&temp_cred, sizeof(temp_cred));
3283 temp_cred.cr_uid = uid;
3284 temp_cred.cr_ruid = uid;
3285 temp_cred.cr_svuid = uid;
3286 temp_cred.cr_flags = cred->cr_flags;
3287 /* inherit the opt-out of memberd */
3288 if (cred->cr_flags & CRF_NOMEMBERD) {
3289 temp_cred.cr_gmuid = KAUTH_UID_NONE;
3290 temp_cred.cr_flags |= CRF_NOMEMBERD;
3291 } else {
3292 temp_cred.cr_gmuid = uid;
3293 temp_cred.cr_flags &= ~CRF_NOMEMBERD;
3294 }
3295 temp_cred.cr_ngroups = 1;
3296 /* displacing a supplementary group opts us out of memberd */
3297 if (kauth_cred_change_egid(&temp_cred, gid)) {
3298 temp_cred.cr_gmuid = KAUTH_UID_NONE;
3299 temp_cred.cr_flags |= CRF_NOMEMBERD;
3300 }
3301 temp_cred.cr_rgid = gid;
3302 temp_cred.cr_svgid = gid;
3303 #if CONFIG_MACF
3304 temp_cred.cr_label = cred->cr_label;
3305 #endif
3306
3307 return(kauth_cred_update(cred, &temp_cred, TRUE));
3308 }
3309
3310
3311 /*
3312 * kauth_cred_setsvuidgid
3313 *
3314 * Description: Function used by execve to set the saved uid and gid values
3315 * for suid/sgid programs
3316 *
3317 * Parameters: cred The credential to update
3318 * uid The saved uid to set
3319 * gid The saved gid to set
3320 *
3321 * Returns: (kauth_cred_t) The updated credential
3322 *
3323 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3324 * if it returns a credential other than the one it is passed,
3325 * will have dropped the reference on the passed credential. All
3326 * callers should be aware of this, and treat this function as an
3327 * unref + ref, potentially on different credentials.
3328 *
3329 * Because of this, the caller is expected to take its own
3330 * reference on the credential passed as the first parameter,
3331 * and be prepared to release the reference on the credential
3332 * that is returned to them, if it is not intended to be a
3333 * persistent reference.
3334 */
3335 kauth_cred_t
3336 kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
3337 {
3338 struct ucred temp_cred;
3339
3340 NULLCRED_CHECK(cred);
3341 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
3342
3343 /*
3344 * We don't need to do anything if the effective, real and saved
3345 * uids are already the same as the uid provided. This check is
3346 * likely insufficient.
3347 */
3348 if (cred->cr_svuid == uid && cred->cr_svgid == gid) {
3349 /* no change needed */
3350 return(cred);
3351 }
3352 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3353
3354 /* look up in cred hash table to see if we have a matching credential
3355 * with new values.
3356 */
3357 bcopy(cred, &temp_cred, sizeof(temp_cred));
3358 temp_cred.cr_svuid = uid;
3359 temp_cred.cr_svgid = gid;
3360
3361 return(kauth_cred_update(cred, &temp_cred, TRUE));
3362 }
3363
3364
3365 /*
3366 * kauth_cred_setauditinfo
3367 *
3368 * Description: Update the given credential using the given au_session_t.
3369 *
3370 * Parameters: cred The original credential
3371 * auditinfo_p Pointer to ne audit information
3372 *
3373 * Returns: (kauth_cred_t) The updated credential
3374 *
3375 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3376 * if it returns a credential other than the one it is passed,
3377 * will have dropped the reference on the passed credential. All
3378 * callers should be aware of this, and treat this function as an
3379 * unref + ref, potentially on different credentials.
3380 *
3381 * Because of this, the caller is expected to take its own
3382 * reference on the credential passed as the first parameter,
3383 * and be prepared to release the reference on the credential
3384 * that is returned to them, if it is not intended to be a
3385 * persistent reference.
3386 */
3387 kauth_cred_t
3388 kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
3389 {
3390 struct ucred temp_cred;
3391
3392 NULLCRED_CHECK(cred);
3393
3394 /*
3395 * We don't need to do anything if the audit info is already the
3396 * same as the audit info in the credential provided.
3397 */
3398 if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) {
3399 /* no change needed */
3400 return(cred);
3401 }
3402
3403 bcopy(cred, &temp_cred, sizeof(temp_cred));
3404 bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
3405 /* XXX the following will go away with cr_au */
3406 temp_cred.cr_au.ai_auid = auditinfo_p->as_aia_p->ai_auid;
3407 temp_cred.cr_au.ai_mask.am_success =
3408 auditinfo_p->as_mask.am_success;
3409 temp_cred.cr_au.ai_mask.am_failure =
3410 auditinfo_p->as_mask.am_failure;
3411 temp_cred.cr_au.ai_termid.port =
3412 auditinfo_p->as_aia_p->ai_termid.at_port;
3413 temp_cred.cr_au.ai_termid.machine =
3414 auditinfo_p->as_aia_p->ai_termid.at_addr[0];
3415 temp_cred.cr_au.ai_asid = auditinfo_p->as_aia_p->ai_asid;
3416 /* XXX */
3417
3418 return(kauth_cred_update(cred, &temp_cred, FALSE));
3419 }
3420
3421 #if CONFIG_MACF
3422 /*
3423 * kauth_cred_label_update
3424 *
3425 * Description: Update the MAC label associated with a credential
3426 *
3427 * Parameters: cred The original credential
3428 * label The MAC label to set
3429 *
3430 * Returns: (kauth_cred_t) The updated credential
3431 *
3432 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3433 * if it returns a credential other than the one it is passed,
3434 * will have dropped the reference on the passed credential. All
3435 * callers should be aware of this, and treat this function as an
3436 * unref + ref, potentially on different credentials.
3437 *
3438 * Because of this, the caller is expected to take its own
3439 * reference on the credential passed as the first parameter,
3440 * and be prepared to release the reference on the credential
3441 * that is returned to them, if it is not intended to be a
3442 * persistent reference.
3443 */
3444 kauth_cred_t
3445 kauth_cred_label_update(kauth_cred_t cred, struct label *label)
3446 {
3447 kauth_cred_t newcred;
3448 struct ucred temp_cred;
3449
3450 bcopy(cred, &temp_cred, sizeof(temp_cred));
3451
3452 mac_cred_label_init(&temp_cred);
3453 mac_cred_label_associate(cred, &temp_cred);
3454 mac_cred_label_update(&temp_cred, label);
3455
3456 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
3457 mac_cred_label_destroy(&temp_cred);
3458 return (newcred);
3459 }
3460
3461 /*
3462 * kauth_cred_label_update_execve
3463 *
3464 * Description: Update the MAC label associated with a credential as
3465 * part of exec
3466 *
3467 * Parameters: cred The original credential
3468 * vp The exec vnode
3469 * scriptl The script MAC label
3470 * execl The executable MAC label
3471 * disjointp Pointer to flag to set if old
3472 * and returned credentials are
3473 * disjoint
3474 *
3475 * Returns: (kauth_cred_t) The updated credential
3476 *
3477 * Implicit returns:
3478 * *disjointp Set to 1 for disjoint creds
3479 *
3480 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3481 * if it returns a credential other than the one it is passed,
3482 * will have dropped the reference on the passed credential. All
3483 * callers should be aware of this, and treat this function as an
3484 * unref + ref, potentially on different credentials.
3485 *
3486 * Because of this, the caller is expected to take its own
3487 * reference on the credential passed as the first parameter,
3488 * and be prepared to release the reference on the credential
3489 * that is returned to them, if it is not intended to be a
3490 * persistent reference.
3491 */
3492 static
3493 kauth_cred_t
3494 kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
3495 struct vnode *vp, struct label *scriptl, struct label *execl,
3496 int *disjointp)
3497 {
3498 kauth_cred_t newcred;
3499 struct ucred temp_cred;
3500
3501 bcopy(cred, &temp_cred, sizeof(temp_cred));
3502
3503 mac_cred_label_init(&temp_cred);
3504 mac_cred_label_associate(cred, &temp_cred);
3505 *disjointp = mac_cred_label_update_execve(ctx, &temp_cred,
3506 vp, scriptl, execl);
3507
3508 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
3509 mac_cred_label_destroy(&temp_cred);
3510 return (newcred);
3511 }
3512
3513 /*
3514 * kauth_proc_label_update
3515 *
3516 * Description: Update the label inside the credential associated with the process.
3517 *
3518 * Parameters: p The process to modify
3519 * label The label to place in the process credential
3520 *
3521 * Notes: The credential associated with the process may change as a result
3522 * of this call. The caller should not assume the process reference to
3523 * the old credential still exists.
3524 */
3525 int kauth_proc_label_update(struct proc *p, struct label *label)
3526 {
3527 kauth_cred_t my_cred, my_new_cred;
3528
3529 my_cred = kauth_cred_proc_ref(p);
3530
3531 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
3532
3533 /* get current credential and take a reference while we muck with it */
3534 for (;;) {
3535
3536 /*
3537 * Set the credential with new info. If there is no change,
3538 * we get back the same credential we passed in; if there is
3539 * a change, we drop the reference on the credential we
3540 * passed in. The subsequent compare is safe, because it is
3541 * a pointer compare rather than a contents compare.
3542 */
3543 my_new_cred = kauth_cred_label_update(my_cred, label);
3544 if (my_cred != my_new_cred) {
3545
3546 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);
3547
3548 proc_lock(p);
3549 /*
3550 * We need to protect for a race where another thread
3551 * also changed the credential after we took our
3552 * reference. If p_ucred has changed then we should
3553 * restart this again with the new cred.
3554 */
3555 if (p->p_ucred != my_cred) {
3556 proc_unlock(p);
3557 kauth_cred_unref(&my_new_cred);
3558 my_cred = kauth_cred_proc_ref(p);
3559 /* try again */
3560 continue;
3561 }
3562 p->p_ucred = my_new_cred;
3563 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
3564 proc_unlock(p);
3565 }
3566 break;
3567 }
3568 /* Drop old proc reference or our extra reference */
3569 kauth_cred_unref(&my_cred);
3570
3571 return (0);
3572 }
3573
3574 /*
3575 * kauth_proc_label_update_execve
3576 *
3577 * Description: Update the label inside the credential associated with the
3578 * process as part of a transitioning execve. The label will
3579 * be updated by the policies as part of this processing, not
3580 * provided up front.
3581 *
3582 * Parameters: p The process to modify
3583 * ctx The context of the exec
3584 * vp The vnode being exec'ed
3585 * scriptl The script MAC label
3586 * execl The executable MAC label
3587 *
3588 * Returns: 0 Label update did not make credential
3589 * disjoint
3590 * 1 Label update caused credential to be
3591 * disjoint
3592 *
3593 * Notes: The credential associated with the process WILL change as a
3594 * result of this call. The caller should not assume the process
3595 * reference to the old credential still exists.
3596 */
3597 int
3598 kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
3599 struct vnode *vp, struct label *scriptl, struct label *execl)
3600 {
3601 kauth_cred_t my_cred, my_new_cred;
3602 int disjoint = 0;
3603
3604 my_cred = kauth_cred_proc_ref(p);
3605
3606 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
3607
3608 /* get current credential and take a reference while we muck with it */
3609 for (;;) {
3610
3611 /*
3612 * Set the credential with new info. If there is no change,
3613 * we get back the same credential we passed in; if there is
3614 * a change, we drop the reference on the credential we
3615 * passed in. The subsequent compare is safe, because it is
3616 * a pointer compare rather than a contents compare.
3617 */
3618 my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, scriptl, execl, &disjoint);
3619 if (my_cred != my_new_cred) {
3620
3621 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);
3622
3623 proc_lock(p);
3624 /*
3625 * We need to protect for a race where another thread
3626 * also changed the credential after we took our
3627 * reference. If p_ucred has changed then we should
3628 * restart this again with the new cred.
3629 */
3630 if (p->p_ucred != my_cred) {
3631 proc_unlock(p);
3632 kauth_cred_unref(&my_new_cred);
3633 my_cred = kauth_cred_proc_ref(p);
3634 /* try again */
3635 continue;
3636 }
3637 p->p_ucred = my_new_cred;
3638 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
3639 proc_unlock(p);
3640 }
3641 break;
3642 }
3643 /* Drop old proc reference or our extra reference */
3644 kauth_cred_unref(&my_cred);
3645
3646 return (disjoint);
3647 }
3648
3649 #if 1
3650 /*
3651 * for temporary binary compatibility
3652 */
3653 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
3654 kauth_cred_t
3655 kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
3656 {
3657 return kauth_cred_label_update(cred, label);
3658 }
3659
3660 int kauth_proc_setlabel(struct proc *p, struct label *label);
3661 int
3662 kauth_proc_setlabel(struct proc *p, struct label *label)
3663 {
3664 return kauth_proc_label_update(p, label);
3665 }
3666 #endif
3667
3668 #else
3669
3670 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
3671 * Since we cannot build our export lists based on the kernel configuration we need
3672 * to define a stub.
3673 */
3674 kauth_cred_t
3675 kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
3676 {
3677 return(NULL);
3678 }
3679
3680 int
3681 kauth_proc_label_update(__unused struct proc *p, __unused void *label)
3682 {
3683 return (0);
3684 }
3685
3686 #if 1
3687 /*
3688 * for temporary binary compatibility
3689 */
3690 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
3691 kauth_cred_t
3692 kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
3693 {
3694 return NULL;
3695 }
3696
3697 int kauth_proc_setlabel(struct proc *p, void *label);
3698 int
3699 kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
3700 {
3701 return (0);
3702 }
3703 #endif
3704 #endif
3705
3706 /*
3707 * kauth_cred_ref
3708 *
3709 * Description: Add a reference to the passed credential
3710 *
3711 * Parameters: cred The credential to reference
3712 *
3713 * Returns: (void)
3714 *
3715 * Notes: This function adds a reference to the provided credential;
3716 * the existing reference on the credential is assumed to be
3717 * held stable over this operation by taking the appropriate
3718 * lock to protect the pointer from which it is being referenced,
3719 * if necessary (e.g. the proc lock is held over the call if the
3720 * credential being referenced is from p_ucred, the vnode lock
3721 * if from the per vnode name cache cred cache, and so on).
3722 *
3723 * This is safe from the kauth_cred_unref() path, since an atomic
3724 * add is used, and the unref path specifically checks to see that
3725 * the value has not been changed to add a reference between the
3726 * time the credential is unreferenced by another pointer and the
3727 * time it is unreferenced from the cred hash cache.
3728 */
3729 void
3730 kauth_cred_ref(kauth_cred_t cred)
3731 {
3732 int old_value;
3733
3734 NULLCRED_CHECK(cred);
3735
3736 old_value = OSAddAtomicLong(1, (long*)&cred->cr_ref);
3737
3738 if (old_value < 1)
3739 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
3740
3741 #if 0 // use this to watch a specific credential
3742 if ( is_target_cred( cred ) != 0 ) {
3743 get_backtrace( );
3744 }
3745 #endif
3746
3747 return;
3748 }
3749
3750
3751 /*
3752 * kauth_cred_unref_hashlocked
3753 *
3754 * Description: release a credential reference; when the last reference is
3755 * released, the credential will be freed.
3756 *
3757 * Parameters: credp Pointer to address containing
3758 * credential to be freed
3759 *
3760 * Returns: (void)
3761 *
3762 * Implicit returns:
3763 * *credp Set to NOCRED
3764 *
3765 * Notes: This function assumes the credential hash lock is held.
3766 *
3767 * This function is internal use only, since the hash lock is
3768 * scoped to this compilation unit.
3769 *
3770 * This function destroys the contents of the pointer passed by
3771 * the caller to prevent the caller accidently attempting to
3772 * release a given reference twice in error.
3773 *
3774 * The last reference is considered to be released when a release
3775 * of a credential of a reference count of 2 occurs; this is an
3776 * intended effect, to take into accout the reference held by
3777 * the credential hash, which is released at the same time.
3778 */
3779 static void
3780 kauth_cred_unref_hashlocked(kauth_cred_t *credp)
3781 {
3782 int old_value;
3783
3784 KAUTH_CRED_HASH_LOCK_ASSERT();
3785 NULLCRED_CHECK(*credp);
3786
3787 old_value = OSAddAtomicLong(-1, (long*)&(*credp)->cr_ref);
3788
3789 #if DIAGNOSTIC
3790 if (old_value == 0)
3791 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
3792 if (old_value == 1)
3793 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
3794 #endif
3795
3796 #if 0 // use this to watch a specific credential
3797 if ( is_target_cred( *credp ) != 0 ) {
3798 get_backtrace( );
3799 }
3800 #endif
3801
3802 /*
3803 * If the old_value is 2, then we have just released the last external
3804 * reference to this credential
3805 */
3806 if (old_value < 3) {
3807 /* The last absolute reference is our credential hash table */
3808 kauth_cred_remove(*credp);
3809 }
3810 *credp = NOCRED;
3811 }
3812
3813
3814 /*
3815 * kauth_cred_unref
3816 *
3817 * Description: Release a credential reference while holding the credential
3818 * hash lock; when the last reference is released, the credential
3819 * will be freed.
3820 *
3821 * Parameters: credp Pointer to address containing
3822 * credential to be freed
3823 *
3824 * Returns: (void)
3825 *
3826 * Implicit returns:
3827 * *credp Set to NOCRED
3828 *
3829 * Notes: See kauth_cred_unref_hashlocked() for more information.
3830 *
3831 */
3832 void
3833 kauth_cred_unref(kauth_cred_t *credp)
3834 {
3835 KAUTH_CRED_HASH_LOCK();
3836 kauth_cred_unref_hashlocked(credp);
3837 KAUTH_CRED_HASH_UNLOCK();
3838 }
3839
3840
3841 #ifndef __LP64__
3842 /*
3843 * kauth_cred_rele
3844 *
3845 * Description: release a credential reference; when the last reference is
3846 * released, the credential will be freed
3847 *
3848 * Parameters: cred Credential to release
3849 *
3850 * Returns: (void)
3851 *
3852 * DEPRECATED: This interface is obsolete due to a failure to clear out the
3853 * clear the pointer in the caller to avoid multiple releases of
3854 * the same credential. The currently recommended interface is
3855 * kauth_cred_unref().
3856 */
3857 void
3858 kauth_cred_rele(kauth_cred_t cred)
3859 {
3860 kauth_cred_unref(&cred);
3861 }
3862 #endif /* !__LP64__ */
3863
3864
3865 /*
3866 * kauth_cred_dup
3867 *
3868 * Description: Duplicate a credential via alloc and copy; the new credential
3869 * has only it's own
3870 *
3871 * Parameters: cred The credential to duplicate
3872 *
3873 * Returns: (kauth_cred_t) The duplicate credential
3874 *
3875 * Notes: The typical value to calling this routine is if you are going
3876 * to modify an existing credential, and expect to need a new one
3877 * from the hash cache.
3878 *
3879 * This should probably not be used in the majority of cases;
3880 * if you are using it instead of kauth_cred_create(), you are
3881 * likely making a mistake.
3882 *
3883 * The newly allocated credential is copied as part of the
3884 * allocation process, with the exception of the reference
3885 * count, which is set to 1 to indicate a single reference
3886 * held by the caller.
3887 *
3888 * Since newly allocated credentials have no external pointers
3889 * referencing them, prior to making them visible in an externally
3890 * visible pointer (e.g. by adding them to the credential hash
3891 * cache) is the only legal time in which an existing credential
3892 * can be safely iinitialized or modified directly.
3893 *
3894 * After initialization, the caller is expected to call the
3895 * function kauth_cred_add() to add the credential to the hash
3896 * cache, after which time it's frozen and becomes publically
3897 * visible.
3898 *
3899 * The release protocol depends on kauth_hash_add() being called
3900 * before kauth_cred_rele() (there is a diagnostic panic which
3901 * will trigger if this protocol is not observed).
3902 *
3903 */
3904 kauth_cred_t
3905 kauth_cred_dup(kauth_cred_t cred)
3906 {
3907 kauth_cred_t newcred;
3908 #if CONFIG_MACF
3909 struct label *temp_label;
3910 #endif
3911
3912 #if CRED_DIAGNOSTIC
3913 if (cred == NOCRED || cred == FSCRED)
3914 panic("kauth_cred_dup: bad credential");
3915 #endif
3916 newcred = kauth_cred_alloc();
3917 if (newcred != NULL) {
3918 #if CONFIG_MACF
3919 temp_label = newcred->cr_label;
3920 #endif
3921 bcopy(cred, newcred, sizeof(*newcred));
3922 #if CONFIG_MACF
3923 newcred->cr_label = temp_label;
3924 mac_cred_label_associate(cred, newcred);
3925 #endif
3926 AUDIT_SESSION_REF(cred);
3927 newcred->cr_ref = 1;
3928 }
3929 return(newcred);
3930 }
3931
3932 /*
3933 * kauth_cred_copy_real
3934 *
3935 * Description: Returns a credential based on the passed credential but which
3936 * reflects the real rather than effective UID and GID.
3937 *
3938 * Parameters: cred The credential from which to
3939 * derive the new credential
3940 *
3941 * Returns: (kauth_cred_t) The copied credential
3942 *
3943 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
3944 * result, the caller is responsible for dropping BOTH the
3945 * additional reference on the passed cred (if any), and the
3946 * credential returned by this function. The drop should be
3947 * via the satnadr kauth_cred_unref() KPI.
3948 */
3949 kauth_cred_t
3950 kauth_cred_copy_real(kauth_cred_t cred)
3951 {
3952 kauth_cred_t newcred = NULL, found_cred;
3953 struct ucred temp_cred;
3954
3955 /* if the credential is already 'real', just take a reference */
3956 if ((cred->cr_ruid == cred->cr_uid) &&
3957 (cred->cr_rgid == cred->cr_gid)) {
3958 kauth_cred_ref(cred);
3959 return(cred);
3960 }
3961
3962 /*
3963 * Look up in cred hash table to see if we have a matching credential
3964 * with the new values.
3965 */
3966 bcopy(cred, &temp_cred, sizeof(temp_cred));
3967 temp_cred.cr_uid = cred->cr_ruid;
3968 /* displacing a supplementary group opts us out of memberd */
3969 if (kauth_cred_change_egid(&temp_cred, cred->cr_rgid)) {
3970 temp_cred.cr_flags |= CRF_NOMEMBERD;
3971 temp_cred.cr_gmuid = KAUTH_UID_NONE;
3972 }
3973 /*
3974 * If the cred is not opted out, make sure we are using the r/euid
3975 * for group checks
3976 */
3977 if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
3978 temp_cred.cr_gmuid = cred->cr_ruid;
3979
3980 for (;;) {
3981 int err;
3982
3983 KAUTH_CRED_HASH_LOCK();
3984 found_cred = kauth_cred_find(&temp_cred);
3985 if (found_cred == cred) {
3986 /* same cred so just bail */
3987 KAUTH_CRED_HASH_UNLOCK();
3988 return(cred);
3989 }
3990 if (found_cred != NULL) {
3991 /*
3992 * Found a match so we bump reference count on new
3993 * one. We leave the old one alone.
3994 */
3995 kauth_cred_ref(found_cred);
3996 KAUTH_CRED_HASH_UNLOCK();
3997 return(found_cred);
3998 }
3999
4000 /*
4001 * Must allocate a new credential, copy in old credential
4002 * data and update the real user and group IDs.
4003 */
4004 newcred = kauth_cred_dup(&temp_cred);
4005 err = kauth_cred_add(newcred);
4006 KAUTH_CRED_HASH_UNLOCK();
4007
4008 /* Retry if kauth_cred_add() fails */
4009 if (err == 0)
4010 break;
4011 #if CONFIG_MACF
4012 mac_cred_label_destroy(newcred);
4013 #endif
4014 AUDIT_SESSION_UNREF(newcred);
4015
4016 FREE_ZONE(newcred, sizeof(*newcred), M_CRED);
4017 newcred = NULL;
4018 }
4019
4020 return(newcred);
4021 }
4022
4023
4024 /*
4025 * kauth_cred_update
4026 *
4027 * Description: Common code to update a credential
4028 *
4029 * Parameters: old_cred Reference counted credential
4030 * to update
4031 * model_cred Non-reference counted model
4032 * credential to apply to the
4033 * credential to be updated
4034 * retain_auditinfo Flag as to whether or not the
4035 * audit information should be
4036 * copied from the old_cred into
4037 * the model_cred
4038 *
4039 * Returns: (kauth_cred_t) The updated credential
4040 *
4041 * IMPORTANT: This function will potentially return a credential other than
4042 * the one it is passed, and if so, it will have dropped the
4043 * reference on the passed credential. All callers should be
4044 * aware of this, and treat this function as an unref + ref,
4045 * potentially on different credentials.
4046 *
4047 * Because of this, the caller is expected to take its own
4048 * reference on the credential passed as the first parameter,
4049 * and be prepared to release the reference on the credential
4050 * that is returned to them, if it is not intended to be a
4051 * persistent reference.
4052 */
4053 static kauth_cred_t
4054 kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
4055 boolean_t retain_auditinfo)
4056 {
4057 kauth_cred_t found_cred, new_cred = NULL;
4058
4059 /*
4060 * Make sure we carry the auditinfo forward to the new credential
4061 * unless we are actually updating the auditinfo.
4062 */
4063 if (retain_auditinfo) {
4064 bcopy(&old_cred->cr_audit, &model_cred->cr_audit,
4065 sizeof(model_cred->cr_audit));
4066 /* XXX following bcopy will go away with cr_au */
4067 bcopy(&old_cred->cr_au, &model_cred->cr_au,
4068 sizeof(model_cred->cr_au));
4069 }
4070
4071 for (;;) {
4072 int err;
4073
4074 KAUTH_CRED_HASH_LOCK();
4075 found_cred = kauth_cred_find(model_cred);
4076 if (found_cred == old_cred) {
4077 /* same cred so just bail */
4078 KAUTH_CRED_HASH_UNLOCK();
4079 return(old_cred);
4080 }
4081 if (found_cred != NULL) {
4082 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
4083 /*
4084 * Found a match so we bump reference count on new
4085 * one and decrement reference count on the old one.
4086 */
4087 kauth_cred_ref(found_cred);
4088 kauth_cred_unref_hashlocked(&old_cred);
4089 KAUTH_CRED_HASH_UNLOCK();
4090 return(found_cred);
4091 }
4092
4093 /*
4094 * Must allocate a new credential using the model. also
4095 * adds the new credential to the credential hash table.
4096 */
4097 new_cred = kauth_cred_dup(model_cred);
4098 err = kauth_cred_add(new_cred);
4099 KAUTH_CRED_HASH_UNLOCK();
4100
4101 /* retry if kauth_cred_add returns non zero value */
4102 if (err == 0)
4103 break;
4104 #if CONFIG_MACF
4105 mac_cred_label_destroy(new_cred);
4106 #endif
4107 AUDIT_SESSION_UNREF(new_cred);
4108
4109 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
4110 new_cred = NULL;
4111 }
4112
4113 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred);
4114 kauth_cred_unref(&old_cred);
4115 return(new_cred);
4116 }
4117
4118
4119 /*
4120 * kauth_cred_add
4121 *
4122 * Description: Add the given credential to our credential hash table and
4123 * take an additional reference to account for our use of the
4124 * credential in the hash table
4125 *
4126 * Parameters: new_cred Credential to insert into cred
4127 * hash cache
4128 *
4129 * Returns: 0 Success
4130 * -1 Hash insertion failed: caller
4131 * should retry
4132 *
4133 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4134 *
4135 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4136 */
4137 static int
4138 kauth_cred_add(kauth_cred_t new_cred)
4139 {
4140 u_long hash_key;
4141
4142 KAUTH_CRED_HASH_LOCK_ASSERT();
4143
4144 hash_key = kauth_cred_get_hashkey(new_cred);
4145 hash_key %= kauth_cred_table_size;
4146
4147 /* race fix - there is a window where another matching credential
4148 * could have been inserted between the time this one was created and we
4149 * got the hash lock. If we find a match return an error and have the
4150 * the caller retry.
4151 */
4152 if (kauth_cred_find(new_cred) != NULL) {
4153 return(-1);
4154 }
4155
4156 /* take a reference for our use in credential hash table */
4157 kauth_cred_ref(new_cred);
4158
4159 /* insert the credential into the hash table */
4160 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
4161
4162 return(0);
4163 }
4164
4165
4166 /*
4167 * kauth_cred_remove
4168 *
4169 * Description: Remove the given credential from our credential hash table
4170 *
4171 * Parameters: cred Credential to remove from cred
4172 * hash cache
4173 *
4174 * Returns: (void)
4175 *
4176 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4177 *
4178 * Notes: The check for the reference increment after entry is generally
4179 * agree to be safe, since we use atomic operations, and the
4180 * following code occurs with the hash lock held; in theory, this
4181 * protects us from the 2->1 reference that gets us here.
4182 */
4183 static void
4184 kauth_cred_remove(kauth_cred_t cred)
4185 {
4186 u_long hash_key;
4187 kauth_cred_t found_cred;
4188
4189 hash_key = kauth_cred_get_hashkey(cred);
4190 hash_key %= kauth_cred_table_size;
4191
4192 /* Avoid race */
4193 if (cred->cr_ref < 1)
4194 panic("cred reference underflow");
4195 if (cred->cr_ref > 1)
4196 return; /* someone else got a ref */
4197
4198 /* Find cred in the credential hash table */
4199 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
4200 if (found_cred == cred) {
4201 /* found a match, remove it from the hash table */
4202 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
4203 #if CONFIG_MACF
4204 mac_cred_label_destroy(cred);
4205 #endif
4206 AUDIT_SESSION_UNREF(cred);
4207
4208 cred->cr_ref = 0;
4209 FREE_ZONE(cred, sizeof(*cred), M_CRED);
4210 #if KAUTH_CRED_HASH_DEBUG
4211 kauth_cred_count--;
4212 #endif
4213 return;
4214 }
4215 }
4216
4217 /* Did not find a match... this should not happen! XXX Make panic? */
4218 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
4219 return;
4220 }
4221
4222
4223 /*
4224 * kauth_cred_find
4225 *
4226 * Description: Using the given credential data, look for a match in our
4227 * credential hash table
4228 *
4229 * Parameters: cred Credential to lookup in cred
4230 * hash cache
4231 *
4232 * Returns: NULL Not found
4233 * !NULL Matching cedential already in
4234 * cred hash cache
4235 *
4236 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4237 */
4238 kauth_cred_t
4239 kauth_cred_find(kauth_cred_t cred)
4240 {
4241 u_long hash_key;
4242 kauth_cred_t found_cred;
4243
4244 KAUTH_CRED_HASH_LOCK_ASSERT();
4245
4246 #if KAUTH_CRED_HASH_DEBUG
4247 static int test_count = 0;
4248
4249 test_count++;
4250 if ((test_count % 200) == 0) {
4251 kauth_cred_hash_print();
4252 }
4253 #endif
4254
4255 hash_key = kauth_cred_get_hashkey(cred);
4256 hash_key %= kauth_cred_table_size;
4257
4258 /* Find cred in the credential hash table */
4259 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
4260 boolean_t match;
4261
4262 /*
4263 * don't worry about the label unless the flags in
4264 * either credential tell us to.
4265 */
4266 if ((found_cred->cr_flags & CRF_MAC_ENFORCE) != 0 ||
4267 (cred->cr_flags & CRF_MAC_ENFORCE) != 0) {
4268 /* include the label pointer in the compare */
4269 match = (bcmp(&found_cred->cr_uid, &cred->cr_uid,
4270 (sizeof(struct ucred) -
4271 offsetof(struct ucred, cr_uid))) == 0);
4272 } else {
4273 /* flags have to match, but skip the label in bcmp */
4274 match = (found_cred->cr_flags == cred->cr_flags &&
4275 bcmp(&found_cred->cr_uid, &cred->cr_uid,
4276 (offsetof(struct ucred, cr_label) -
4277 offsetof(struct ucred, cr_uid))) == 0);
4278 }
4279 if (match) {
4280 /* found a match */
4281 return(found_cred);
4282 }
4283 }
4284 /* No match found */
4285
4286 return(NULL);
4287 }
4288
4289
4290 /*
4291 * kauth_cred_hash
4292 *
4293 * Description: Generates a hash key using data that makes up a credential;
4294 * based on ElfHash
4295 *
4296 * Parameters: datap Pointer to data to hash
4297 * data_len Count of bytes to hash
4298 * start_key Start key value
4299 *
4300 * Returns: (u_long) Returned hash key
4301 */
4302 static inline u_long
4303 kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
4304 {
4305 u_long hash_key = start_key;
4306 u_long temp;
4307
4308 while (data_len > 0) {
4309 hash_key = (hash_key << 4) + *datap++;
4310 temp = hash_key & 0xF0000000;
4311 if (temp) {
4312 hash_key ^= temp >> 24;
4313 }
4314 hash_key &= ~temp;
4315 data_len--;
4316 }
4317 return(hash_key);
4318 }
4319
4320
4321 /*
4322 * kauth_cred_get_hashkey
4323 *
4324 * Description: Generate a hash key using data that makes up a credential;
4325 * based on ElfHash. We hash on the entire credential data,
4326 * not including the ref count or the TAILQ, which are mutable;
4327 * everything else isn't.
4328 *
4329 * We also avoid the label (if the flag is not set saying the
4330 * label is actually enforced).
4331 *
4332 * Parameters: cred Credential for which hash is
4333 * desired
4334 *
4335 * Returns: (u_long) Returned hash key
4336 */
4337 static u_long
4338 kauth_cred_get_hashkey(kauth_cred_t cred)
4339 {
4340 u_long hash_key = 0;
4341
4342 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid,
4343 ((cred->cr_flags & CRF_MAC_ENFORCE) ?
4344 sizeof(struct ucred) : offsetof(struct ucred, cr_label)) -
4345 offsetof(struct ucred, cr_uid),
4346 hash_key);
4347 return(hash_key);
4348 }
4349
4350
4351 #if KAUTH_CRED_HASH_DEBUG
4352 /*
4353 * kauth_cred_hash_print
4354 *
4355 * Description: Print out cred hash cache table information for debugging
4356 * purposes, including the credential contents
4357 *
4358 * Parameters: (void)
4359 *
4360 * Returns: (void)
4361 *
4362 * Implicit returns: Results in console output
4363 */
4364 static void
4365 kauth_cred_hash_print(void)
4366 {
4367 int i, j;
4368 kauth_cred_t found_cred;
4369
4370 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
4371 /* count slot hits, misses, collisions, and max depth */
4372 for (i = 0; i < kauth_cred_table_size; i++) {
4373 printf("[%02d] ", i);
4374 j = 0;
4375 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4376 if (j > 0) {
4377 printf("---- ");
4378 }
4379 j++;
4380 kauth_cred_print(found_cred);
4381 printf("\n");
4382 }
4383 if (j == 0) {
4384 printf("NOCRED \n");
4385 }
4386 }
4387 }
4388 #endif /* KAUTH_CRED_HASH_DEBUG */
4389
4390
4391 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4392 /*
4393 * kauth_cred_print
4394 *
4395 * Description: Print out an individual credential's contents for debugging
4396 * purposes
4397 *
4398 * Parameters: cred The credential to print out
4399 *
4400 * Returns: (void)
4401 *
4402 * Implicit returns: Results in console output
4403 */
4404 void
4405 kauth_cred_print(kauth_cred_t cred)
4406 {
4407 int i;
4408
4409 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);
4410 printf("group count %d gids ", cred->cr_ngroups);
4411 for (i = 0; i < NGROUPS; i++) {
4412 if (i == 0)
4413 printf("e");
4414 printf("%d ", cred->cr_groups[i]);
4415 }
4416 printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid);
4417 printf("auditinfo_addr %d %d %d %d %d %d\n",
4418 cred->cr_audit.s_aia_p->ai_auid,
4419 cred->cr_audit.as_mask.am_success,
4420 cred->cr_audit.as_mask.am_failure,
4421 cred->cr_audit.as_aia_p->ai_termid.at_port,
4422 cred->cr_audit.as_aia_p->ai_termid.at_addr[0],
4423 cred->cr_audit.as_aia_p->ai_asid);
4424 }
4425
4426 int is_target_cred( kauth_cred_t the_cred )
4427 {
4428 if ( the_cred->cr_uid != 0 )
4429 return( 0 );
4430 if ( the_cred->cr_ruid != 0 )
4431 return( 0 );
4432 if ( the_cred->cr_svuid != 0 )
4433 return( 0 );
4434 if ( the_cred->cr_ngroups != 11 )
4435 return( 0 );
4436 if ( the_cred->cr_groups[0] != 11 )
4437 return( 0 );
4438 if ( the_cred->cr_groups[1] != 81 )
4439 return( 0 );
4440 if ( the_cred->cr_groups[2] != 63947 )
4441 return( 0 );
4442 if ( the_cred->cr_groups[3] != 80288 )
4443 return( 0 );
4444 if ( the_cred->cr_groups[4] != 89006 )
4445 return( 0 );
4446 if ( the_cred->cr_groups[5] != 52173 )
4447 return( 0 );
4448 if ( the_cred->cr_groups[6] != 84524 )
4449 return( 0 );
4450 if ( the_cred->cr_groups[7] != 79 )
4451 return( 0 );
4452 if ( the_cred->cr_groups[8] != 80292 )
4453 return( 0 );
4454 if ( the_cred->cr_groups[9] != 80 )
4455 return( 0 );
4456 if ( the_cred->cr_groups[10] != 90824 )
4457 return( 0 );
4458 if ( the_cred->cr_rgid != 11 )
4459 return( 0 );
4460 if ( the_cred->cr_svgid != 11 )
4461 return( 0 );
4462 if ( the_cred->cr_gmuid != 3475 )
4463 return( 0 );
4464 if ( the_cred->cr_audit.as_aia_p->ai_auid != 3475 )
4465 return( 0 );
4466 /*
4467 if ( the_cred->cr_audit.as_mask.am_success != 0 )
4468 return( 0 );
4469 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
4470 return( 0 );
4471 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
4472 return( 0 );
4473 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
4474 return( 0 );
4475 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
4476 return( 0 );
4477 if ( the_cred->cr_flags != 0 )
4478 return( 0 );
4479 */
4480 return( -1 ); // found target cred
4481 }
4482
4483 void get_backtrace( void )
4484 {
4485 int my_slot;
4486 void * my_stack[ MAX_STACK_DEPTH ];
4487 int i, my_depth;
4488
4489 if ( cred_debug_buf_p == NULL ) {
4490 MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK);
4491 bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p));
4492 }
4493
4494 if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) {
4495 /* buffer is full */
4496 return;
4497 }
4498
4499 my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
4500 if ( my_depth == 0 ) {
4501 printf("%s - OSBacktrace failed \n", __FUNCTION__);
4502 return;
4503 }
4504
4505 /* fill new backtrace */
4506 my_slot = cred_debug_buf_p->next_slot;
4507 cred_debug_buf_p->next_slot++;
4508 cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
4509 for ( i = 0; i < my_depth; i++ ) {
4510 cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
4511 }
4512
4513 return;
4514 }
4515
4516
4517 /* subset of struct ucred for use in sysctl_dump_creds */
4518 struct debug_ucred {
4519 void *credp;
4520 u_long cr_ref; /* reference count */
4521 uid_t cr_uid; /* effective user id */
4522 uid_t cr_ruid; /* real user id */
4523 uid_t cr_svuid; /* saved user id */
4524 short cr_ngroups; /* number of groups in advisory list */
4525 gid_t cr_groups[NGROUPS]; /* advisory group list */
4526 gid_t cr_rgid; /* real group id */
4527 gid_t cr_svgid; /* saved group id */
4528 uid_t cr_gmuid; /* UID for group membership purposes */
4529 struct auditinfo_addr cr_audit; /* user auditing data. */
4530 void *cr_label; /* MACF label */
4531 int cr_flags; /* flags on credential */
4532 };
4533 typedef struct debug_ucred debug_ucred;
4534
4535 SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
4536 NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
4537
4538 /* accessed by:
4539 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
4540 */
4541
4542 static int
4543 sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
4544 {
4545 int i, j, counter = 0;
4546 int error;
4547 size_t space;
4548 kauth_cred_t found_cred;
4549 debug_ucred * cred_listp;
4550 debug_ucred * nextp;
4551
4552 /* This is a readonly node. */
4553 if (req->newptr != USER_ADDR_NULL)
4554 return (EPERM);
4555
4556 /* calculate space needed */
4557 for (i = 0; i < kauth_cred_table_size; i++) {
4558 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4559 counter++;
4560 }
4561 }
4562
4563 /* they are querying us so just return the space required. */
4564 if (req->oldptr == USER_ADDR_NULL) {
4565 counter += 10; // add in some padding;
4566 req->oldidx = counter * sizeof(debug_ucred);
4567 return 0;
4568 }
4569
4570 MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK );
4571 if ( cred_listp == NULL ) {
4572 return (ENOMEM);
4573 }
4574
4575 /* fill in creds to send back */
4576 nextp = cred_listp;
4577 space = 0;
4578 for (i = 0; i < kauth_cred_table_size; i++) {
4579 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4580 nextp->credp = found_cred;
4581 nextp->cr_ref = found_cred->cr_ref;
4582 nextp->cr_uid = found_cred->cr_uid;
4583 nextp->cr_ruid = found_cred->cr_ruid;
4584 nextp->cr_svuid = found_cred->cr_svuid;
4585 nextp->cr_ngroups = found_cred->cr_ngroups;
4586 for ( j = 0; j < nextp->cr_ngroups; j++ ) {
4587 nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
4588 }
4589 nextp->cr_rgid = found_cred->cr_rgid;
4590 nextp->cr_svgid = found_cred->cr_svgid;
4591 nextp->cr_gmuid = found_cred->cr_gmuid;
4592 nextp->cr_audit.ai_auid =
4593 found_cred->cr_audit.as_aia_p->ai_auid;
4594 nextp->cr_audit.ai_mask.am_success =
4595 found_cred->cr_audit.as_mask.am_success;
4596 nextp->cr_audit.ai_mask.am_failure =
4597 found_cred->cr_audit.as_mask.am_failure;
4598 nextp->cr_audit.ai_termid.at_port =
4599 found_cred->cr_audit.as_aia_p->ai_termid.at_port;
4600 nextp->cr_audit.ai_termid.at_type =
4601 found_cred->cr_audit.as_aia_p->ai_termid.at_type;
4602 nextp->cr_audit.ai_termid.at_addr[0] =
4603 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0];
4604 nextp->cr_audit.ai_termid.at_addr[1] =
4605 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1];
4606 nextp->cr_audit.ai_termid.at_addr[2] =
4607 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2];
4608 nextp->cr_audit.ai_termid.at_addr[3] =
4609 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3];
4610 nextp->cr_audit.ai_asid =
4611 found_cred->cr_audit.as_aia_p->ai_asid;
4612 nextp->cr_audit.ai_flags =
4613 found_cred->cr_audit.as_aia_p->ai_flags;
4614 nextp->cr_label = found_cred->cr_label;
4615 nextp->cr_flags = found_cred->cr_flags;
4616 nextp++;
4617 space += sizeof(debug_ucred);
4618 if ( space > req->oldlen ) {
4619 FREE(cred_listp, M_TEMP);
4620 return (ENOMEM);
4621 }
4622 }
4623 }
4624 req->oldlen = space;
4625 error = SYSCTL_OUT(req, cred_listp, req->oldlen);
4626 FREE(cred_listp, M_TEMP);
4627 return (error);
4628 }
4629
4630
4631 SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
4632 NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
4633
4634 /* accessed by:
4635 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
4636 */
4637
4638 static int
4639 sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
4640 {
4641 int i, j;
4642 int error;
4643 size_t space;
4644 cred_debug_buffer * bt_bufp;
4645 cred_backtrace * nextp;
4646
4647 /* This is a readonly node. */
4648 if (req->newptr != USER_ADDR_NULL)
4649 return (EPERM);
4650
4651 if ( cred_debug_buf_p == NULL ) {
4652 return (EAGAIN);
4653 }
4654
4655 /* calculate space needed */
4656 space = sizeof( cred_debug_buf_p->next_slot );
4657 space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
4658
4659 /* they are querying us so just return the space required. */
4660 if (req->oldptr == USER_ADDR_NULL) {
4661 req->oldidx = space;
4662 return 0;
4663 }
4664
4665 if ( space > req->oldlen ) {
4666 return (ENOMEM);
4667 }
4668
4669 MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK );
4670 if ( bt_bufp == NULL ) {
4671 return (ENOMEM);
4672 }
4673
4674 /* fill in backtrace info to send back */
4675 bt_bufp->next_slot = cred_debug_buf_p->next_slot;
4676 space = sizeof(bt_bufp->next_slot);
4677
4678 nextp = &bt_bufp->stack_buffer[ 0 ];
4679 for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
4680 nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
4681 for ( j = 0; j < nextp->depth; j++ ) {
4682 nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
4683 }
4684 space += sizeof(*nextp);
4685 nextp++;
4686 }
4687 req->oldlen = space;
4688 error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
4689 FREE(bt_bufp, M_TEMP);
4690 return (error);
4691 }
4692
4693 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */