]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_credential.c
xnu-1228.15.4.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 * disjointp Pointer to flag to set if old
3243 * and returned credentials are
3244 * disjoint
3245 *
3246 * Returns: (kauth_cred_t) The updated credential
3247 *
3248 * Implicit returns:
3249 * *disjointp Set to 1 for disjoint creds
3250 *
3251 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3252 * if it returns a credential other than the one it is passed,
3253 * will have dropped the reference on the passed credential. All
3254 * callers should be aware of this, and treat this function as an
3255 * unref + ref, potentially on different credentials.
3256 *
3257 * Because of this, the caller is expected to take its own
3258 * reference on the credential passed as the first parameter,
3259 * and be prepared to release the reference on the credential
3260 * that is returned to them, if it is not intended to be a
3261 * persistent reference.
3262 */
3263 static
3264 kauth_cred_t
3265 kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
3266 struct vnode *vp, struct label *scriptl, struct label *execl,
3267 int *disjointp)
3268 {
3269 kauth_cred_t newcred;
3270 struct ucred temp_cred;
3271
3272 bcopy(cred, &temp_cred, sizeof(temp_cred));
3273
3274 mac_cred_label_init(&temp_cred);
3275 mac_cred_label_associate(cred, &temp_cred);
3276 *disjointp = mac_cred_label_update_execve(ctx, &temp_cred,
3277 vp, scriptl, execl);
3278
3279 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
3280 mac_cred_label_destroy(&temp_cred);
3281 return (newcred);
3282 }
3283
3284 /*
3285 * kauth_proc_label_update
3286 *
3287 * Description: Update the label inside the credential associated with the process.
3288 *
3289 * Parameters: p The process to modify
3290 * label The label to place in the process credential
3291 *
3292 * Notes: The credential associated with the process may change as a result
3293 * of this call. The caller should not assume the process reference to
3294 * the old credential still exists.
3295 */
3296 int kauth_proc_label_update(struct proc *p, struct label *label)
3297 {
3298 kauth_cred_t my_cred, my_new_cred;
3299
3300 my_cred = kauth_cred_proc_ref(p);
3301
3302 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
3303
3304 /* get current credential and take a reference while we muck with it */
3305 for (;;) {
3306
3307 /*
3308 * Set the credential with new info. If there is no change,
3309 * we get back the same credential we passed in; if there is
3310 * a change, we drop the reference on the credential we
3311 * passed in. The subsequent compare is safe, because it is
3312 * a pointer compare rather than a contents compare.
3313 */
3314 my_new_cred = kauth_cred_label_update(my_cred, label);
3315 if (my_cred != my_new_cred) {
3316
3317 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);
3318
3319 proc_lock(p);
3320 /*
3321 * We need to protect for a race where another thread
3322 * also changed the credential after we took our
3323 * reference. If p_ucred has changed then we should
3324 * restart this again with the new cred.
3325 */
3326 if (p->p_ucred != my_cred) {
3327 proc_unlock(p);
3328 kauth_cred_unref(&my_new_cred);
3329 my_cred = kauth_cred_proc_ref(p);
3330 /* try again */
3331 continue;
3332 }
3333 p->p_ucred = my_new_cred;
3334 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
3335 proc_unlock(p);
3336 }
3337 break;
3338 }
3339 /* Drop old proc reference or our extra reference */
3340 kauth_cred_unref(&my_cred);
3341
3342 return (0);
3343 }
3344
3345 /*
3346 * kauth_proc_label_update_execve
3347 *
3348 * Description: Update the label inside the credential associated with the
3349 * process as part of a transitioning execve. The label will
3350 * be updated by the policies as part of this processing, not
3351 * provided up front.
3352 *
3353 * Parameters: p The process to modify
3354 * ctx The context of the exec
3355 * vp The vnode being exec'ed
3356 * scriptl The script MAC label
3357 * execl The executable MAC label
3358 *
3359 * Returns: 0 Label update did not make credential
3360 * disjoint
3361 * 1 Label update caused credential to be
3362 * disjoint
3363 *
3364 * Notes: The credential associated with the process WILL change as a
3365 * result of this call. The caller should not assume the process
3366 * reference to the old credential still exists.
3367 */
3368 int
3369 kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
3370 struct vnode *vp, struct label *scriptl, struct label *execl)
3371 {
3372 kauth_cred_t my_cred, my_new_cred;
3373 int disjoint = 0;
3374
3375 my_cred = kauth_cred_proc_ref(p);
3376
3377 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
3378
3379 /* get current credential and take a reference while we muck with it */
3380 for (;;) {
3381
3382 /*
3383 * Set the credential with new info. If there is no change,
3384 * we get back the same credential we passed in; if there is
3385 * a change, we drop the reference on the credential we
3386 * passed in. The subsequent compare is safe, because it is
3387 * a pointer compare rather than a contents compare.
3388 */
3389 my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, scriptl, execl, &disjoint);
3390 if (my_cred != my_new_cred) {
3391
3392 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);
3393
3394 proc_lock(p);
3395 /*
3396 * We need to protect for a race where another thread
3397 * also changed the credential after we took our
3398 * reference. If p_ucred has changed then we should
3399 * restart this again with the new cred.
3400 */
3401 if (p->p_ucred != my_cred) {
3402 proc_unlock(p);
3403 kauth_cred_unref(&my_new_cred);
3404 my_cred = kauth_cred_proc_ref(p);
3405 /* try again */
3406 continue;
3407 }
3408 p->p_ucred = my_new_cred;
3409 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
3410 proc_unlock(p);
3411 }
3412 break;
3413 }
3414 /* Drop old proc reference or our extra reference */
3415 kauth_cred_unref(&my_cred);
3416
3417 return (disjoint);
3418 }
3419
3420 #if 1
3421 /*
3422 * for temporary binary compatibility
3423 */
3424 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
3425 kauth_cred_t
3426 kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
3427 {
3428 return kauth_cred_label_update(cred, label);
3429 }
3430
3431 int kauth_proc_setlabel(struct proc *p, struct label *label);
3432 int
3433 kauth_proc_setlabel(struct proc *p, struct label *label)
3434 {
3435 return kauth_proc_label_update(p, label);
3436 }
3437 #endif
3438
3439 #else
3440
3441 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
3442 * Since we cannot build our export lists based on the kernel configuration we need
3443 * to define a stub.
3444 */
3445 kauth_cred_t
3446 kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
3447 {
3448 return(NULL);
3449 }
3450
3451 int
3452 kauth_proc_label_update(__unused struct proc *p, __unused void *label)
3453 {
3454 return (0);
3455 }
3456
3457 #if 1
3458 /*
3459 * for temporary binary compatibility
3460 */
3461 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
3462 kauth_cred_t
3463 kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
3464 {
3465 return NULL;
3466 }
3467
3468 int kauth_proc_setlabel(struct proc *p, void *label);
3469 int
3470 kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
3471 {
3472 return (0);
3473 }
3474 #endif
3475 #endif
3476
3477 /*
3478 * kauth_cred_ref
3479 *
3480 * Description: Add a reference to the passed credential
3481 *
3482 * Parameters: cred The credential to reference
3483 *
3484 * Returns: (void)
3485 *
3486 * Notes: This function adds a reference to the provided credential;
3487 * the existing reference on the credential is assumed to be
3488 * held stable over this operation by taking the appropriate
3489 * lock to protect the pointer from which it is being referenced,
3490 * if necessary (e.g. the proc lock is held over the call if the
3491 * credential being referenced is from p_ucred, the vnode lock
3492 * if from the per vnode name cache cred cache, and so on).
3493 *
3494 * This is safe from the kauth_cred_unref() path, since an atomic
3495 * add is used, and the unref path specifically checks to see that
3496 * the value has not been changed to add a reference between the
3497 * time the credential is unreferenced by another pointer and the
3498 * time it is unreferenced from the cred hash cache.
3499 */
3500 void
3501 kauth_cred_ref(kauth_cred_t cred)
3502 {
3503 int old_value;
3504
3505 NULLCRED_CHECK(cred);
3506
3507 // XXX SInt32 not safe for an LP64 kernel
3508 old_value = OSAddAtomic(1, (SInt32 *)&cred->cr_ref);
3509
3510 if (old_value < 1)
3511 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
3512
3513 #if 0 // use this to watch a specific credential
3514 if ( is_target_cred( cred ) != 0 ) {
3515 get_backtrace( );
3516 }
3517 #endif
3518
3519 return;
3520 }
3521
3522
3523 /*
3524 * kauth_cred_unref_hashlocked
3525 *
3526 * Description: release a credential reference; when the last reference is
3527 * released, the credential will be freed.
3528 *
3529 * Parameters: credp Pointer to address containing
3530 * credential to be freed
3531 *
3532 * Returns: (void)
3533 *
3534 * Implicit returns:
3535 * *credp Set to NOCRED
3536 *
3537 * Notes: This function assumes the credential hash lock is held.
3538 *
3539 * This function is internal use only, since the hash lock is
3540 * scoped to this compilation unit.
3541 *
3542 * This function destroys the contents of the pointer passed by
3543 * the caller to prevent the caller accidently attempting to
3544 * release a given reference twice in error.
3545 *
3546 * The last reference is considered to be released when a release
3547 * of a credential of a reference count of 2 occurs; this is an
3548 * intended effect, to take into accout the reference held by
3549 * the credential hash, which is released at the same time.
3550 */
3551 static void
3552 kauth_cred_unref_hashlocked(kauth_cred_t *credp)
3553 {
3554 int old_value;
3555
3556 KAUTH_CRED_HASH_LOCK_ASSERT();
3557 NULLCRED_CHECK(*credp);
3558
3559 // XXX SInt32 not safe for an LP64 kernel
3560 old_value = OSAddAtomic(-1, (SInt32 *)&(*credp)->cr_ref);
3561
3562 #if DIAGNOSTIC
3563 if (old_value == 0)
3564 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
3565 if (old_value == 1)
3566 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
3567 #endif
3568
3569 #if 0 // use this to watch a specific credential
3570 if ( is_target_cred( *credp ) != 0 ) {
3571 get_backtrace( );
3572 }
3573 #endif
3574
3575 /*
3576 * If the old_value is 2, then we have just released the last external
3577 * reference to this credential
3578 */
3579 if (old_value < 3) {
3580 /* The last absolute reference is our credential hash table */
3581 kauth_cred_remove(*credp);
3582 }
3583 *credp = NOCRED;
3584 }
3585
3586
3587 /*
3588 * kauth_cred_unref
3589 *
3590 * Description: Release a credential reference while holding the credential
3591 * hash lock; when the last reference is released, the credential
3592 * will be freed.
3593 *
3594 * Parameters: credp Pointer to address containing
3595 * credential to be freed
3596 *
3597 * Returns: (void)
3598 *
3599 * Implicit returns:
3600 * *credp Set to NOCRED
3601 *
3602 * Notes: See kauth_cred_unref_hashlocked() for more information.
3603 *
3604 */
3605 void
3606 kauth_cred_unref(kauth_cred_t *credp)
3607 {
3608 KAUTH_CRED_HASH_LOCK();
3609 kauth_cred_unref_hashlocked(credp);
3610 KAUTH_CRED_HASH_UNLOCK();
3611 }
3612
3613
3614 /*
3615 * kauth_cred_rele
3616 *
3617 * Description: release a credential reference; when the last reference is
3618 * released, the credential will be freed
3619 *
3620 * Parameters: cred Credential to release
3621 *
3622 * Returns: (void)
3623 *
3624 * DEPRECATED: This interface is obsolete due to a failure to clear out the
3625 * clear the pointer in the caller to avoid multiple releases of
3626 * the same credential. The currently recommended interface is
3627 * kauth_cred_unref().
3628 */
3629 void
3630 kauth_cred_rele(kauth_cred_t cred)
3631 {
3632 kauth_cred_unref(&cred);
3633 }
3634
3635
3636 /*
3637 * kauth_cred_dup
3638 *
3639 * Description: Duplicate a credential via alloc and copy; the new credential
3640 * has only it's own
3641 *
3642 * Parameters: cred The credential to duplicate
3643 *
3644 * Returns: (kauth_cred_t) The duplicate credential
3645 *
3646 * Notes: The typical value to calling this routine is if you are going
3647 * to modify an existing credential, and expect to need a new one
3648 * from the hash cache.
3649 *
3650 * This should probably not be used in the majority of cases;
3651 * if you are using it instead of kauth_cred_create(), you are
3652 * likely making a mistake.
3653 *
3654 * The newly allocated credential is copied as part of the
3655 * allocation process, with the exception of the reference
3656 * count, which is set to 1 to indicate a single reference
3657 * held by the caller.
3658 *
3659 * Since newly allocated credentials have no external pointers
3660 * referencing them, prior to making them visible in an externally
3661 * visible pointer (e.g. by adding them to the credential hash
3662 * cache) is the only legal time in which an existing credential
3663 * can be safely iinitialized or modified directly.
3664 *
3665 * After initialization, the caller is expected to call the
3666 * function kauth_cred_add() to add the credential to the hash
3667 * cache, after which time it's frozen and becomes publically
3668 * visible.
3669 *
3670 * The release protocol depends on kauth_hash_add() being called
3671 * before kauth_cred_rele() (there is a diagnostic panic which
3672 * will trigger if this protocol is not observed).
3673 *
3674 */
3675 kauth_cred_t
3676 kauth_cred_dup(kauth_cred_t cred)
3677 {
3678 kauth_cred_t newcred;
3679 #if CONFIG_MACF
3680 struct label *temp_label;
3681 #endif
3682
3683 #if CRED_DIAGNOSTIC
3684 if (cred == NOCRED || cred == FSCRED)
3685 panic("kauth_cred_dup: bad credential");
3686 #endif
3687 newcred = kauth_cred_alloc();
3688 if (newcred != NULL) {
3689 #if CONFIG_MACF
3690 temp_label = newcred->cr_label;
3691 #endif
3692 bcopy(cred, newcred, sizeof(*newcred));
3693 #if CONFIG_MACF
3694 newcred->cr_label = temp_label;
3695 mac_cred_label_associate(cred, newcred);
3696 #endif
3697 newcred->cr_ref = 1;
3698 }
3699 return(newcred);
3700 }
3701
3702 /*
3703 * kauth_cred_copy_real
3704 *
3705 * Description: Returns a credential based on the passed credential but which
3706 * reflects the real rather than effective UID and GID.
3707 *
3708 * Parameters: cred The credential from which to
3709 * derive the new credential
3710 *
3711 * Returns: (kauth_cred_t) The copied credential
3712 *
3713 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
3714 * result, the caller is responsible for dropping BOTH the
3715 * additional reference on the passed cred (if any), and the
3716 * credential returned by this function. The drop should be
3717 * via the satnadr kauth_cred_unref() KPI.
3718 */
3719 kauth_cred_t
3720 kauth_cred_copy_real(kauth_cred_t cred)
3721 {
3722 kauth_cred_t newcred = NULL, found_cred;
3723 struct ucred temp_cred;
3724
3725 /* if the credential is already 'real', just take a reference */
3726 if ((cred->cr_ruid == cred->cr_uid) &&
3727 (cred->cr_rgid == cred->cr_gid)) {
3728 kauth_cred_ref(cred);
3729 return(cred);
3730 }
3731
3732 /*
3733 * Look up in cred hash table to see if we have a matching credential
3734 * with the new values.
3735 */
3736 bcopy(cred, &temp_cred, sizeof(temp_cred));
3737 temp_cred.cr_uid = cred->cr_ruid;
3738 /* displacing a supplementary group opts us out of memberd */
3739 if (kauth_cred_change_egid(&temp_cred, cred->cr_rgid)) {
3740 temp_cred.cr_flags |= CRF_NOMEMBERD;
3741 temp_cred.cr_gmuid = KAUTH_UID_NONE;
3742 }
3743 /*
3744 * If the cred is not opted out, make sure we are using the r/euid
3745 * for group checks
3746 */
3747 if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
3748 temp_cred.cr_gmuid = cred->cr_ruid;
3749
3750 for (;;) {
3751 int err;
3752
3753 KAUTH_CRED_HASH_LOCK();
3754 found_cred = kauth_cred_find(&temp_cred);
3755 if (found_cred == cred) {
3756 /* same cred so just bail */
3757 KAUTH_CRED_HASH_UNLOCK();
3758 return(cred);
3759 }
3760 if (found_cred != NULL) {
3761 /*
3762 * Found a match so we bump reference count on new
3763 * one. We leave the old one alone.
3764 */
3765 kauth_cred_ref(found_cred);
3766 KAUTH_CRED_HASH_UNLOCK();
3767 return(found_cred);
3768 }
3769
3770 /*
3771 * Must allocate a new credential, copy in old credential
3772 * data and update the real user and group IDs.
3773 */
3774 newcred = kauth_cred_dup(&temp_cred);
3775 err = kauth_cred_add(newcred);
3776 KAUTH_CRED_HASH_UNLOCK();
3777
3778 /* Retry if kauth_cred_add() fails */
3779 if (err == 0)
3780 break;
3781 #if CONFIG_MACF
3782 mac_cred_label_destroy(newcred);
3783 #endif
3784 FREE_ZONE(newcred, sizeof(*newcred), M_CRED);
3785 newcred = NULL;
3786 }
3787
3788 return(newcred);
3789 }
3790
3791
3792 /*
3793 * kauth_cred_update
3794 *
3795 * Description: Common code to update a credential
3796 *
3797 * Parameters: old_cred Reference counted credential
3798 * to update
3799 * model_cred Non-reference counted model
3800 * credential to apply to the
3801 * credential to be updated
3802 * retain_auditinfo Flag as to whether or not the
3803 * audit information should be
3804 * copied from the old_cred into
3805 * the model_cred
3806 *
3807 * Returns: (kauth_cred_t) The updated credential
3808 *
3809 * IMPORTANT: This function will potentially return a credential other than
3810 * the one it is passed, and if so, it will have dropped the
3811 * reference on the passed credential. All callers should be
3812 * aware of this, and treat this function as an unref + ref,
3813 * potentially on different credentials.
3814 *
3815 * Because of this, the caller is expected to take its own
3816 * reference on the credential passed as the first parameter,
3817 * and be prepared to release the reference on the credential
3818 * that is returned to them, if it is not intended to be a
3819 * persistent reference.
3820 */
3821 static kauth_cred_t
3822 kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
3823 boolean_t retain_auditinfo)
3824 {
3825 kauth_cred_t found_cred, new_cred = NULL;
3826
3827 /*
3828 * Make sure we carry the auditinfo forward to the new credential
3829 * unless we are actually updating the auditinfo.
3830 */
3831 if (retain_auditinfo)
3832 bcopy(&old_cred->cr_au, &model_cred->cr_au, sizeof(model_cred->cr_au));
3833
3834 for (;;) {
3835 int err;
3836
3837 KAUTH_CRED_HASH_LOCK();
3838 found_cred = kauth_cred_find(model_cred);
3839 if (found_cred == old_cred) {
3840 /* same cred so just bail */
3841 KAUTH_CRED_HASH_UNLOCK();
3842 return(old_cred);
3843 }
3844 if (found_cred != NULL) {
3845 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
3846 /*
3847 * Found a match so we bump reference count on new
3848 * one and decrement reference count on the old one.
3849 */
3850 kauth_cred_ref(found_cred);
3851 kauth_cred_unref_hashlocked(&old_cred);
3852 KAUTH_CRED_HASH_UNLOCK();
3853 return(found_cred);
3854 }
3855
3856 /*
3857 * Must allocate a new credential using the model. also
3858 * adds the new credential to the credential hash table.
3859 */
3860 new_cred = kauth_cred_dup(model_cred);
3861 err = kauth_cred_add(new_cred);
3862 KAUTH_CRED_HASH_UNLOCK();
3863
3864 /* retry if kauth_cred_add returns non zero value */
3865 if (err == 0)
3866 break;
3867 #if CONFIG_MACF
3868 mac_cred_label_destroy(new_cred);
3869 #endif
3870 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
3871 new_cred = NULL;
3872 }
3873
3874 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred);
3875 kauth_cred_unref(&old_cred);
3876 return(new_cred);
3877 }
3878
3879
3880 /*
3881 * kauth_cred_add
3882 *
3883 * Description: Add the given credential to our credential hash table and
3884 * take an additional reference to account for our use of the
3885 * credential in the hash table
3886 *
3887 * Parameters: new_cred Credential to insert into cred
3888 * hash cache
3889 *
3890 * Returns: 0 Success
3891 * -1 Hash insertion failed: caller
3892 * should retry
3893 *
3894 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3895 *
3896 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
3897 */
3898 static int
3899 kauth_cred_add(kauth_cred_t new_cred)
3900 {
3901 u_long hash_key;
3902
3903 KAUTH_CRED_HASH_LOCK_ASSERT();
3904
3905 hash_key = kauth_cred_get_hashkey(new_cred);
3906 hash_key %= kauth_cred_table_size;
3907
3908 /* race fix - there is a window where another matching credential
3909 * could have been inserted between the time this one was created and we
3910 * got the hash lock. If we find a match return an error and have the
3911 * the caller retry.
3912 */
3913 if (kauth_cred_find(new_cred) != NULL) {
3914 return(-1);
3915 }
3916
3917 /* take a reference for our use in credential hash table */
3918 kauth_cred_ref(new_cred);
3919
3920 /* insert the credential into the hash table */
3921 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
3922
3923 return(0);
3924 }
3925
3926
3927 /*
3928 * kauth_cred_remove
3929 *
3930 * Description: Remove the given credential from our credential hash table
3931 *
3932 * Parameters: cred Credential to remove from cred
3933 * hash cache
3934 *
3935 * Returns: (void)
3936 *
3937 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3938 *
3939 * Notes: The check for the reference increment after entry is generally
3940 * agree to be safe, since we use atomic operations, and the
3941 * following code occurs with the hash lock held; in theory, this
3942 * protects us from the 2->1 reference that gets us here.
3943 */
3944 static void
3945 kauth_cred_remove(kauth_cred_t cred)
3946 {
3947 u_long hash_key;
3948 kauth_cred_t found_cred;
3949
3950 hash_key = kauth_cred_get_hashkey(cred);
3951 hash_key %= kauth_cred_table_size;
3952
3953 /* Avoid race */
3954 if (cred->cr_ref < 1)
3955 panic("cred reference underflow");
3956 if (cred->cr_ref > 1)
3957 return; /* someone else got a ref */
3958
3959 /* Find cred in the credential hash table */
3960 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
3961 if (found_cred == cred) {
3962 /* found a match, remove it from the hash table */
3963 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
3964 #if CONFIG_MACF
3965 mac_cred_label_destroy(cred);
3966 #endif
3967 cred->cr_ref = 0;
3968 FREE_ZONE(cred, sizeof(*cred), M_CRED);
3969 #if KAUTH_CRED_HASH_DEBUG
3970 kauth_cred_count--;
3971 #endif
3972 return;
3973 }
3974 }
3975
3976 /* Did not find a match... this should not happen! XXX Make panic? */
3977 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
3978 return;
3979 }
3980
3981
3982 /*
3983 * kauth_cred_find
3984 *
3985 * Description: Using the given credential data, look for a match in our
3986 * credential hash table
3987 *
3988 * Parameters: cred Credential to lookup in cred
3989 * hash cache
3990 *
3991 * Returns: NULL Not found
3992 * !NULL Matching cedential already in
3993 * cred hash cache
3994 *
3995 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
3996 */
3997 kauth_cred_t
3998 kauth_cred_find(kauth_cred_t cred)
3999 {
4000 u_long hash_key;
4001 kauth_cred_t found_cred;
4002
4003 KAUTH_CRED_HASH_LOCK_ASSERT();
4004
4005 #if KAUTH_CRED_HASH_DEBUG
4006 static int test_count = 0;
4007
4008 test_count++;
4009 if ((test_count % 200) == 0) {
4010 kauth_cred_hash_print();
4011 }
4012 #endif
4013
4014 hash_key = kauth_cred_get_hashkey(cred);
4015 hash_key %= kauth_cred_table_size;
4016
4017 /* Find cred in the credential hash table */
4018 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
4019 boolean_t match;
4020
4021 /*
4022 * don't worry about the label unless the flags in
4023 * either credential tell us to.
4024 */
4025 if ((found_cred->cr_flags & CRF_MAC_ENFORCE) != 0 ||
4026 (cred->cr_flags & CRF_MAC_ENFORCE) != 0) {
4027 /* include the label pointer in the compare */
4028 match = (bcmp(&found_cred->cr_uid, &cred->cr_uid,
4029 (sizeof(struct ucred) -
4030 offsetof(struct ucred, cr_uid))) == 0);
4031 } else {
4032 /* flags have to match, but skip the label in bcmp */
4033 match = (found_cred->cr_flags == cred->cr_flags &&
4034 bcmp(&found_cred->cr_uid, &cred->cr_uid,
4035 (offsetof(struct ucred, cr_label) -
4036 offsetof(struct ucred, cr_uid))) == 0);
4037 }
4038 if (match) {
4039 /* found a match */
4040 return(found_cred);
4041 }
4042 }
4043 /* No match found */
4044
4045 return(NULL);
4046 }
4047
4048
4049 /*
4050 * kauth_cred_hash
4051 *
4052 * Description: Generates a hash key using data that makes up a credential;
4053 * based on ElfHash
4054 *
4055 * Parameters: datap Pointer to data to hash
4056 * data_len Count of bytes to hash
4057 * start_key Start key value
4058 *
4059 * Returns: (u_long) Returned hash key
4060 */
4061 static inline u_long
4062 kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
4063 {
4064 u_long hash_key = start_key;
4065 u_long temp;
4066
4067 while (data_len > 0) {
4068 hash_key = (hash_key << 4) + *datap++;
4069 temp = hash_key & 0xF0000000;
4070 if (temp) {
4071 hash_key ^= temp >> 24;
4072 }
4073 hash_key &= ~temp;
4074 data_len--;
4075 }
4076 return(hash_key);
4077 }
4078
4079
4080 /*
4081 * kauth_cred_get_hashkey
4082 *
4083 * Description: Generate a hash key using data that makes up a credential;
4084 * based on ElfHash. We hash on the entire credential data,
4085 * not including the ref count or the TAILQ, which are mutable;
4086 * everything else isn't.
4087 *
4088 * We also avoid the label (if the flag is not set saying the
4089 * label is actually enforced).
4090 *
4091 * Parameters: cred Credential for which hash is
4092 * desired
4093 *
4094 * Returns: (u_long) Returned hash key
4095 */
4096 static u_long
4097 kauth_cred_get_hashkey(kauth_cred_t cred)
4098 {
4099 u_long hash_key = 0;
4100
4101 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid,
4102 ((cred->cr_flags & CRF_MAC_ENFORCE) ?
4103 sizeof(struct ucred) : offsetof(struct ucred, cr_label)) -
4104 offsetof(struct ucred, cr_uid),
4105 hash_key);
4106 return(hash_key);
4107 }
4108
4109
4110 #if KAUTH_CRED_HASH_DEBUG
4111 /*
4112 * kauth_cred_hash_print
4113 *
4114 * Description: Print out cred hash cache table information for debugging
4115 * purposes, including the credential contents
4116 *
4117 * Parameters: (void)
4118 *
4119 * Returns: (void)
4120 *
4121 * Implicit returns: Results in console output
4122 */
4123 static void
4124 kauth_cred_hash_print(void)
4125 {
4126 int i, j;
4127 kauth_cred_t found_cred;
4128
4129 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
4130 /* count slot hits, misses, collisions, and max depth */
4131 for (i = 0; i < kauth_cred_table_size; i++) {
4132 printf("[%02d] ", i);
4133 j = 0;
4134 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4135 if (j > 0) {
4136 printf("---- ");
4137 }
4138 j++;
4139 kauth_cred_print(found_cred);
4140 printf("\n");
4141 }
4142 if (j == 0) {
4143 printf("NOCRED \n");
4144 }
4145 }
4146 }
4147 #endif /* KAUTH_CRED_HASH_DEBUG */
4148
4149
4150 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4151 /*
4152 * kauth_cred_print
4153 *
4154 * Description: Print out an individual credential's contents for debugging
4155 * purposes
4156 *
4157 * Parameters: cred The credential to print out
4158 *
4159 * Returns: (void)
4160 *
4161 * Implicit returns: Results in console output
4162 */
4163 void
4164 kauth_cred_print(kauth_cred_t cred)
4165 {
4166 int i;
4167
4168 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);
4169 printf("group count %d gids ", cred->cr_ngroups);
4170 for (i = 0; i < NGROUPS; i++) {
4171 if (i == 0)
4172 printf("e");
4173 printf("%d ", cred->cr_groups[i]);
4174 }
4175 printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid);
4176 printf("auditinfo %d %d %d %d %d %d\n",
4177 cred->cr_au.ai_auid, cred->cr_au.ai_mask.am_success, cred->cr_au.ai_mask.am_failure,
4178 cred->cr_au.ai_termid.port, cred->cr_au.ai_termid.machine, cred->cr_au.ai_asid);
4179 }
4180
4181 int is_target_cred( kauth_cred_t the_cred )
4182 {
4183 if ( the_cred->cr_uid != 0 )
4184 return( 0 );
4185 if ( the_cred->cr_ruid != 0 )
4186 return( 0 );
4187 if ( the_cred->cr_svuid != 0 )
4188 return( 0 );
4189 if ( the_cred->cr_ngroups != 11 )
4190 return( 0 );
4191 if ( the_cred->cr_groups[0] != 11 )
4192 return( 0 );
4193 if ( the_cred->cr_groups[1] != 81 )
4194 return( 0 );
4195 if ( the_cred->cr_groups[2] != 63947 )
4196 return( 0 );
4197 if ( the_cred->cr_groups[3] != 80288 )
4198 return( 0 );
4199 if ( the_cred->cr_groups[4] != 89006 )
4200 return( 0 );
4201 if ( the_cred->cr_groups[5] != 52173 )
4202 return( 0 );
4203 if ( the_cred->cr_groups[6] != 84524 )
4204 return( 0 );
4205 if ( the_cred->cr_groups[7] != 79 )
4206 return( 0 );
4207 if ( the_cred->cr_groups[8] != 80292 )
4208 return( 0 );
4209 if ( the_cred->cr_groups[9] != 80 )
4210 return( 0 );
4211 if ( the_cred->cr_groups[10] != 90824 )
4212 return( 0 );
4213 if ( the_cred->cr_rgid != 11 )
4214 return( 0 );
4215 if ( the_cred->cr_svgid != 11 )
4216 return( 0 );
4217 if ( the_cred->cr_gmuid != 3475 )
4218 return( 0 );
4219 if ( the_cred->cr_au.ai_auid != 3475 )
4220 return( 0 );
4221 /*
4222 if ( the_cred->cr_au.ai_mask.am_success != 0 )
4223 return( 0 );
4224 if ( the_cred->cr_au.ai_mask.am_failure != 0 )
4225 return( 0 );
4226 if ( the_cred->cr_au.ai_termid.port != 0 )
4227 return( 0 );
4228 if ( the_cred->cr_au.ai_termid.machine != 0 )
4229 return( 0 );
4230 if ( the_cred->cr_au.ai_asid != 0 )
4231 return( 0 );
4232 if ( the_cred->cr_flags != 0 )
4233 return( 0 );
4234 */
4235 return( -1 ); // found target cred
4236 }
4237
4238 void get_backtrace( void )
4239 {
4240 int my_slot;
4241 void * my_stack[ MAX_STACK_DEPTH ];
4242 int i, my_depth;
4243
4244 if ( cred_debug_buf_p == NULL ) {
4245 MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK);
4246 bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p));
4247 }
4248
4249 if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) {
4250 /* buffer is full */
4251 return;
4252 }
4253
4254 my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
4255 if ( my_depth == 0 ) {
4256 printf("%s - OSBacktrace failed \n", __FUNCTION__);
4257 return;
4258 }
4259
4260 /* fill new backtrace */
4261 my_slot = cred_debug_buf_p->next_slot;
4262 cred_debug_buf_p->next_slot++;
4263 cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
4264 for ( i = 0; i < my_depth; i++ ) {
4265 cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
4266 }
4267
4268 return;
4269 }
4270
4271
4272 /* subset of struct ucred for use in sysctl_dump_creds */
4273 struct debug_ucred {
4274 void *credp;
4275 u_long cr_ref; /* reference count */
4276 uid_t cr_uid; /* effective user id */
4277 uid_t cr_ruid; /* real user id */
4278 uid_t cr_svuid; /* saved user id */
4279 short cr_ngroups; /* number of groups in advisory list */
4280 gid_t cr_groups[NGROUPS]; /* advisory group list */
4281 gid_t cr_rgid; /* real group id */
4282 gid_t cr_svgid; /* saved group id */
4283 uid_t cr_gmuid; /* UID for group membership purposes */
4284 struct auditinfo cr_au; /* user auditing data */
4285 void *cr_label; /* MACF label */
4286 int cr_flags; /* flags on credential */
4287 };
4288 typedef struct debug_ucred debug_ucred;
4289
4290 SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
4291 NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
4292
4293 /* accessed by:
4294 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
4295 */
4296
4297 static int
4298 sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
4299 {
4300 int i, j, counter = 0;
4301 int error;
4302 size_t space;
4303 kauth_cred_t found_cred;
4304 debug_ucred * cred_listp;
4305 debug_ucred * nextp;
4306
4307 /* This is a readonly node. */
4308 if (req->newptr != USER_ADDR_NULL)
4309 return (EPERM);
4310
4311 /* calculate space needed */
4312 for (i = 0; i < kauth_cred_table_size; i++) {
4313 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4314 counter++;
4315 }
4316 }
4317
4318 /* they are querying us so just return the space required. */
4319 if (req->oldptr == USER_ADDR_NULL) {
4320 counter += 10; // add in some padding;
4321 req->oldidx = counter * sizeof(debug_ucred);
4322 return 0;
4323 }
4324
4325 MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK );
4326 if ( cred_listp == NULL ) {
4327 return (ENOMEM);
4328 }
4329
4330 /* fill in creds to send back */
4331 nextp = cred_listp;
4332 space = 0;
4333 for (i = 0; i < kauth_cred_table_size; i++) {
4334 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4335 nextp->credp = found_cred;
4336 nextp->cr_ref = found_cred->cr_ref;
4337 nextp->cr_uid = found_cred->cr_uid;
4338 nextp->cr_ruid = found_cred->cr_ruid;
4339 nextp->cr_svuid = found_cred->cr_svuid;
4340 nextp->cr_ngroups = found_cred->cr_ngroups;
4341 for ( j = 0; j < nextp->cr_ngroups; j++ ) {
4342 nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
4343 }
4344 nextp->cr_rgid = found_cred->cr_rgid;
4345 nextp->cr_svgid = found_cred->cr_svgid;
4346 nextp->cr_gmuid = found_cred->cr_gmuid;
4347 nextp->cr_au.ai_auid = found_cred->cr_au.ai_auid;
4348 nextp->cr_au.ai_mask.am_success = found_cred->cr_au.ai_mask.am_success;
4349 nextp->cr_au.ai_mask.am_failure = found_cred->cr_au.ai_mask.am_failure;
4350 nextp->cr_au.ai_termid.port = found_cred->cr_au.ai_termid.port;
4351 nextp->cr_au.ai_termid.machine = found_cred->cr_au.ai_termid.machine;
4352 nextp->cr_au.ai_asid = found_cred->cr_au.ai_asid;
4353 nextp->cr_label = found_cred->cr_label;
4354 nextp->cr_flags = found_cred->cr_flags;
4355 nextp++;
4356 space += sizeof(debug_ucred);
4357 if ( space > req->oldlen ) {
4358 FREE(cred_listp, M_TEMP);
4359 return (ENOMEM);
4360 }
4361 }
4362 }
4363 req->oldlen = space;
4364 error = SYSCTL_OUT(req, cred_listp, req->oldlen);
4365 FREE(cred_listp, M_TEMP);
4366 return (error);
4367 }
4368
4369
4370 SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
4371 NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
4372
4373 /* accessed by:
4374 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
4375 */
4376
4377 static int
4378 sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
4379 {
4380 int i, j;
4381 int error;
4382 size_t space;
4383 cred_debug_buffer * bt_bufp;
4384 cred_backtrace * nextp;
4385
4386 /* This is a readonly node. */
4387 if (req->newptr != USER_ADDR_NULL)
4388 return (EPERM);
4389
4390 if ( cred_debug_buf_p == NULL ) {
4391 return (EAGAIN);
4392 }
4393
4394 /* calculate space needed */
4395 space = sizeof( cred_debug_buf_p->next_slot );
4396 space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
4397
4398 /* they are querying us so just return the space required. */
4399 if (req->oldptr == USER_ADDR_NULL) {
4400 req->oldidx = space;
4401 return 0;
4402 }
4403
4404 if ( space > req->oldlen ) {
4405 return (ENOMEM);
4406 }
4407
4408 MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK );
4409 if ( bt_bufp == NULL ) {
4410 return (ENOMEM);
4411 }
4412
4413 /* fill in backtrace info to send back */
4414 bt_bufp->next_slot = cred_debug_buf_p->next_slot;
4415 space = sizeof(bt_bufp->next_slot);
4416
4417 nextp = &bt_bufp->stack_buffer[ 0 ];
4418 for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
4419 nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
4420 for ( j = 0; j < nextp->depth; j++ ) {
4421 nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
4422 }
4423 space += sizeof(*nextp);
4424 nextp++;
4425 }
4426 req->oldlen = space;
4427 error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
4428 FREE(bt_bufp, M_TEMP);
4429 return (error);
4430 }
4431
4432 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */