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