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