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