]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_credential.c
xnu-1699.26.8.tar.gz
[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 /* do we have a translation? */
2282 if (ki.ki_valid & to) {
2283 /* found a valid cached entry, check expiry */
2284 switch(to) {
2285 case KI_VALID_GUID:
2286 expired = kauth_identity_guid_expired;
2287 break;
2288 case KI_VALID_NTSID:
2289 expired = kauth_identity_ntsid_expired;
2290 break;
2291 default:
2292 switch(from) {
2293 case KI_VALID_GUID:
2294 expired = kauth_identity_guid_expired;
2295 break;
2296 case KI_VALID_NTSID:
2297 expired = kauth_identity_ntsid_expired;
2298 break;
2299 default:
2300 expired = NULL;
2301 }
2302 }
2303 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki.ki_valid);
2304 /*
2305 * If no expiry function, or not expired, we have found
2306 * a hit.
2307 */
2308 if (!expired) {
2309 KAUTH_DEBUG("CACHE - no expiry function");
2310 goto found;
2311 }
2312 if (!expired(&ki)) {
2313 KAUTH_DEBUG("CACHE - entry valid, unexpired");
2314 goto found;
2315 }
2316 /*
2317 * We leave ki_valid set here; it contains a
2318 * translation but the TTL has expired. If we can't
2319 * get a result from the resolver, we will use it as
2320 * a better-than nothing alternative.
2321 */
2322 KAUTH_DEBUG("CACHE - expired entry found");
2323 } else {
2324 /*
2325 * A guid can't both match a uid and a gid, so if we
2326 * found a cache entry while looking for one or the
2327 * other from a guid, the 'from' is KI_VALID_GUID,
2328 * and the 'to' is one, and the other one is valid,
2329 * then we immediately return ENOENT without calling
2330 * the resolver again.
2331 */
2332 if (from == KI_VALID_GUID &&
2333 (((ki.ki_valid & KI_VALID_UID) &&
2334 to == KI_VALID_GID) ||
2335 ((ki.ki_valid & KI_VALID_GID) &&
2336 to == KI_VALID_UID))) {
2337 return (ENOENT);
2338 }
2339 }
2340 }
2341
2342 /*
2343 * We failed to find a cache entry; call the resolver.
2344 *
2345 * Note: We ask for as much non-extended data as we can get,
2346 * and only provide (or ask for) extended information if
2347 * we have a 'from' (or 'to') which requires it. This
2348 * way we don't pay for the extra transfer overhead for
2349 * data we don't need.
2350 */
2351 bzero(&el, sizeof(el));
2352 el.el_info_pid = current_proc()->p_pid;
2353 switch(from) {
2354 case KI_VALID_UID:
2355 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
2356 el.el_uid = *(uid_t *)src;
2357 break;
2358 case KI_VALID_GID:
2359 el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
2360 el.el_gid = *(gid_t *)src;
2361 break;
2362 case KI_VALID_GUID:
2363 el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
2364 el.el_uguid = *(guid_t *)src;
2365 el.el_gguid = *(guid_t *)src;
2366 break;
2367 case KI_VALID_NTSID:
2368 el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
2369 el.el_usid = *(ntsid_t *)src;
2370 el.el_gsid = *(ntsid_t *)src;
2371 break;
2372 case KI_VALID_PWNAM:
2373 /* extra overhead */
2374 el.el_flags = KAUTH_EXTLOOKUP_VALID_PWNAM;
2375 extend_data = CAST_USER_ADDR_T(src);
2376 break;
2377 case KI_VALID_GRNAM:
2378 /* extra overhead */
2379 el.el_flags = KAUTH_EXTLOOKUP_VALID_GRNAM;
2380 extend_data = CAST_USER_ADDR_T(src);
2381 break;
2382 default:
2383 return(EINVAL);
2384 }
2385 /*
2386 * Here we ask for everything all at once, to avoid having to work
2387 * out what we really want now, or might want soon.
2388 *
2389 * Asking for SID translations when we don't know we need them right
2390 * now is going to cause excess work to be done if we're connected
2391 * to a network that thinks it can translate them. This list needs
2392 * to get smaller/smarter.
2393 */
2394 el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
2395 KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
2396 KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
2397 if (to == KI_VALID_PWNAM) {
2398 /* extra overhead */
2399 el.el_flags |= KAUTH_EXTLOOKUP_WANT_PWNAM;
2400 extend_data = CAST_USER_ADDR_T(dst);
2401 }
2402 if (to == KI_VALID_GRNAM) {
2403 /* extra overhead */
2404 el.el_flags |= KAUTH_EXTLOOKUP_WANT_GRNAM;
2405 extend_data = CAST_USER_ADDR_T(dst);
2406 }
2407
2408 /* Call resolver */
2409 KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
2410 error = kauth_resolver_submit(&el, extend_data);
2411 KAUTH_DEBUG("CACHE - resolver returned %d", error);
2412
2413 /* was the external lookup successful? */
2414 if (error == 0) {
2415 /*
2416 * Save the results from the lookup - we may have other
2417 * information, even if we didn't get a guid or the
2418 * extended data.
2419 *
2420 * If we came from a name, we know the extend_data is valid.
2421 */
2422 if (from == KI_VALID_PWNAM)
2423 el.el_flags |= KAUTH_EXTLOOKUP_VALID_PWNAM;
2424 else if (from == KI_VALID_GRNAM)
2425 el.el_flags |= KAUTH_EXTLOOKUP_VALID_GRNAM;
2426
2427 kauth_identity_updatecache(&el, &ki, extend_data);
2428
2429 /*
2430 * Check to see if we have a valid cache entry
2431 * originating from the result.
2432 */
2433 if (!(ki.ki_valid & to)) {
2434 error = ENOENT;
2435 }
2436 }
2437 if (error)
2438 return(error);
2439 found:
2440 /*
2441 * Copy from the appropriate struct kauth_identity cache entry
2442 * structure into the destination buffer area.
2443 */
2444 switch(to) {
2445 case KI_VALID_UID:
2446 *(uid_t *)dst = ki.ki_uid;
2447 break;
2448 case KI_VALID_GID:
2449 *(gid_t *)dst = ki.ki_gid;
2450 break;
2451 case KI_VALID_GUID:
2452 *(guid_t *)dst = ki.ki_guid;
2453 break;
2454 case KI_VALID_NTSID:
2455 *(ntsid_t *)dst = ki.ki_ntsid;
2456 break;
2457 case KI_VALID_PWNAM:
2458 case KI_VALID_GRNAM:
2459 /* handled in kauth_resolver_complete() */
2460 break;
2461 default:
2462 return(EINVAL);
2463 }
2464 KAUTH_DEBUG("CACHE - returned successfully");
2465 return(0);
2466 }
2467
2468
2469 /*
2470 * Group membership cache.
2471 *
2472 * XXX the linked-list implementation here needs to be optimized.
2473 */
2474
2475 struct kauth_group_membership {
2476 TAILQ_ENTRY(kauth_group_membership) gm_link;
2477 uid_t gm_uid; /* the identity whose membership we're recording */
2478 gid_t gm_gid; /* group of which they are a member */
2479 time_t gm_expiry; /* TTL for the membership */
2480 int gm_flags;
2481 #define KAUTH_GROUP_ISMEMBER (1<<0)
2482 };
2483
2484 TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
2485 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
2486 static int kauth_groups_count;
2487
2488 static lck_mtx_t *kauth_groups_mtx;
2489 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
2490 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
2491
2492 static int kauth_groups_expired(struct kauth_group_membership *gm);
2493 static void kauth_groups_lru(struct kauth_group_membership *gm);
2494 static void kauth_groups_updatecache(struct kauth_identity_extlookup *el);
2495
2496
2497 /*
2498 * kauth_groups_init
2499 *
2500 * Description: Initialize the groups cache
2501 *
2502 * Parameters: (void)
2503 *
2504 * Returns: (void)
2505 *
2506 * Notes: Intialize the groups cache for use; the group cache is used
2507 * to avoid unnecessary calls out to user space.
2508 *
2509 * This function is called from kauth_init() in the file
2510 * kern_authorization.c.
2511 */
2512 void
2513 kauth_groups_init(void)
2514 {
2515 TAILQ_INIT(&kauth_groups);
2516 kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
2517 }
2518
2519
2520 /*
2521 * kauth_groups_expired
2522 *
2523 * Description: Handle lazy expiration of group membership cache entries
2524 *
2525 * Parameters: gm group membership entry to
2526 * check for expiration
2527 *
2528 * Returns: 1 Expired
2529 * 0 Not expired
2530 */
2531 static int
2532 kauth_groups_expired(struct kauth_group_membership *gm)
2533 {
2534 struct timeval tv;
2535
2536 microuptime(&tv);
2537 return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
2538 }
2539
2540
2541 /*
2542 * kauth_groups_lru
2543 *
2544 * Description: Promote the entry to the head of the LRU, assumes the cache
2545 * is locked.
2546 *
2547 * Parameters: kip group membership entry to move
2548 * to the head of the LRU list,
2549 * if it's not already there
2550 *
2551 * Returns: (void)
2552 *
2553 * Notes: This is called even if the entry has expired; typically an
2554 * expired entry that's been looked up is about to be revalidated,
2555 * and having it closer to the head of the LRU means finding it
2556 * quickly again when the revalidation comes through.
2557 */
2558 static void
2559 kauth_groups_lru(struct kauth_group_membership *gm)
2560 {
2561 if (gm != TAILQ_FIRST(&kauth_groups)) {
2562 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2563 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2564 }
2565 }
2566
2567
2568 /*
2569 * kauth_groups_updatecache
2570 *
2571 * Description: Given a lookup result, add any group cache associations that
2572 * we don't currently have.
2573 *
2574 * Parameters: elp External lookup result from
2575 * user space daemon to kernel
2576 * rkip pointer to returned kauth
2577 * identity, or NULL
2578 *
2579 * Returns: (void)
2580 */
2581 static void
2582 kauth_groups_updatecache(struct kauth_identity_extlookup *el)
2583 {
2584 struct kauth_group_membership *gm;
2585 struct timeval tv;
2586
2587 /* need a valid response if we are to cache anything */
2588 if ((el->el_flags &
2589 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
2590 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
2591 return;
2592
2593 microuptime(&tv);
2594
2595 /*
2596 * Search for an existing record for this association before inserting
2597 * a new one; if we find one, update it instead of creating a new one
2598 */
2599 KAUTH_GROUPS_LOCK();
2600 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2601 if ((el->el_uid == gm->gm_uid) &&
2602 (el->el_gid == gm->gm_gid)) {
2603 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2604 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2605 } else {
2606 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2607 }
2608 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
2609 kauth_groups_lru(gm);
2610 break;
2611 }
2612 }
2613 KAUTH_GROUPS_UNLOCK();
2614
2615 /* if we found an entry to update, stop here */
2616 if (gm != NULL)
2617 return;
2618
2619 /* allocate a new record */
2620 MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
2621 if (gm != NULL) {
2622 gm->gm_uid = el->el_uid;
2623 gm->gm_gid = el->el_gid;
2624 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
2625 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
2626 } else {
2627 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
2628 }
2629 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
2630 }
2631
2632 /*
2633 * Insert the new entry. Note that it's possible to race ourselves
2634 * here and end up with duplicate entries in the list. Wasteful, but
2635 * harmless since the first into the list will never be looked up,
2636 * and thus will eventually just fall off the end.
2637 */
2638 KAUTH_GROUPS_LOCK();
2639 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
2640 if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
2641 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
2642 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
2643 kauth_groups_count--;
2644 } else {
2645 gm = NULL;
2646 }
2647 KAUTH_GROUPS_UNLOCK();
2648
2649 /* free expired cache entry */
2650 if (gm != NULL)
2651 FREE(gm, M_KAUTH);
2652 }
2653
2654
2655 /*
2656 * Group membership KPI
2657 */
2658
2659 /*
2660 * kauth_cred_ismember_gid
2661 *
2662 * Description: Given a credential and a GID, determine if the GID is a member
2663 * of one of the supplementary groups associated with the given
2664 * credential
2665 *
2666 * Parameters: cred Credential to check in
2667 * gid GID to check for membership
2668 * resultp Pointer to int to contain the
2669 * result of the call
2670 *
2671 * Returns: 0 Success
2672 * ENOENT Could not proform lookup
2673 * kauth_resolver_submit:EWOULDBLOCK
2674 * kauth_resolver_submit:EINTR
2675 * kauth_resolver_submit:ENOMEM
2676 * kauth_resolver_submit:ENOENT User space daemon did not vend
2677 * this credential.
2678 * kauth_resolver_submit:??? Unlikely error from user space
2679 *
2680 * Implicit returns:
2681 * *resultp (modified) 1 Is member
2682 * 0 Is not member
2683 *
2684 * Notes: This function guarantees not to modify resultp when returning
2685 * an error.
2686 *
2687 * This function effectively checkes the EGID as well, since the
2688 * EGID is cr_groups[0] as an implementation detail.
2689 */
2690 int
2691 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
2692 {
2693 posix_cred_t pcred = posix_cred_get(cred);
2694 struct kauth_group_membership *gm;
2695 struct kauth_identity_extlookup el;
2696 int i, error;
2697
2698 /*
2699 * Check the per-credential list of override groups.
2700 *
2701 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
2702 * the cache should be used for that case.
2703 */
2704 for (i = 0; i < pcred->cr_ngroups; i++) {
2705 if (gid == pcred->cr_groups[i]) {
2706 *resultp = 1;
2707 return(0);
2708 }
2709 }
2710
2711 /*
2712 * If we don't have a UID for group membership checks, the in-cred list
2713 * was authoritative and we can stop here.
2714 */
2715 if (pcred->cr_gmuid == KAUTH_UID_NONE) {
2716 *resultp = 0;
2717 return(0);
2718 }
2719
2720
2721 /*
2722 * If the resolver hasn't checked in yet, we are early in the boot
2723 * phase and the local group list is complete and authoritative.
2724 */
2725 if (!kauth_resolver_registered) {
2726 *resultp = 0;
2727 return(0);
2728 }
2729
2730 /* TODO: */
2731 /* XXX check supplementary groups */
2732 /* XXX check whiteout groups */
2733 /* XXX nesting of supplementary/whiteout groups? */
2734
2735 /*
2736 * Check the group cache.
2737 */
2738 KAUTH_GROUPS_LOCK();
2739 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
2740 if ((gm->gm_uid == pcred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
2741 kauth_groups_lru(gm);
2742 break;
2743 }
2744 }
2745
2746 /* did we find a membership entry? */
2747 if (gm != NULL)
2748 *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
2749 KAUTH_GROUPS_UNLOCK();
2750
2751 /* if we did, we can return now */
2752 if (gm != NULL)
2753 return(0);
2754
2755 /* nothing in the cache, need to go to userland */
2756 bzero(&el, sizeof(el));
2757 el.el_info_pid = current_proc()->p_pid;
2758 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
2759 el.el_uid = pcred->cr_gmuid;
2760 el.el_gid = gid;
2761 el.el_member_valid = 0; /* XXX set by resolver? */
2762 error = kauth_resolver_submit(&el, 0ULL);
2763 if (error != 0)
2764 return(error);
2765 /* save the results from the lookup */
2766 kauth_groups_updatecache(&el);
2767
2768 /* if we successfully ascertained membership, report */
2769 if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
2770 *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
2771 return(0);
2772 }
2773
2774 return(ENOENT);
2775 }
2776
2777
2778 /*
2779 * kauth_cred_ismember_guid
2780 *
2781 * Description: Determine whether the supplied credential is a member of the
2782 * group nominated by GUID.
2783 *
2784 * Parameters: cred Credential to check in
2785 * guidp Pointer to GUID whose group
2786 * we are testing for membership
2787 * resultp Pointer to int to contain the
2788 * result of the call
2789 *
2790 * Returns: 0 Success
2791 * kauth_cred_guid2gid:EINVAL
2792 * kauth_cred_ismember_gid:ENOENT
2793 * kauth_resolver_submit:ENOENT User space daemon did not vend
2794 * this credential.
2795 * kauth_cred_ismember_gid:EWOULDBLOCK
2796 * kauth_cred_ismember_gid:EINTR
2797 * kauth_cred_ismember_gid:ENOMEM
2798 * kauth_cred_ismember_gid:??? Unlikely error from user space
2799 *
2800 * Implicit returns:
2801 * *resultp (modified) 1 Is member
2802 * 0 Is not member
2803 */
2804 int
2805 kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
2806 {
2807 struct kauth_identity ki;
2808 gid_t gid;
2809 int error, wkg;
2810
2811 error = 0;
2812 wkg = kauth_wellknown_guid(guidp);
2813 switch(wkg) {
2814 case KAUTH_WKG_NOBODY:
2815 *resultp = 0;
2816 break;
2817 case KAUTH_WKG_EVERYBODY:
2818 *resultp = 1;
2819 break;
2820 default:
2821 #if 6603280
2822 /*
2823 * Grovel the identity cache looking for this GUID.
2824 * If we find it, and it is for a user record, return
2825 * false because it's not a group.
2826 *
2827 * This is necessary because we don't have -ve caching
2828 * of group memberships, and we really want to avoid
2829 * calling out to the resolver if at all possible.
2830 *
2831 * Because we're called by the ACL evaluator, and the
2832 * ACL evaluator is likely to encounter ACEs for users,
2833 * this is expected to be a common case.
2834 */
2835 ki.ki_valid = 0;
2836 if ((error = kauth_identity_find_guid(guidp, &ki, NULL)) == 0 &&
2837 !kauth_identity_guid_expired(&ki)) {
2838 if (ki.ki_valid & KI_VALID_GID) {
2839 /* It's a group after all... */
2840 gid = ki.ki_gid;
2841 goto do_check;
2842 }
2843 if (ki.ki_valid & KI_VALID_UID) {
2844 *resultp = 0;
2845 return (0);
2846 }
2847 }
2848 #endif /* 6603280 */
2849 /*
2850 * Attempt to translate the GUID to a GID. Even if
2851 * this fails, we will have primed the cache if it is
2852 * a user record and we'll see it above the next time
2853 * we're asked.
2854 */
2855 if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
2856 /*
2857 * If we have no guid -> gid translation, it's not a group and
2858 * thus the cred can't be a member.
2859 */
2860 if (error == ENOENT) {
2861 *resultp = 0;
2862 error = 0;
2863 }
2864 } else {
2865 do_check:
2866 error = kauth_cred_ismember_gid(cred, gid, resultp);
2867 }
2868 }
2869 return(error);
2870 }
2871
2872 /*
2873 * kauth_cred_gid_subset
2874 *
2875 * Description: Given two credentials, determine if all GIDs associated with
2876 * the first are also associated with the second
2877 *
2878 * Parameters: cred1 Credential to check for
2879 * cred2 Credential to check in
2880 * resultp Pointer to int to contain the
2881 * result of the call
2882 *
2883 * Returns: 0 Success
2884 * non-zero See kauth_cred_ismember_gid for
2885 * error codes
2886 *
2887 * Implicit returns:
2888 * *resultp (modified) 1 Is subset
2889 * 0 Is not subset
2890 *
2891 * Notes: This function guarantees not to modify resultp when returning
2892 * an error.
2893 */
2894 int
2895 kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp)
2896 {
2897 int i, err, res = 1;
2898 gid_t gid;
2899 posix_cred_t pcred1 = posix_cred_get(cred1);
2900 posix_cred_t pcred2 = posix_cred_get(cred2);
2901
2902 /* First, check the local list of groups */
2903 for (i = 0; i < pcred1->cr_ngroups; i++) {
2904 gid = pcred1->cr_groups[i];
2905 if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) {
2906 return err;
2907 }
2908
2909 if (!res && gid != pcred2->cr_rgid && gid != pcred2->cr_svgid) {
2910 *resultp = 0;
2911 return 0;
2912 }
2913 }
2914
2915 /* Check real gid */
2916 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_rgid, &res)) != 0) {
2917 return err;
2918 }
2919
2920 if (!res && pcred1->cr_rgid != pcred2->cr_rgid &&
2921 pcred1->cr_rgid != pcred2->cr_svgid) {
2922 *resultp = 0;
2923 return 0;
2924 }
2925
2926 /* Finally, check saved gid */
2927 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_svgid, &res)) != 0){
2928 return err;
2929 }
2930
2931 if (!res && pcred1->cr_svgid != pcred2->cr_rgid &&
2932 pcred1->cr_svgid != pcred2->cr_svgid) {
2933 *resultp = 0;
2934 return 0;
2935 }
2936
2937 *resultp = 1;
2938 return 0;
2939 }
2940
2941
2942 /*
2943 * kauth_cred_issuser
2944 *
2945 * Description: Fast replacement for issuser()
2946 *
2947 * Parameters: cred Credential to check for super
2948 * user privileges
2949 *
2950 * Returns: 0 Not super user
2951 * !0 Is super user
2952 *
2953 * Notes: This function uses a magic number which is not a manifest
2954 * constant; this is bad practice.
2955 */
2956 int
2957 kauth_cred_issuser(kauth_cred_t cred)
2958 {
2959 return(kauth_cred_getuid(cred) == 0);
2960 }
2961
2962
2963 /*
2964 * Credential KPI
2965 */
2966
2967 /* lock protecting credential hash table */
2968 static lck_mtx_t *kauth_cred_hash_mtx;
2969 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
2970 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
2971 #if KAUTH_CRED_HASH_DEBUG
2972 #define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED)
2973 #else /* !KAUTH_CRED_HASH_DEBUG */
2974 #define KAUTH_CRED_HASH_LOCK_ASSERT()
2975 #endif /* !KAUTH_CRED_HASH_DEBUG */
2976
2977
2978 /*
2979 * kauth_cred_init
2980 *
2981 * Description: Initialize the credential hash cache
2982 *
2983 * Parameters: (void)
2984 *
2985 * Returns: (void)
2986 *
2987 * Notes: Intialize the credential hash cache for use; the credential
2988 * hash cache is used convert duplicate credentials into a
2989 * single reference counted credential in order to save wired
2990 * kernel memory. In practice, this generally means a desktop
2991 * system runs with a few tens of credentials, instead of one
2992 * per process, one per thread, one per vnode cache entry, and
2993 * so on. This generally results in savings of 200K or more
2994 * (potentially much more on server systems).
2995 *
2996 * The hash cache internally has a reference on the credential
2997 * for itself as a means of avoiding a reclaim race for a
2998 * credential in the process of having it's last non-hash
2999 * reference released. This would otherwise result in the
3000 * possibility of a freed credential that was still in uses due
3001 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK.
3002 *
3003 * On final release, the hash reference is droped, and the
3004 * credential is freed back to the system.
3005 *
3006 * This function is called from kauth_init() in the file
3007 * kern_authorization.c.
3008 */
3009 void
3010 kauth_cred_init(void)
3011 {
3012 int i;
3013
3014 kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
3015 kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
3016
3017 /*allocate credential hash table */
3018 MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
3019 (sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size),
3020 M_KAUTH, M_WAITOK | M_ZERO);
3021 if (kauth_cred_table_anchor == NULL)
3022 panic("startup: kauth_cred_init");
3023 for (i = 0; i < kauth_cred_table_size; i++) {
3024 TAILQ_INIT(&kauth_cred_table_anchor[i]);
3025 }
3026 }
3027
3028
3029 /*
3030 * kauth_getuid
3031 *
3032 * Description: Get the current thread's effective UID.
3033 *
3034 * Parameters: (void)
3035 *
3036 * Returns: (uid_t) The effective UID of the
3037 * current thread
3038 */
3039 uid_t
3040 kauth_getuid(void)
3041 {
3042 return(kauth_cred_getuid(kauth_cred_get()));
3043 }
3044
3045
3046 /*
3047 * kauth_getruid
3048 *
3049 * Description: Get the current thread's real UID.
3050 *
3051 * Parameters: (void)
3052 *
3053 * Returns: (uid_t) The real UID of the current
3054 * thread
3055 */
3056 uid_t
3057 kauth_getruid(void)
3058 {
3059 return(kauth_cred_getruid(kauth_cred_get()));
3060 }
3061
3062
3063 /*
3064 * kauth_getgid
3065 *
3066 * Description: Get the current thread's effective GID.
3067 *
3068 * Parameters: (void)
3069 *
3070 * Returns: (gid_t) The effective GID of the
3071 * current thread
3072 */
3073 gid_t
3074 kauth_getgid(void)
3075 {
3076 return(kauth_cred_getgid(kauth_cred_get()));
3077 }
3078
3079
3080 /*
3081 * kauth_getgid
3082 *
3083 * Description: Get the current thread's real GID.
3084 *
3085 * Parameters: (void)
3086 *
3087 * Returns: (gid_t) The real GID of the current
3088 * thread
3089 */
3090 gid_t
3091 kauth_getrgid(void)
3092 {
3093 return(kauth_cred_getrgid(kauth_cred_get()));
3094 }
3095
3096
3097 /*
3098 * kauth_cred_get
3099 *
3100 * Description: Returns a pointer to the current thread's credential
3101 *
3102 * Parameters: (void)
3103 *
3104 * Returns: (kauth_cred_t) Pointer to the current thread's
3105 * credential
3106 *
3107 * Notes: This function does not take a reference; because of this, the
3108 * caller MUST NOT do anything that would let the thread's
3109 * credential change while using the returned value, without
3110 * first explicitly taking their own reference.
3111 *
3112 * If a caller intends to take a reference on the resulting
3113 * credential pointer from calling this function, it is strongly
3114 * recommended that the caller use kauth_cred_get_with_ref()
3115 * instead, to protect against any future changes to the cred
3116 * locking protocols; such changes could otherwise potentially
3117 * introduce race windows in the callers code.
3118 */
3119 kauth_cred_t
3120 kauth_cred_get(void)
3121 {
3122 struct proc *p;
3123 struct uthread *uthread;
3124
3125 uthread = get_bsdthread_info(current_thread());
3126 /* sanity */
3127 if (uthread == NULL)
3128 panic("thread wants credential but has no BSD thread info");
3129 /*
3130 * We can lazy-bind credentials to threads, as long as their processes
3131 * have them.
3132 *
3133 * XXX If we later inline this function, the code in this block
3134 * XXX should probably be called out in a function.
3135 */
3136 if (uthread->uu_ucred == NOCRED) {
3137 if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3138 panic("thread wants credential but has no BSD process");
3139 uthread->uu_ucred = kauth_cred_proc_ref(p);
3140 }
3141 return(uthread->uu_ucred);
3142 }
3143
3144 void
3145 mach_kauth_cred_uthread_update(void)
3146 {
3147 uthread_t uthread;
3148 proc_t proc;
3149
3150 uthread = get_bsdthread_info(current_thread());
3151 proc = current_proc();
3152
3153 kauth_cred_uthread_update(uthread, proc);
3154 }
3155
3156 /*
3157 * kauth_cred_uthread_update
3158 *
3159 * Description: Given a uthread, a proc, and whether or not the proc is locked,
3160 * late-bind the uthread cred to the proc cred.
3161 *
3162 * Parameters: uthread_t The uthread to update
3163 * proc_t The process to update to
3164 *
3165 * Returns: (void)
3166 *
3167 * Notes: This code is common code called from system call or trap entry
3168 * in the case that the process thread may have been changed
3169 * since the last time the thread entered the kernel. It is
3170 * generally only called with the current uthread and process as
3171 * parameters.
3172 */
3173 void
3174 kauth_cred_uthread_update(uthread_t uthread, proc_t proc)
3175 {
3176 if (uthread->uu_ucred != proc->p_ucred &&
3177 (uthread->uu_flag & UT_SETUID) == 0) {
3178 kauth_cred_t old = uthread->uu_ucred;
3179 uthread->uu_ucred = kauth_cred_proc_ref(proc);
3180 if (IS_VALID_CRED(old))
3181 kauth_cred_unref(&old);
3182 }
3183 }
3184
3185
3186 /*
3187 * kauth_cred_get_with_ref
3188 *
3189 * Description: Takes a reference on the current thread's credential, and then
3190 * returns a pointer to it to the caller.
3191 *
3192 * Parameters: (void)
3193 *
3194 * Returns: (kauth_cred_t) Pointer to the current thread's
3195 * newly referenced credential
3196 *
3197 * Notes: This function takes a reference on the credential before
3198 * returning it to the caller.
3199 *
3200 * It is the responsibility of the calling code to release this
3201 * reference when the credential is no longer in use.
3202 *
3203 * Since the returned reference may be a persistent reference
3204 * (e.g. one cached in another data structure with a lifetime
3205 * longer than the calling function), this release may be delayed
3206 * until such time as the persistent reference is to be destroyed.
3207 * An example of this would be the per vnode credential cache used
3208 * to accelerate lookup operations.
3209 */
3210 kauth_cred_t
3211 kauth_cred_get_with_ref(void)
3212 {
3213 struct proc *procp;
3214 struct uthread *uthread;
3215
3216 uthread = get_bsdthread_info(current_thread());
3217 /* sanity checks */
3218 if (uthread == NULL)
3219 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
3220 if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
3221 panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
3222
3223 /*
3224 * We can lazy-bind credentials to threads, as long as their processes
3225 * have them.
3226 *
3227 * XXX If we later inline this function, the code in this block
3228 * XXX should probably be called out in a function.
3229 */
3230 if (uthread->uu_ucred == NOCRED) {
3231 /* take reference for new cred in thread */
3232 uthread->uu_ucred = kauth_cred_proc_ref(procp);
3233 }
3234 /* take a reference for our caller */
3235 kauth_cred_ref(uthread->uu_ucred);
3236 return(uthread->uu_ucred);
3237 }
3238
3239
3240 /*
3241 * kauth_cred_proc_ref
3242 *
3243 * Description: Takes a reference on the current process's credential, and
3244 * then returns a pointer to it to the caller.
3245 *
3246 * Parameters: procp Process whose credential we
3247 * intend to take a reference on
3248 *
3249 * Returns: (kauth_cred_t) Pointer to the process's
3250 * newly referenced credential
3251 *
3252 * Locks: PROC_LOCK is held before taking the reference and released
3253 * after the refeence is taken to protect the p_ucred field of
3254 * the process referred to by procp.
3255 *
3256 * Notes: This function takes a reference on the credential before
3257 * returning it to the caller.
3258 *
3259 * It is the responsibility of the calling code to release this
3260 * reference when the credential is no longer in use.
3261 *
3262 * Since the returned reference may be a persistent reference
3263 * (e.g. one cached in another data structure with a lifetime
3264 * longer than the calling function), this release may be delayed
3265 * until such time as the persistent reference is to be destroyed.
3266 * An example of this would be the per vnode credential cache used
3267 * to accelerate lookup operations.
3268 */
3269 kauth_cred_t
3270 kauth_cred_proc_ref(proc_t procp)
3271 {
3272 kauth_cred_t cred;
3273
3274 proc_lock(procp);
3275 cred = proc_ucred(procp);
3276 kauth_cred_ref(cred);
3277 proc_unlock(procp);
3278 return(cred);
3279 }
3280
3281
3282 /*
3283 * kauth_cred_alloc
3284 *
3285 * Description: Allocate a new credential
3286 *
3287 * Parameters: (void)
3288 *
3289 * Returns: !NULL Newly allocated credential
3290 * NULL Insufficient memory
3291 *
3292 * Notes: The newly allocated credential is zero'ed as part of the
3293 * allocation process, with the exception of the reference
3294 * count, which is set to 1 to indicate a single reference
3295 * held by the caller.
3296 *
3297 * Since newly allocated credentials have no external pointers
3298 * referencing them, prior to making them visible in an externally
3299 * visible pointer (e.g. by adding them to the credential hash
3300 * cache) is the only legal time in which an existing credential
3301 * can be safely iinitialized or modified directly.
3302 *
3303 * After initialization, the caller is expected to call the
3304 * function kauth_cred_add() to add the credential to the hash
3305 * cache, after which time it's frozen and becomes publically
3306 * visible.
3307 *
3308 * The release protocol depends on kauth_hash_add() being called
3309 * before kauth_cred_rele() (there is a diagnostic panic which
3310 * will trigger if this protocol is not observed).
3311 *
3312 * XXX: This function really ought to be static, rather than being
3313 * exported as KPI, since a failure of kauth_cred_add() can only
3314 * be handled by an explicit free of the credential; such frees
3315 * depend on knowlegdge of the allocation method used, which is
3316 * permitted to change between kernel revisions.
3317 *
3318 * XXX: In the insufficient resource case, this code panic's rather
3319 * than returning a NULL pointer; the code that calls this
3320 * function needs to be audited before this can be changed.
3321 */
3322 kauth_cred_t
3323 kauth_cred_alloc(void)
3324 {
3325 kauth_cred_t newcred;
3326
3327 MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK);
3328 if (newcred != 0) {
3329 posix_cred_t newpcred = posix_cred_get(newcred);
3330 bzero(newcred, sizeof(*newcred));
3331 newcred->cr_ref = 1;
3332 newcred->cr_audit.as_aia_p = audit_default_aia_p;
3333 /* must do this, or cred has same group membership as uid 0 */
3334 newpcred->cr_gmuid = KAUTH_UID_NONE;
3335 #if CRED_DIAGNOSTIC
3336 } else {
3337 panic("kauth_cred_alloc: couldn't allocate credential");
3338 #endif
3339 }
3340
3341 #if KAUTH_CRED_HASH_DEBUG
3342 kauth_cred_count++;
3343 #endif
3344
3345 #if CONFIG_MACF
3346 mac_cred_label_init(newcred);
3347 #endif
3348
3349 return(newcred);
3350 }
3351
3352
3353 /*
3354 * kauth_cred_create
3355 *
3356 * Description: Look to see if we already have a known credential in the hash
3357 * cache; if one is found, bump the reference count and return
3358 * it. If there are no credentials that match the given
3359 * credential, then allocate a new credential.
3360 *
3361 * Parameters: cred Template for credential to
3362 * be created
3363 *
3364 * Returns: (kauth_cred_t) The credential that was found
3365 * in the hash or created
3366 * NULL kauth_cred_add() failed, or
3367 * there was not an egid specified
3368 *
3369 * Notes: The gmuid is hard-defaulted to the UID specified. Since we
3370 * maintain this field, we can't expect callers to know how it
3371 * needs to be set. Callers should be prepared for this field
3372 * to be overwritten.
3373 *
3374 * XXX: This code will tight-loop if memory for a new credential is
3375 * persistently unavailable; this is perhaps not the wisest way
3376 * to handle this condition, but current callers do not expect
3377 * a failure.
3378 */
3379 kauth_cred_t
3380 kauth_cred_create(kauth_cred_t cred)
3381 {
3382 kauth_cred_t found_cred, new_cred = NULL;
3383 posix_cred_t pcred = posix_cred_get(cred);
3384 int is_member = 0;
3385
3386 KAUTH_CRED_HASH_LOCK_ASSERT();
3387
3388 if (pcred->cr_flags & CRF_NOMEMBERD) {
3389 pcred->cr_gmuid = KAUTH_UID_NONE;
3390 } else {
3391 /*
3392 * If the template credential is not opting out of external
3393 * group membership resolution, then we need to check that
3394 * the UID we will be using is resolvable by the external
3395 * resolver. If it's not, then we opt it out anyway, since
3396 * all future external resolution requests will be failing
3397 * anyway, and potentially taking a long time to do it. We
3398 * use gid 0 because we always know it will exist and not
3399 * trigger additional lookups. This is OK, because we end up
3400 * precatching the information here as a result.
3401 */
3402 if (!kauth_cred_ismember_gid(cred, 0, &is_member)) {
3403 /*
3404 * It's a recognized value; we don't really care about
3405 * the answer, so long as it's something the external
3406 * resolver could have vended.
3407 */
3408 pcred->cr_gmuid = pcred->cr_uid;
3409 } else {
3410 /*
3411 * It's not something the external resolver could
3412 * have vended, so we don't want to ask it more
3413 * questions about the credential in the future. This
3414 * speeds up future lookups, as long as the caller
3415 * caches results; otherwise, it the same recurring
3416 * cost. Since most credentials are used multiple
3417 * times, we still get some performance win from this.
3418 */
3419 pcred->cr_gmuid = KAUTH_UID_NONE;
3420 pcred->cr_flags |= CRF_NOMEMBERD;
3421 }
3422 }
3423
3424 /* Caller *must* specify at least the egid in cr_groups[0] */
3425 if (pcred->cr_ngroups < 1)
3426 return(NULL);
3427
3428 for (;;) {
3429 KAUTH_CRED_HASH_LOCK();
3430 found_cred = kauth_cred_find(cred);
3431 if (found_cred != NULL) {
3432 /*
3433 * Found an existing credential so we'll bump
3434 * reference count and return
3435 */
3436 kauth_cred_ref(found_cred);
3437 KAUTH_CRED_HASH_UNLOCK();
3438 return(found_cred);
3439 }
3440 KAUTH_CRED_HASH_UNLOCK();
3441
3442 /*
3443 * No existing credential found. Create one and add it to
3444 * our hash table.
3445 */
3446 new_cred = kauth_cred_alloc();
3447 if (new_cred != NULL) {
3448 int err;
3449 posix_cred_t new_pcred = posix_cred_get(new_cred);
3450 new_pcred->cr_uid = pcred->cr_uid;
3451 new_pcred->cr_ruid = pcred->cr_ruid;
3452 new_pcred->cr_svuid = pcred->cr_svuid;
3453 new_pcred->cr_rgid = pcred->cr_rgid;
3454 new_pcred->cr_svgid = pcred->cr_svgid;
3455 new_pcred->cr_gmuid = pcred->cr_gmuid;
3456 new_pcred->cr_ngroups = pcred->cr_ngroups;
3457 bcopy(&pcred->cr_groups[0], &new_pcred->cr_groups[0], sizeof(new_pcred->cr_groups));
3458 #if CONFIG_AUDIT
3459 bcopy(&cred->cr_audit, &new_cred->cr_audit,
3460 sizeof(new_cred->cr_audit));
3461 #endif
3462 new_pcred->cr_flags = pcred->cr_flags;
3463
3464 KAUTH_CRED_HASH_LOCK();
3465 err = kauth_cred_add(new_cred);
3466 KAUTH_CRED_HASH_UNLOCK();
3467
3468 /* Retry if kauth_cred_add returns non zero value */
3469 if (err == 0)
3470 break;
3471 #if CONFIG_MACF
3472 mac_cred_label_destroy(new_cred);
3473 #endif
3474 AUDIT_SESSION_UNREF(new_cred);
3475
3476 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
3477 new_cred = NULL;
3478 }
3479 }
3480
3481 return(new_cred);
3482 }
3483
3484
3485 /*
3486 * kauth_cred_setresuid
3487 *
3488 * Description: Update the given credential using the UID arguments. The given
3489 * UIDs are used to set the effective UID, real UID, saved UID,
3490 * and GMUID (used for group membership checking).
3491 *
3492 * Parameters: cred The original credential
3493 * ruid The new real UID
3494 * euid The new effective UID
3495 * svuid The new saved UID
3496 * gmuid KAUTH_UID_NONE -or- the new
3497 * group membership UID
3498 *
3499 * Returns: (kauth_cred_t) The updated credential
3500 *
3501 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3502 * setting, so if you don't want it to change, pass it the
3503 * previous value, explicitly.
3504 *
3505 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3506 * if it returns a credential other than the one it is passed,
3507 * will have dropped the reference on the passed credential. All
3508 * callers should be aware of this, and treat this function as an
3509 * unref + ref, potentially on different credentials.
3510 *
3511 * Because of this, the caller is expected to take its own
3512 * reference on the credential passed as the first parameter,
3513 * and be prepared to release the reference on the credential
3514 * that is returned to them, if it is not intended to be a
3515 * persistent reference.
3516 */
3517 kauth_cred_t
3518 kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid)
3519 {
3520 struct ucred temp_cred;
3521 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3522 posix_cred_t pcred = posix_cred_get(cred);
3523
3524 NULLCRED_CHECK(cred);
3525
3526 /*
3527 * We don't need to do anything if the UIDs we are changing are
3528 * already the same as the UIDs passed in
3529 */
3530 if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) &&
3531 (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) &&
3532 (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) &&
3533 (pcred->cr_gmuid == gmuid)) {
3534 /* no change needed */
3535 return(cred);
3536 }
3537
3538 /*
3539 * Look up in cred hash table to see if we have a matching credential
3540 * with the new values; this is done by calling kauth_cred_update().
3541 */
3542 bcopy(cred, &temp_cred, sizeof(temp_cred));
3543 if (euid != KAUTH_UID_NONE) {
3544 temp_pcred->cr_uid = euid;
3545 }
3546 if (ruid != KAUTH_UID_NONE) {
3547 temp_pcred->cr_ruid = ruid;
3548 }
3549 if (svuid != KAUTH_UID_NONE) {
3550 temp_pcred->cr_svuid = svuid;
3551 }
3552
3553 /*
3554 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to
3555 * opt out of participation in external group resolution, unless we
3556 * unless we explicitly opt back in later.
3557 */
3558 if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) {
3559 temp_pcred->cr_flags |= CRF_NOMEMBERD;
3560 }
3561
3562 return(kauth_cred_update(cred, &temp_cred, TRUE));
3563 }
3564
3565
3566 /*
3567 * kauth_cred_setresgid
3568 *
3569 * Description: Update the given credential using the GID arguments. The given
3570 * GIDs are used to set the effective GID, real GID, and saved
3571 * GID.
3572 *
3573 * Parameters: cred The original credential
3574 * rgid The new real GID
3575 * egid The new effective GID
3576 * svgid The new saved GID
3577 *
3578 * Returns: (kauth_cred_t) The updated credential
3579 *
3580 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3581 * if it returns a credential other than the one it is passed,
3582 * will have dropped the reference on the passed credential. All
3583 * callers should be aware of this, and treat this function as an
3584 * unref + ref, potentially on different credentials.
3585 *
3586 * Because of this, the caller is expected to take its own
3587 * reference on the credential passed as the first parameter,
3588 * and be prepared to release the reference on the credential
3589 * that is returned to them, if it is not intended to be a
3590 * persistent reference.
3591 */
3592 kauth_cred_t
3593 kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid)
3594 {
3595 struct ucred temp_cred;
3596 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3597 posix_cred_t pcred = posix_cred_get(cred);
3598
3599 NULLCRED_CHECK(cred);
3600 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid);
3601
3602 /*
3603 * We don't need to do anything if the given GID are already the
3604 * same as the GIDs in the credential.
3605 */
3606 if (pcred->cr_groups[0] == egid &&
3607 pcred->cr_rgid == rgid &&
3608 pcred->cr_svgid == svgid) {
3609 /* no change needed */
3610 return(cred);
3611 }
3612
3613 /*
3614 * Look up in cred hash table to see if we have a matching credential
3615 * with the new values; this is done by calling kauth_cred_update().
3616 */
3617 bcopy(cred, &temp_cred, sizeof(temp_cred));
3618 if (egid != KAUTH_GID_NONE) {
3619 /* displacing a supplementary group opts us out of memberd */
3620 if (kauth_cred_change_egid(&temp_cred, egid)) {
3621 DEBUG_CRED_CHANGE("displaced!\n");
3622 temp_pcred->cr_flags |= CRF_NOMEMBERD;
3623 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
3624 } else {
3625 DEBUG_CRED_CHANGE("not displaced\n");
3626 }
3627 }
3628 if (rgid != KAUTH_GID_NONE) {
3629 temp_pcred->cr_rgid = rgid;
3630 }
3631 if (svgid != KAUTH_GID_NONE) {
3632 temp_pcred->cr_svgid = svgid;
3633 }
3634
3635 return(kauth_cred_update(cred, &temp_cred, TRUE));
3636 }
3637
3638
3639 /*
3640 * Update the given credential with the given groups. We only allocate a new
3641 * credential when the given gid actually results in changes to the existing
3642 * credential.
3643 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
3644 * which will be used for group membership checking.
3645 */
3646 /*
3647 * kauth_cred_setgroups
3648 *
3649 * Description: Update the given credential using the provide supplementary
3650 * group list and group membership UID
3651 *
3652 * Parameters: cred The original credential
3653 * groups Pointer to gid_t array which
3654 * contains the new group list
3655 * groupcount The cound of valid groups which
3656 * are contained in 'groups'
3657 * gmuid KAUTH_UID_NONE -or- the new
3658 * group membership UID
3659 *
3660 * Returns: (kauth_cred_t) The updated credential
3661 *
3662 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid
3663 * setting, so if you don't want it to change, pass it the
3664 * previous value, explicitly.
3665 *
3666 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3667 * if it returns a credential other than the one it is passed,
3668 * will have dropped the reference on the passed credential. All
3669 * callers should be aware of this, and treat this function as an
3670 * unref + ref, potentially on different credentials.
3671 *
3672 * Because of this, the caller is expected to take its own
3673 * reference on the credential passed as the first parameter,
3674 * and be prepared to release the reference on the credential
3675 * that is returned to them, if it is not intended to be a
3676 * persistent reference.
3677 *
3678 * XXX: Changes are determined in ordinal order - if the caller pasess
3679 * in the same groups list that is already present in the
3680 * credential, but the members are in a different order, even if
3681 * the EGID is not modified (i.e. cr_groups[0] is the same), it
3682 * is considered a modification to the credential, and a new
3683 * credential is created.
3684 *
3685 * This should perhaps be better optimized, but it is considered
3686 * to be the caller's problem.
3687 */
3688 kauth_cred_t
3689 kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
3690 {
3691 int i;
3692 struct ucred temp_cred;
3693 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3694 posix_cred_t pcred;
3695
3696 NULLCRED_CHECK(cred);
3697
3698 pcred = posix_cred_get(cred);
3699
3700 /*
3701 * We don't need to do anything if the given list of groups does not
3702 * change.
3703 */
3704 if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) {
3705 for (i = 0; i < groupcount; i++) {
3706 if (pcred->cr_groups[i] != groups[i])
3707 break;
3708 }
3709 if (i == groupcount) {
3710 /* no change needed */
3711 return(cred);
3712 }
3713 }
3714
3715 /*
3716 * Look up in cred hash table to see if we have a matching credential
3717 * with new values. If we are setting or clearing the gmuid, then
3718 * update the cr_flags, since clearing it is sticky. This permits an
3719 * opt-out of memberd processing using setgroups(), and an opt-in
3720 * using initgroups(). This is required for POSIX conformance.
3721 */
3722 bcopy(cred, &temp_cred, sizeof(temp_cred));
3723 temp_pcred->cr_ngroups = groupcount;
3724 bcopy(groups, temp_pcred->cr_groups, sizeof(temp_pcred->cr_groups));
3725 temp_pcred->cr_gmuid = gmuid;
3726 if (gmuid == KAUTH_UID_NONE)
3727 temp_pcred->cr_flags |= CRF_NOMEMBERD;
3728 else
3729 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
3730
3731 return(kauth_cred_update(cred, &temp_cred, TRUE));
3732 }
3733
3734 /*
3735 * XXX temporary, for NFS support until we can come up with a better
3736 * XXX enumeration/comparison mechanism
3737 *
3738 * Notes: The return value exists to account for the possbility of a
3739 * kauth_cred_t without a POSIX label. This will be the case in
3740 * the future (see posix_cred_get() below, for more details).
3741 */
3742 int
3743 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, int *countp)
3744 {
3745 int limit = NGROUPS;
3746
3747 /*
3748 * If they just want a copy of the groups list, they may not care
3749 * about the actual count. If they specify an input count, however,
3750 * treat it as an indicator of the buffer size available in grouplist,
3751 * and limit the returned list to that size.
3752 */
3753 if (countp) {
3754 limit = MIN(*countp, cred->cr_posix.cr_ngroups);
3755 *countp = limit;
3756 }
3757
3758 memcpy(grouplist, cred->cr_posix.cr_groups, sizeof(gid_t) * limit);
3759
3760 return 0;
3761 }
3762
3763
3764 /*
3765 * kauth_cred_setuidgid
3766 *
3767 * Description: Update the given credential using the UID and GID arguments.
3768 * The given UID is used to set the effective UID, real UID, and
3769 * saved UID. The given GID is used to set the effective GID,
3770 * real GID, and saved GID.
3771 *
3772 * Parameters: cred The original credential
3773 * uid The new UID to use
3774 * gid The new GID to use
3775 *
3776 * Returns: (kauth_cred_t) The updated credential
3777 *
3778 * Notes: We set the gmuid to uid if the credential we are inheriting
3779 * from has not opted out of memberd participation; otherwise
3780 * we set it to KAUTH_UID_NONE
3781 *
3782 * This code is only ever called from the per-thread credential
3783 * code path in the "set per thread credential" case; and in
3784 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS
3785 * flag is set.
3786 *
3787 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3788 * if it returns a credential other than the one it is passed,
3789 * will have dropped the reference on the passed credential. All
3790 * callers should be aware of this, and treat this function as an
3791 * unref + ref, potentially on different credentials.
3792 *
3793 * Because of this, the caller is expected to take its own
3794 * reference on the credential passed as the first parameter,
3795 * and be prepared to release the reference on the credential
3796 * that is returned to them, if it is not intended to be a
3797 * persistent reference.
3798 */
3799 kauth_cred_t
3800 kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
3801 {
3802 struct ucred temp_cred;
3803 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3804 posix_cred_t pcred;
3805
3806 NULLCRED_CHECK(cred);
3807
3808 pcred = posix_cred_get(cred);
3809
3810 /*
3811 * We don't need to do anything if the effective, real and saved
3812 * user IDs are already the same as the user ID passed into us.
3813 */
3814 if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid &&
3815 pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) {
3816 /* no change needed */
3817 return(cred);
3818 }
3819
3820 /*
3821 * Look up in cred hash table to see if we have a matching credential
3822 * with the new values.
3823 */
3824 bzero(&temp_cred, sizeof(temp_cred));
3825 temp_pcred->cr_uid = uid;
3826 temp_pcred->cr_ruid = uid;
3827 temp_pcred->cr_svuid = uid;
3828 temp_pcred->cr_flags = pcred->cr_flags;
3829 /* inherit the opt-out of memberd */
3830 if (pcred->cr_flags & CRF_NOMEMBERD) {
3831 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
3832 temp_pcred->cr_flags |= CRF_NOMEMBERD;
3833 } else {
3834 temp_pcred->cr_gmuid = uid;
3835 temp_pcred->cr_flags &= ~CRF_NOMEMBERD;
3836 }
3837 temp_pcred->cr_ngroups = 1;
3838 /* displacing a supplementary group opts us out of memberd */
3839 if (kauth_cred_change_egid(&temp_cred, gid)) {
3840 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
3841 temp_pcred->cr_flags |= CRF_NOMEMBERD;
3842 }
3843 temp_pcred->cr_rgid = gid;
3844 temp_pcred->cr_svgid = gid;
3845 #if CONFIG_MACF
3846 temp_cred.cr_label = cred->cr_label;
3847 #endif
3848
3849 return(kauth_cred_update(cred, &temp_cred, TRUE));
3850 }
3851
3852
3853 /*
3854 * kauth_cred_setsvuidgid
3855 *
3856 * Description: Function used by execve to set the saved uid and gid values
3857 * for suid/sgid programs
3858 *
3859 * Parameters: cred The credential to update
3860 * uid The saved uid to set
3861 * gid The saved gid to set
3862 *
3863 * Returns: (kauth_cred_t) The updated credential
3864 *
3865 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3866 * if it returns a credential other than the one it is passed,
3867 * will have dropped the reference on the passed credential. All
3868 * callers should be aware of this, and treat this function as an
3869 * unref + ref, potentially on different credentials.
3870 *
3871 * Because of this, the caller is expected to take its own
3872 * reference on the credential passed as the first parameter,
3873 * and be prepared to release the reference on the credential
3874 * that is returned to them, if it is not intended to be a
3875 * persistent reference.
3876 */
3877 kauth_cred_t
3878 kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
3879 {
3880 struct ucred temp_cred;
3881 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
3882 posix_cred_t pcred;
3883
3884 NULLCRED_CHECK(cred);
3885
3886 pcred = posix_cred_get(cred);
3887
3888 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid);
3889
3890 /*
3891 * We don't need to do anything if the effective, real and saved
3892 * uids are already the same as the uid provided. This check is
3893 * likely insufficient.
3894 */
3895 if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) {
3896 /* no change needed */
3897 return(cred);
3898 }
3899 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n");
3900
3901 /* look up in cred hash table to see if we have a matching credential
3902 * with new values.
3903 */
3904 bcopy(cred, &temp_cred, sizeof(temp_cred));
3905 temp_pcred->cr_svuid = uid;
3906 temp_pcred->cr_svgid = gid;
3907
3908 return(kauth_cred_update(cred, &temp_cred, TRUE));
3909 }
3910
3911
3912 /*
3913 * kauth_cred_setauditinfo
3914 *
3915 * Description: Update the given credential using the given au_session_t.
3916 *
3917 * Parameters: cred The original credential
3918 * auditinfo_p Pointer to ne audit information
3919 *
3920 * Returns: (kauth_cred_t) The updated credential
3921 *
3922 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3923 * if it returns a credential other than the one it is passed,
3924 * will have dropped the reference on the passed credential. All
3925 * callers should be aware of this, and treat this function as an
3926 * unref + ref, potentially on different credentials.
3927 *
3928 * Because of this, the caller is expected to take its own
3929 * reference on the credential passed as the first parameter,
3930 * and be prepared to release the reference on the credential
3931 * that is returned to them, if it is not intended to be a
3932 * persistent reference.
3933 */
3934 kauth_cred_t
3935 kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p)
3936 {
3937 struct ucred temp_cred;
3938
3939 NULLCRED_CHECK(cred);
3940
3941 /*
3942 * We don't need to do anything if the audit info is already the
3943 * same as the audit info in the credential provided.
3944 */
3945 if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) {
3946 /* no change needed */
3947 return(cred);
3948 }
3949
3950 bcopy(cred, &temp_cred, sizeof(temp_cred));
3951 bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit));
3952
3953 return(kauth_cred_update(cred, &temp_cred, FALSE));
3954 }
3955
3956 #if CONFIG_MACF
3957 /*
3958 * kauth_cred_label_update
3959 *
3960 * Description: Update the MAC label associated with a credential
3961 *
3962 * Parameters: cred The original credential
3963 * label The MAC label to set
3964 *
3965 * Returns: (kauth_cred_t) The updated credential
3966 *
3967 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
3968 * if it returns a credential other than the one it is passed,
3969 * will have dropped the reference on the passed credential. All
3970 * callers should be aware of this, and treat this function as an
3971 * unref + ref, potentially on different credentials.
3972 *
3973 * Because of this, the caller is expected to take its own
3974 * reference on the credential passed as the first parameter,
3975 * and be prepared to release the reference on the credential
3976 * that is returned to them, if it is not intended to be a
3977 * persistent reference.
3978 */
3979 kauth_cred_t
3980 kauth_cred_label_update(kauth_cred_t cred, struct label *label)
3981 {
3982 kauth_cred_t newcred;
3983 struct ucred temp_cred;
3984
3985 bcopy(cred, &temp_cred, sizeof(temp_cred));
3986
3987 mac_cred_label_init(&temp_cred);
3988 mac_cred_label_associate(cred, &temp_cred);
3989 mac_cred_label_update(&temp_cred, label);
3990
3991 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
3992 mac_cred_label_destroy(&temp_cred);
3993 return (newcred);
3994 }
3995
3996 /*
3997 * kauth_cred_label_update_execve
3998 *
3999 * Description: Update the MAC label associated with a credential as
4000 * part of exec
4001 *
4002 * Parameters: cred The original credential
4003 * vp The exec vnode
4004 * scriptl The script MAC label
4005 * execl The executable MAC label
4006 * disjointp Pointer to flag to set if old
4007 * and returned credentials are
4008 * disjoint
4009 *
4010 * Returns: (kauth_cred_t) The updated credential
4011 *
4012 * Implicit returns:
4013 * *disjointp Set to 1 for disjoint creds
4014 *
4015 * IMPORTANT: This function is implemented via kauth_cred_update(), which,
4016 * if it returns a credential other than the one it is passed,
4017 * will have dropped the reference on the passed credential. All
4018 * callers should be aware of this, and treat this function as an
4019 * unref + ref, potentially on different credentials.
4020 *
4021 * Because of this, the caller is expected to take its own
4022 * reference on the credential passed as the first parameter,
4023 * and be prepared to release the reference on the credential
4024 * that is returned to them, if it is not intended to be a
4025 * persistent reference.
4026 */
4027 static
4028 kauth_cred_t
4029 kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx,
4030 struct vnode *vp, struct label *scriptl, struct label *execl,
4031 int *disjointp)
4032 {
4033 kauth_cred_t newcred;
4034 struct ucred temp_cred;
4035
4036 bcopy(cred, &temp_cred, sizeof(temp_cred));
4037
4038 mac_cred_label_init(&temp_cred);
4039 mac_cred_label_associate(cred, &temp_cred);
4040 *disjointp = mac_cred_label_update_execve(ctx, &temp_cred,
4041 vp, scriptl, execl);
4042
4043 newcred = kauth_cred_update(cred, &temp_cred, TRUE);
4044 mac_cred_label_destroy(&temp_cred);
4045 return (newcred);
4046 }
4047
4048 /*
4049 * kauth_proc_label_update
4050 *
4051 * Description: Update the label inside the credential associated with the process.
4052 *
4053 * Parameters: p The process to modify
4054 * label The label to place in the process credential
4055 *
4056 * Notes: The credential associated with the process may change as a result
4057 * of this call. The caller should not assume the process reference to
4058 * the old credential still exists.
4059 */
4060 int kauth_proc_label_update(struct proc *p, struct label *label)
4061 {
4062 kauth_cred_t my_cred, my_new_cred;
4063
4064 my_cred = kauth_cred_proc_ref(p);
4065
4066 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred);
4067
4068 /* get current credential and take a reference while we muck with it */
4069 for (;;) {
4070
4071 /*
4072 * Set the credential with new info. If there is no change,
4073 * we get back the same credential we passed in; if there is
4074 * a change, we drop the reference on the credential we
4075 * passed in. The subsequent compare is safe, because it is
4076 * a pointer compare rather than a contents compare.
4077 */
4078 my_new_cred = kauth_cred_label_update(my_cred, label);
4079 if (my_cred != my_new_cred) {
4080
4081 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);
4082
4083 proc_lock(p);
4084 /*
4085 * We need to protect for a race where another thread
4086 * also changed the credential after we took our
4087 * reference. If p_ucred has changed then we should
4088 * restart this again with the new cred.
4089 */
4090 if (p->p_ucred != my_cred) {
4091 proc_unlock(p);
4092 kauth_cred_unref(&my_new_cred);
4093 my_cred = kauth_cred_proc_ref(p);
4094 /* try again */
4095 continue;
4096 }
4097 p->p_ucred = my_new_cred;
4098 /* update cred on proc */
4099 PROC_UPDATE_CREDS_ONPROC(p);
4100
4101 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4102 proc_unlock(p);
4103 }
4104 break;
4105 }
4106 /* Drop old proc reference or our extra reference */
4107 kauth_cred_unref(&my_cred);
4108
4109 return (0);
4110 }
4111
4112 /*
4113 * kauth_proc_label_update_execve
4114 *
4115 * Description: Update the label inside the credential associated with the
4116 * process as part of a transitioning execve. The label will
4117 * be updated by the policies as part of this processing, not
4118 * provided up front.
4119 *
4120 * Parameters: p The process to modify
4121 * ctx The context of the exec
4122 * vp The vnode being exec'ed
4123 * scriptl The script MAC label
4124 * execl The executable MAC label
4125 *
4126 * Returns: 0 Label update did not make credential
4127 * disjoint
4128 * 1 Label update caused credential to be
4129 * disjoint
4130 *
4131 * Notes: The credential associated with the process WILL change as a
4132 * result of this call. The caller should not assume the process
4133 * reference to the old credential still exists.
4134 */
4135 int
4136 kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx,
4137 struct vnode *vp, struct label *scriptl, struct label *execl)
4138 {
4139 kauth_cred_t my_cred, my_new_cred;
4140 int disjoint = 0;
4141
4142 my_cred = kauth_cred_proc_ref(p);
4143
4144 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred);
4145
4146 /* get current credential and take a reference while we muck with it */
4147 for (;;) {
4148
4149 /*
4150 * Set the credential with new info. If there is no change,
4151 * we get back the same credential we passed in; if there is
4152 * a change, we drop the reference on the credential we
4153 * passed in. The subsequent compare is safe, because it is
4154 * a pointer compare rather than a contents compare.
4155 */
4156 my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, scriptl, execl, &disjoint);
4157 if (my_cred != my_new_cred) {
4158
4159 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);
4160
4161 proc_lock(p);
4162 /*
4163 * We need to protect for a race where another thread
4164 * also changed the credential after we took our
4165 * reference. If p_ucred has changed then we should
4166 * restart this again with the new cred.
4167 */
4168 if (p->p_ucred != my_cred) {
4169 proc_unlock(p);
4170 kauth_cred_unref(&my_new_cred);
4171 my_cred = kauth_cred_proc_ref(p);
4172 /* try again */
4173 continue;
4174 }
4175 p->p_ucred = my_new_cred;
4176 /* update cred on proc */
4177 PROC_UPDATE_CREDS_ONPROC(p);
4178 mac_proc_set_enforce(p, MAC_ALL_ENFORCE);
4179 proc_unlock(p);
4180 }
4181 break;
4182 }
4183 /* Drop old proc reference or our extra reference */
4184 kauth_cred_unref(&my_cred);
4185
4186 return (disjoint);
4187 }
4188
4189 #if 1
4190 /*
4191 * for temporary binary compatibility
4192 */
4193 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label);
4194 kauth_cred_t
4195 kauth_cred_setlabel(kauth_cred_t cred, struct label *label)
4196 {
4197 return kauth_cred_label_update(cred, label);
4198 }
4199
4200 int kauth_proc_setlabel(struct proc *p, struct label *label);
4201 int
4202 kauth_proc_setlabel(struct proc *p, struct label *label)
4203 {
4204 return kauth_proc_label_update(p, label);
4205 }
4206 #endif
4207
4208 #else
4209
4210 /* this is a temp hack to cover us when MACF is not built in a kernel configuration.
4211 * Since we cannot build our export lists based on the kernel configuration we need
4212 * to define a stub.
4213 */
4214 kauth_cred_t
4215 kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label)
4216 {
4217 return(NULL);
4218 }
4219
4220 int
4221 kauth_proc_label_update(__unused struct proc *p, __unused void *label)
4222 {
4223 return (0);
4224 }
4225
4226 #if 1
4227 /*
4228 * for temporary binary compatibility
4229 */
4230 kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label);
4231 kauth_cred_t
4232 kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label)
4233 {
4234 return NULL;
4235 }
4236
4237 int kauth_proc_setlabel(struct proc *p, void *label);
4238 int
4239 kauth_proc_setlabel(__unused struct proc *p, __unused void *label)
4240 {
4241 return (0);
4242 }
4243 #endif
4244 #endif
4245
4246 /*
4247 * kauth_cred_ref
4248 *
4249 * Description: Add a reference to the passed credential
4250 *
4251 * Parameters: cred The credential to reference
4252 *
4253 * Returns: (void)
4254 *
4255 * Notes: This function adds a reference to the provided credential;
4256 * the existing reference on the credential is assumed to be
4257 * held stable over this operation by taking the appropriate
4258 * lock to protect the pointer from which it is being referenced,
4259 * if necessary (e.g. the proc lock is held over the call if the
4260 * credential being referenced is from p_ucred, the vnode lock
4261 * if from the per vnode name cache cred cache, and so on).
4262 *
4263 * This is safe from the kauth_cred_unref() path, since an atomic
4264 * add is used, and the unref path specifically checks to see that
4265 * the value has not been changed to add a reference between the
4266 * time the credential is unreferenced by another pointer and the
4267 * time it is unreferenced from the cred hash cache.
4268 */
4269 void
4270 kauth_cred_ref(kauth_cred_t cred)
4271 {
4272 int old_value;
4273
4274 NULLCRED_CHECK(cred);
4275
4276 old_value = OSAddAtomicLong(1, (long*)&cred->cr_ref);
4277
4278 if (old_value < 1)
4279 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
4280
4281 #if 0 // use this to watch a specific credential
4282 if ( is_target_cred( cred ) != 0 ) {
4283 get_backtrace( );
4284 }
4285 #endif
4286
4287 return;
4288 }
4289
4290
4291 /*
4292 * kauth_cred_unref_hashlocked
4293 *
4294 * Description: release a credential reference; when the last reference is
4295 * released, the credential will be freed.
4296 *
4297 * Parameters: credp Pointer to address containing
4298 * credential to be freed
4299 *
4300 * Returns: (void)
4301 *
4302 * Implicit returns:
4303 * *credp Set to NOCRED
4304 *
4305 * Notes: This function assumes the credential hash lock is held.
4306 *
4307 * This function is internal use only, since the hash lock is
4308 * scoped to this compilation unit.
4309 *
4310 * This function destroys the contents of the pointer passed by
4311 * the caller to prevent the caller accidently attempting to
4312 * release a given reference twice in error.
4313 *
4314 * The last reference is considered to be released when a release
4315 * of a credential of a reference count of 2 occurs; this is an
4316 * intended effect, to take into accout the reference held by
4317 * the credential hash, which is released at the same time.
4318 */
4319 static void
4320 kauth_cred_unref_hashlocked(kauth_cred_t *credp)
4321 {
4322 int old_value;
4323
4324 KAUTH_CRED_HASH_LOCK_ASSERT();
4325 NULLCRED_CHECK(*credp);
4326
4327 old_value = OSAddAtomicLong(-1, (long*)&(*credp)->cr_ref);
4328
4329 #if DIAGNOSTIC
4330 if (old_value == 0)
4331 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
4332 if (old_value == 1)
4333 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
4334 #endif
4335
4336 #if 0 // use this to watch a specific credential
4337 if ( is_target_cred( *credp ) != 0 ) {
4338 get_backtrace( );
4339 }
4340 #endif
4341
4342 /*
4343 * If the old_value is 2, then we have just released the last external
4344 * reference to this credential
4345 */
4346 if (old_value < 3) {
4347 /* The last absolute reference is our credential hash table */
4348 kauth_cred_remove(*credp);
4349 }
4350 *credp = NOCRED;
4351 }
4352
4353
4354 /*
4355 * kauth_cred_unref
4356 *
4357 * Description: Release a credential reference while holding the credential
4358 * hash lock; when the last reference is released, the credential
4359 * will be freed.
4360 *
4361 * Parameters: credp Pointer to address containing
4362 * credential to be freed
4363 *
4364 * Returns: (void)
4365 *
4366 * Implicit returns:
4367 * *credp Set to NOCRED
4368 *
4369 * Notes: See kauth_cred_unref_hashlocked() for more information.
4370 *
4371 */
4372 void
4373 kauth_cred_unref(kauth_cred_t *credp)
4374 {
4375 KAUTH_CRED_HASH_LOCK();
4376 kauth_cred_unref_hashlocked(credp);
4377 KAUTH_CRED_HASH_UNLOCK();
4378 }
4379
4380
4381 #ifndef __LP64__
4382 /*
4383 * kauth_cred_rele
4384 *
4385 * Description: release a credential reference; when the last reference is
4386 * released, the credential will be freed
4387 *
4388 * Parameters: cred Credential to release
4389 *
4390 * Returns: (void)
4391 *
4392 * DEPRECATED: This interface is obsolete due to a failure to clear out the
4393 * clear the pointer in the caller to avoid multiple releases of
4394 * the same credential. The currently recommended interface is
4395 * kauth_cred_unref().
4396 */
4397 void
4398 kauth_cred_rele(kauth_cred_t cred)
4399 {
4400 kauth_cred_unref(&cred);
4401 }
4402 #endif /* !__LP64__ */
4403
4404
4405 /*
4406 * kauth_cred_dup
4407 *
4408 * Description: Duplicate a credential via alloc and copy; the new credential
4409 * has only it's own
4410 *
4411 * Parameters: cred The credential to duplicate
4412 *
4413 * Returns: (kauth_cred_t) The duplicate credential
4414 *
4415 * Notes: The typical value to calling this routine is if you are going
4416 * to modify an existing credential, and expect to need a new one
4417 * from the hash cache.
4418 *
4419 * This should probably not be used in the majority of cases;
4420 * if you are using it instead of kauth_cred_create(), you are
4421 * likely making a mistake.
4422 *
4423 * The newly allocated credential is copied as part of the
4424 * allocation process, with the exception of the reference
4425 * count, which is set to 1 to indicate a single reference
4426 * held by the caller.
4427 *
4428 * Since newly allocated credentials have no external pointers
4429 * referencing them, prior to making them visible in an externally
4430 * visible pointer (e.g. by adding them to the credential hash
4431 * cache) is the only legal time in which an existing credential
4432 * can be safely iinitialized or modified directly.
4433 *
4434 * After initialization, the caller is expected to call the
4435 * function kauth_cred_add() to add the credential to the hash
4436 * cache, after which time it's frozen and becomes publically
4437 * visible.
4438 *
4439 * The release protocol depends on kauth_hash_add() being called
4440 * before kauth_cred_rele() (there is a diagnostic panic which
4441 * will trigger if this protocol is not observed).
4442 *
4443 */
4444 kauth_cred_t
4445 kauth_cred_dup(kauth_cred_t cred)
4446 {
4447 kauth_cred_t newcred;
4448 #if CONFIG_MACF
4449 struct label *temp_label;
4450 #endif
4451
4452 #if CRED_DIAGNOSTIC
4453 if (cred == NOCRED || cred == FSCRED)
4454 panic("kauth_cred_dup: bad credential");
4455 #endif
4456 newcred = kauth_cred_alloc();
4457 if (newcred != NULL) {
4458 #if CONFIG_MACF
4459 temp_label = newcred->cr_label;
4460 #endif
4461 bcopy(cred, newcred, sizeof(*newcred));
4462 #if CONFIG_MACF
4463 newcred->cr_label = temp_label;
4464 mac_cred_label_associate(cred, newcred);
4465 #endif
4466 AUDIT_SESSION_REF(cred);
4467 newcred->cr_ref = 1;
4468 }
4469 return(newcred);
4470 }
4471
4472 /*
4473 * kauth_cred_copy_real
4474 *
4475 * Description: Returns a credential based on the passed credential but which
4476 * reflects the real rather than effective UID and GID.
4477 *
4478 * Parameters: cred The credential from which to
4479 * derive the new credential
4480 *
4481 * Returns: (kauth_cred_t) The copied credential
4482 *
4483 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a
4484 * result, the caller is responsible for dropping BOTH the
4485 * additional reference on the passed cred (if any), and the
4486 * credential returned by this function. The drop should be
4487 * via the satnadr kauth_cred_unref() KPI.
4488 */
4489 kauth_cred_t
4490 kauth_cred_copy_real(kauth_cred_t cred)
4491 {
4492 kauth_cred_t newcred = NULL, found_cred;
4493 struct ucred temp_cred;
4494 posix_cred_t temp_pcred = posix_cred_get(&temp_cred);
4495 posix_cred_t pcred = posix_cred_get(cred);
4496
4497 /* if the credential is already 'real', just take a reference */
4498 if ((pcred->cr_ruid == pcred->cr_uid) &&
4499 (pcred->cr_rgid == pcred->cr_gid)) {
4500 kauth_cred_ref(cred);
4501 return(cred);
4502 }
4503
4504 /*
4505 * Look up in cred hash table to see if we have a matching credential
4506 * with the new values.
4507 */
4508 bcopy(cred, &temp_cred, sizeof(temp_cred));
4509 temp_pcred->cr_uid = pcred->cr_ruid;
4510 /* displacing a supplementary group opts us out of memberd */
4511 if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) {
4512 temp_pcred->cr_flags |= CRF_NOMEMBERD;
4513 temp_pcred->cr_gmuid = KAUTH_UID_NONE;
4514 }
4515 /*
4516 * If the cred is not opted out, make sure we are using the r/euid
4517 * for group checks
4518 */
4519 if (temp_pcred->cr_gmuid != KAUTH_UID_NONE)
4520 temp_pcred->cr_gmuid = pcred->cr_ruid;
4521
4522 for (;;) {
4523 int err;
4524
4525 KAUTH_CRED_HASH_LOCK();
4526 found_cred = kauth_cred_find(&temp_cred);
4527 if (found_cred == cred) {
4528 /* same cred so just bail */
4529 KAUTH_CRED_HASH_UNLOCK();
4530 return(cred);
4531 }
4532 if (found_cred != NULL) {
4533 /*
4534 * Found a match so we bump reference count on new
4535 * one. We leave the old one alone.
4536 */
4537 kauth_cred_ref(found_cred);
4538 KAUTH_CRED_HASH_UNLOCK();
4539 return(found_cred);
4540 }
4541
4542 /*
4543 * Must allocate a new credential, copy in old credential
4544 * data and update the real user and group IDs.
4545 */
4546 newcred = kauth_cred_dup(&temp_cred);
4547 err = kauth_cred_add(newcred);
4548 KAUTH_CRED_HASH_UNLOCK();
4549
4550 /* Retry if kauth_cred_add() fails */
4551 if (err == 0)
4552 break;
4553 #if CONFIG_MACF
4554 mac_cred_label_destroy(newcred);
4555 #endif
4556 AUDIT_SESSION_UNREF(newcred);
4557
4558 FREE_ZONE(newcred, sizeof(*newcred), M_CRED);
4559 newcred = NULL;
4560 }
4561
4562 return(newcred);
4563 }
4564
4565
4566 /*
4567 * kauth_cred_update
4568 *
4569 * Description: Common code to update a credential
4570 *
4571 * Parameters: old_cred Reference counted credential
4572 * to update
4573 * model_cred Non-reference counted model
4574 * credential to apply to the
4575 * credential to be updated
4576 * retain_auditinfo Flag as to whether or not the
4577 * audit information should be
4578 * copied from the old_cred into
4579 * the model_cred
4580 *
4581 * Returns: (kauth_cred_t) The updated credential
4582 *
4583 * IMPORTANT: This function will potentially return a credential other than
4584 * the one it is passed, and if so, it will have dropped the
4585 * reference on the passed credential. All callers should be
4586 * aware of this, and treat this function as an unref + ref,
4587 * potentially on different credentials.
4588 *
4589 * Because of this, the caller is expected to take its own
4590 * reference on the credential passed as the first parameter,
4591 * and be prepared to release the reference on the credential
4592 * that is returned to them, if it is not intended to be a
4593 * persistent reference.
4594 */
4595 static kauth_cred_t
4596 kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred,
4597 boolean_t retain_auditinfo)
4598 {
4599 kauth_cred_t found_cred, new_cred = NULL;
4600
4601 /*
4602 * Make sure we carry the auditinfo forward to the new credential
4603 * unless we are actually updating the auditinfo.
4604 */
4605 if (retain_auditinfo) {
4606 bcopy(&old_cred->cr_audit, &model_cred->cr_audit,
4607 sizeof(model_cred->cr_audit));
4608 }
4609
4610 for (;;) {
4611 int err;
4612
4613 KAUTH_CRED_HASH_LOCK();
4614 found_cred = kauth_cred_find(model_cred);
4615 if (found_cred == old_cred) {
4616 /* same cred so just bail */
4617 KAUTH_CRED_HASH_UNLOCK();
4618 return(old_cred);
4619 }
4620 if (found_cred != NULL) {
4621 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred);
4622 /*
4623 * Found a match so we bump reference count on new
4624 * one and decrement reference count on the old one.
4625 */
4626 kauth_cred_ref(found_cred);
4627 kauth_cred_unref_hashlocked(&old_cred);
4628 KAUTH_CRED_HASH_UNLOCK();
4629 return(found_cred);
4630 }
4631
4632 /*
4633 * Must allocate a new credential using the model. also
4634 * adds the new credential to the credential hash table.
4635 */
4636 new_cred = kauth_cred_dup(model_cred);
4637 err = kauth_cred_add(new_cred);
4638 KAUTH_CRED_HASH_UNLOCK();
4639
4640 /* retry if kauth_cred_add returns non zero value */
4641 if (err == 0)
4642 break;
4643 #if CONFIG_MACF
4644 mac_cred_label_destroy(new_cred);
4645 #endif
4646 AUDIT_SESSION_UNREF(new_cred);
4647
4648 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED);
4649 new_cred = NULL;
4650 }
4651
4652 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred);
4653 kauth_cred_unref(&old_cred);
4654 return(new_cred);
4655 }
4656
4657
4658 /*
4659 * kauth_cred_add
4660 *
4661 * Description: Add the given credential to our credential hash table and
4662 * take an additional reference to account for our use of the
4663 * credential in the hash table
4664 *
4665 * Parameters: new_cred Credential to insert into cred
4666 * hash cache
4667 *
4668 * Returns: 0 Success
4669 * -1 Hash insertion failed: caller
4670 * should retry
4671 *
4672 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4673 *
4674 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache
4675 */
4676 static int
4677 kauth_cred_add(kauth_cred_t new_cred)
4678 {
4679 u_long hash_key;
4680
4681 KAUTH_CRED_HASH_LOCK_ASSERT();
4682
4683 hash_key = kauth_cred_get_hashkey(new_cred);
4684 hash_key %= kauth_cred_table_size;
4685
4686 /* race fix - there is a window where another matching credential
4687 * could have been inserted between the time this one was created and we
4688 * got the hash lock. If we find a match return an error and have the
4689 * the caller retry.
4690 */
4691 if (kauth_cred_find(new_cred) != NULL) {
4692 return(-1);
4693 }
4694
4695 /* take a reference for our use in credential hash table */
4696 kauth_cred_ref(new_cred);
4697
4698 /* insert the credential into the hash table */
4699 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
4700
4701 return(0);
4702 }
4703
4704
4705 /*
4706 * kauth_cred_remove
4707 *
4708 * Description: Remove the given credential from our credential hash table
4709 *
4710 * Parameters: cred Credential to remove from cred
4711 * hash cache
4712 *
4713 * Returns: (void)
4714 *
4715 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4716 *
4717 * Notes: The check for the reference increment after entry is generally
4718 * agree to be safe, since we use atomic operations, and the
4719 * following code occurs with the hash lock held; in theory, this
4720 * protects us from the 2->1 reference that gets us here.
4721 */
4722 static void
4723 kauth_cred_remove(kauth_cred_t cred)
4724 {
4725 u_long hash_key;
4726 kauth_cred_t found_cred;
4727
4728 hash_key = kauth_cred_get_hashkey(cred);
4729 hash_key %= kauth_cred_table_size;
4730
4731 /* Avoid race */
4732 if (cred->cr_ref < 1)
4733 panic("cred reference underflow");
4734 if (cred->cr_ref > 1)
4735 return; /* someone else got a ref */
4736
4737 /* Find cred in the credential hash table */
4738 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
4739 if (found_cred == cred) {
4740 /* found a match, remove it from the hash table */
4741 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
4742 #if CONFIG_MACF
4743 mac_cred_label_destroy(cred);
4744 #endif
4745 AUDIT_SESSION_UNREF(cred);
4746
4747 cred->cr_ref = 0;
4748 FREE_ZONE(cred, sizeof(*cred), M_CRED);
4749 #if KAUTH_CRED_HASH_DEBUG
4750 kauth_cred_count--;
4751 #endif
4752 return;
4753 }
4754 }
4755
4756 /* Did not find a match... this should not happen! XXX Make panic? */
4757 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
4758 return;
4759 }
4760
4761
4762 /*
4763 * kauth_cred_find
4764 *
4765 * Description: Using the given credential data, look for a match in our
4766 * credential hash table
4767 *
4768 * Parameters: cred Credential to lookup in cred
4769 * hash cache
4770 *
4771 * Returns: NULL Not found
4772 * !NULL Matching cedential already in
4773 * cred hash cache
4774 *
4775 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK
4776 */
4777 kauth_cred_t
4778 kauth_cred_find(kauth_cred_t cred)
4779 {
4780 u_long hash_key;
4781 kauth_cred_t found_cred;
4782 posix_cred_t pcred = posix_cred_get(cred);
4783
4784 KAUTH_CRED_HASH_LOCK_ASSERT();
4785
4786 #if KAUTH_CRED_HASH_DEBUG
4787 static int test_count = 0;
4788
4789 test_count++;
4790 if ((test_count % 200) == 0) {
4791 kauth_cred_hash_print();
4792 }
4793 #endif
4794
4795 hash_key = kauth_cred_get_hashkey(cred);
4796 hash_key %= kauth_cred_table_size;
4797
4798 /* Find cred in the credential hash table */
4799 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
4800 boolean_t match;
4801 posix_cred_t found_pcred = posix_cred_get(found_cred);
4802
4803 /*
4804 * don't worry about the label unless the flags in
4805 * either credential tell us to.
4806 */
4807 if ((found_pcred->cr_flags & CRF_MAC_ENFORCE) != 0 ||
4808 (pcred->cr_flags & CRF_MAC_ENFORCE) != 0) {
4809 /* include the label pointer in the compare */
4810 match = (bcmp(&found_pcred->cr_uid, &pcred->cr_uid,
4811 (sizeof(struct ucred) -
4812 offsetof(struct ucred, cr_posix))) == 0);
4813 } else {
4814 /* flags have to match, but skip the label in bcmp */
4815 match = (found_pcred->cr_flags == pcred->cr_flags &&
4816 bcmp(&found_pcred->cr_uid, &pcred->cr_uid,
4817 sizeof(struct posix_cred)) == 0 &&
4818 bcmp(&found_cred->cr_audit, &cred->cr_audit,
4819 sizeof(cred->cr_audit)) == 0);
4820
4821 }
4822 if (match) {
4823 /* found a match */
4824 return(found_cred);
4825 }
4826 }
4827 /* No match found */
4828
4829 return(NULL);
4830 }
4831
4832
4833 /*
4834 * kauth_cred_hash
4835 *
4836 * Description: Generates a hash key using data that makes up a credential;
4837 * based on ElfHash
4838 *
4839 * Parameters: datap Pointer to data to hash
4840 * data_len Count of bytes to hash
4841 * start_key Start key value
4842 *
4843 * Returns: (u_long) Returned hash key
4844 */
4845 static inline u_long
4846 kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
4847 {
4848 u_long hash_key = start_key;
4849 u_long temp;
4850
4851 while (data_len > 0) {
4852 hash_key = (hash_key << 4) + *datap++;
4853 temp = hash_key & 0xF0000000;
4854 if (temp) {
4855 hash_key ^= temp >> 24;
4856 }
4857 hash_key &= ~temp;
4858 data_len--;
4859 }
4860 return(hash_key);
4861 }
4862
4863
4864 /*
4865 * kauth_cred_get_hashkey
4866 *
4867 * Description: Generate a hash key using data that makes up a credential;
4868 * based on ElfHash. We hash on the entire credential data,
4869 * not including the ref count or the TAILQ, which are mutable;
4870 * everything else isn't.
4871 *
4872 * Parameters: cred Credential for which hash is
4873 * desired
4874 *
4875 * Returns: (u_long) Returned hash key
4876 *
4877 * Notes: When actually moving the POSIX credential into a real label,
4878 * remember to update this hash computation.
4879 */
4880 static u_long
4881 kauth_cred_get_hashkey(kauth_cred_t cred)
4882 {
4883 posix_cred_t pcred = posix_cred_get(cred);
4884 u_long hash_key = 0;
4885
4886 if (pcred->cr_flags & CRF_MAC_ENFORCE) {
4887 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix,
4888 sizeof(struct ucred) - offsetof(struct ucred, cr_posix),
4889 hash_key);
4890 } else {
4891 /* skip label */
4892 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix,
4893 sizeof(struct posix_cred),
4894 hash_key);
4895 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_audit,
4896 sizeof(struct au_session),
4897 hash_key);
4898 }
4899 return(hash_key);
4900 }
4901
4902
4903 #if KAUTH_CRED_HASH_DEBUG
4904 /*
4905 * kauth_cred_hash_print
4906 *
4907 * Description: Print out cred hash cache table information for debugging
4908 * purposes, including the credential contents
4909 *
4910 * Parameters: (void)
4911 *
4912 * Returns: (void)
4913 *
4914 * Implicit returns: Results in console output
4915 */
4916 static void
4917 kauth_cred_hash_print(void)
4918 {
4919 int i, j;
4920 kauth_cred_t found_cred;
4921
4922 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
4923 /* count slot hits, misses, collisions, and max depth */
4924 for (i = 0; i < kauth_cred_table_size; i++) {
4925 printf("[%02d] ", i);
4926 j = 0;
4927 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
4928 if (j > 0) {
4929 printf("---- ");
4930 }
4931 j++;
4932 kauth_cred_print(found_cred);
4933 printf("\n");
4934 }
4935 if (j == 0) {
4936 printf("NOCRED \n");
4937 }
4938 }
4939 }
4940 #endif /* KAUTH_CRED_HASH_DEBUG */
4941
4942
4943 #if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED)
4944 /*
4945 * kauth_cred_print
4946 *
4947 * Description: Print out an individual credential's contents for debugging
4948 * purposes
4949 *
4950 * Parameters: cred The credential to print out
4951 *
4952 * Returns: (void)
4953 *
4954 * Implicit returns: Results in console output
4955 */
4956 void
4957 kauth_cred_print(kauth_cred_t cred)
4958 {
4959 int i;
4960
4961 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);
4962 printf("group count %d gids ", cred->cr_ngroups);
4963 for (i = 0; i < NGROUPS; i++) {
4964 if (i == 0)
4965 printf("e");
4966 printf("%d ", cred->cr_groups[i]);
4967 }
4968 printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid);
4969 printf("auditinfo_addr %d %d %d %d %d %d\n",
4970 cred->cr_audit.s_aia_p->ai_auid,
4971 cred->cr_audit.as_mask.am_success,
4972 cred->cr_audit.as_mask.am_failure,
4973 cred->cr_audit.as_aia_p->ai_termid.at_port,
4974 cred->cr_audit.as_aia_p->ai_termid.at_addr[0],
4975 cred->cr_audit.as_aia_p->ai_asid);
4976 }
4977
4978 int is_target_cred( kauth_cred_t the_cred )
4979 {
4980 if ( the_cred->cr_uid != 0 )
4981 return( 0 );
4982 if ( the_cred->cr_ruid != 0 )
4983 return( 0 );
4984 if ( the_cred->cr_svuid != 0 )
4985 return( 0 );
4986 if ( the_cred->cr_ngroups != 11 )
4987 return( 0 );
4988 if ( the_cred->cr_groups[0] != 11 )
4989 return( 0 );
4990 if ( the_cred->cr_groups[1] != 81 )
4991 return( 0 );
4992 if ( the_cred->cr_groups[2] != 63947 )
4993 return( 0 );
4994 if ( the_cred->cr_groups[3] != 80288 )
4995 return( 0 );
4996 if ( the_cred->cr_groups[4] != 89006 )
4997 return( 0 );
4998 if ( the_cred->cr_groups[5] != 52173 )
4999 return( 0 );
5000 if ( the_cred->cr_groups[6] != 84524 )
5001 return( 0 );
5002 if ( the_cred->cr_groups[7] != 79 )
5003 return( 0 );
5004 if ( the_cred->cr_groups[8] != 80292 )
5005 return( 0 );
5006 if ( the_cred->cr_groups[9] != 80 )
5007 return( 0 );
5008 if ( the_cred->cr_groups[10] != 90824 )
5009 return( 0 );
5010 if ( the_cred->cr_rgid != 11 )
5011 return( 0 );
5012 if ( the_cred->cr_svgid != 11 )
5013 return( 0 );
5014 if ( the_cred->cr_gmuid != 3475 )
5015 return( 0 );
5016 if ( the_cred->cr_audit.as_aia_p->ai_auid != 3475 )
5017 return( 0 );
5018 /*
5019 if ( the_cred->cr_audit.as_mask.am_success != 0 )
5020 return( 0 );
5021 if ( the_cred->cr_audit.as_mask.am_failure != 0 )
5022 return( 0 );
5023 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 )
5024 return( 0 );
5025 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 )
5026 return( 0 );
5027 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 )
5028 return( 0 );
5029 if ( the_cred->cr_flags != 0 )
5030 return( 0 );
5031 */
5032 return( -1 ); // found target cred
5033 }
5034
5035 void get_backtrace( void )
5036 {
5037 int my_slot;
5038 void * my_stack[ MAX_STACK_DEPTH ];
5039 int i, my_depth;
5040
5041 if ( cred_debug_buf_p == NULL ) {
5042 MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK);
5043 bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p));
5044 }
5045
5046 if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) {
5047 /* buffer is full */
5048 return;
5049 }
5050
5051 my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH);
5052 if ( my_depth == 0 ) {
5053 printf("%s - OSBacktrace failed \n", __FUNCTION__);
5054 return;
5055 }
5056
5057 /* fill new backtrace */
5058 my_slot = cred_debug_buf_p->next_slot;
5059 cred_debug_buf_p->next_slot++;
5060 cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth;
5061 for ( i = 0; i < my_depth; i++ ) {
5062 cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ];
5063 }
5064
5065 return;
5066 }
5067
5068
5069 /* subset of struct ucred for use in sysctl_dump_creds */
5070 struct debug_ucred {
5071 void *credp;
5072 u_long cr_ref; /* reference count */
5073 uid_t cr_uid; /* effective user id */
5074 uid_t cr_ruid; /* real user id */
5075 uid_t cr_svuid; /* saved user id */
5076 short cr_ngroups; /* number of groups in advisory list */
5077 gid_t cr_groups[NGROUPS]; /* advisory group list */
5078 gid_t cr_rgid; /* real group id */
5079 gid_t cr_svgid; /* saved group id */
5080 uid_t cr_gmuid; /* UID for group membership purposes */
5081 struct auditinfo_addr cr_audit; /* user auditing data. */
5082 void *cr_label; /* MACF label */
5083 int cr_flags; /* flags on credential */
5084 };
5085 typedef struct debug_ucred debug_ucred;
5086
5087 SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD,
5088 NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash");
5089
5090 /* accessed by:
5091 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 );
5092 */
5093
5094 static int
5095 sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5096 {
5097 int i, j, counter = 0;
5098 int error;
5099 size_t space;
5100 kauth_cred_t found_cred;
5101 debug_ucred * cred_listp;
5102 debug_ucred * nextp;
5103
5104 /* This is a readonly node. */
5105 if (req->newptr != USER_ADDR_NULL)
5106 return (EPERM);
5107
5108 /* calculate space needed */
5109 for (i = 0; i < kauth_cred_table_size; i++) {
5110 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5111 counter++;
5112 }
5113 }
5114
5115 /* they are querying us so just return the space required. */
5116 if (req->oldptr == USER_ADDR_NULL) {
5117 counter += 10; // add in some padding;
5118 req->oldidx = counter * sizeof(debug_ucred);
5119 return 0;
5120 }
5121
5122 MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK );
5123 if ( cred_listp == NULL ) {
5124 return (ENOMEM);
5125 }
5126
5127 /* fill in creds to send back */
5128 nextp = cred_listp;
5129 space = 0;
5130 for (i = 0; i < kauth_cred_table_size; i++) {
5131 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
5132 nextp->credp = found_cred;
5133 nextp->cr_ref = found_cred->cr_ref;
5134 nextp->cr_uid = found_cred->cr_uid;
5135 nextp->cr_ruid = found_cred->cr_ruid;
5136 nextp->cr_svuid = found_cred->cr_svuid;
5137 nextp->cr_ngroups = found_cred->cr_ngroups;
5138 for ( j = 0; j < nextp->cr_ngroups; j++ ) {
5139 nextp->cr_groups[ j ] = found_cred->cr_groups[ j ];
5140 }
5141 nextp->cr_rgid = found_cred->cr_rgid;
5142 nextp->cr_svgid = found_cred->cr_svgid;
5143 nextp->cr_gmuid = found_cred->cr_gmuid;
5144 nextp->cr_audit.ai_auid =
5145 found_cred->cr_audit.as_aia_p->ai_auid;
5146 nextp->cr_audit.ai_mask.am_success =
5147 found_cred->cr_audit.as_mask.am_success;
5148 nextp->cr_audit.ai_mask.am_failure =
5149 found_cred->cr_audit.as_mask.am_failure;
5150 nextp->cr_audit.ai_termid.at_port =
5151 found_cred->cr_audit.as_aia_p->ai_termid.at_port;
5152 nextp->cr_audit.ai_termid.at_type =
5153 found_cred->cr_audit.as_aia_p->ai_termid.at_type;
5154 nextp->cr_audit.ai_termid.at_addr[0] =
5155 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0];
5156 nextp->cr_audit.ai_termid.at_addr[1] =
5157 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1];
5158 nextp->cr_audit.ai_termid.at_addr[2] =
5159 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2];
5160 nextp->cr_audit.ai_termid.at_addr[3] =
5161 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3];
5162 nextp->cr_audit.ai_asid =
5163 found_cred->cr_audit.as_aia_p->ai_asid;
5164 nextp->cr_audit.ai_flags =
5165 found_cred->cr_audit.as_aia_p->ai_flags;
5166 nextp->cr_label = found_cred->cr_label;
5167 nextp->cr_flags = found_cred->cr_flags;
5168 nextp++;
5169 space += sizeof(debug_ucred);
5170 if ( space > req->oldlen ) {
5171 FREE(cred_listp, M_TEMP);
5172 return (ENOMEM);
5173 }
5174 }
5175 }
5176 req->oldlen = space;
5177 error = SYSCTL_OUT(req, cred_listp, req->oldlen);
5178 FREE(cred_listp, M_TEMP);
5179 return (error);
5180 }
5181
5182
5183 SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD,
5184 NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace");
5185
5186 /* accessed by:
5187 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 );
5188 */
5189
5190 static int
5191 sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req )
5192 {
5193 int i, j;
5194 int error;
5195 size_t space;
5196 cred_debug_buffer * bt_bufp;
5197 cred_backtrace * nextp;
5198
5199 /* This is a readonly node. */
5200 if (req->newptr != USER_ADDR_NULL)
5201 return (EPERM);
5202
5203 if ( cred_debug_buf_p == NULL ) {
5204 return (EAGAIN);
5205 }
5206
5207 /* calculate space needed */
5208 space = sizeof( cred_debug_buf_p->next_slot );
5209 space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot);
5210
5211 /* they are querying us so just return the space required. */
5212 if (req->oldptr == USER_ADDR_NULL) {
5213 req->oldidx = space;
5214 return 0;
5215 }
5216
5217 if ( space > req->oldlen ) {
5218 return (ENOMEM);
5219 }
5220
5221 MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK );
5222 if ( bt_bufp == NULL ) {
5223 return (ENOMEM);
5224 }
5225
5226 /* fill in backtrace info to send back */
5227 bt_bufp->next_slot = cred_debug_buf_p->next_slot;
5228 space = sizeof(bt_bufp->next_slot);
5229
5230 nextp = &bt_bufp->stack_buffer[ 0 ];
5231 for (i = 0; i < cred_debug_buf_p->next_slot; i++) {
5232 nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth;
5233 for ( j = 0; j < nextp->depth; j++ ) {
5234 nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ];
5235 }
5236 space += sizeof(*nextp);
5237 nextp++;
5238 }
5239 req->oldlen = space;
5240 error = SYSCTL_OUT(req, bt_bufp, req->oldlen);
5241 FREE(bt_bufp, M_TEMP);
5242 return (error);
5243 }
5244
5245 #endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */
5246
5247
5248 /*
5249 **********************************************************************
5250 * The following routines will be moved to a policy_posix.c module at
5251 * some future point.
5252 **********************************************************************
5253 */
5254
5255 /*
5256 * posix_cred_create
5257 *
5258 * Description: Helper function to create a kauth_cred_t credential that is
5259 * initally labelled with a specific POSIX credential label
5260 *
5261 * Parameters: pcred The posix_cred_t to use as the initial
5262 * label value
5263 *
5264 * Returns: (kauth_cred_t) The credential that was found in the
5265 * hash or creates
5266 * NULL kauth_cred_add() failed, or there was
5267 * no egid specified, or we failed to
5268 * attach a label to the new credential
5269 *
5270 * Notes: This function currently wraps kauth_cred_create(), and is the
5271 * only consume of tht ill-fated function, apart from bsd_init().
5272 * It exists solely to support the NFS server code creation of
5273 * credentials based on the over-the-wire RPC cals containing
5274 * traditional POSIX credential information being tunneled to
5275 * the server host from the client machine.
5276 *
5277 * In the future, we hope this function goes away.
5278 *
5279 * In the short term, it creates a temporary credential, puts
5280 * the POSIX information from NFS into it, and then calls
5281 * kauth_cred_create(), as an internal implementaiton detail.
5282 *
5283 * If we have to keep it around in the medium term, it will
5284 * create a new kauth_cred_t, then label it with a POSIX label
5285 * corresponding to the contents of the kauth_cred_t. If the
5286 * policy_posix MACF module is not loaded, it will instead
5287 * substitute a posix_cred_t which GRANTS all access (effectively
5288 * a "root" credential) in order to not prevent NFS from working
5289 * in the case that we are not supporting POSIX credentials.
5290 */
5291 kauth_cred_t
5292 posix_cred_create(posix_cred_t pcred)
5293 {
5294 struct ucred temp_cred;
5295
5296 bzero(&temp_cred, sizeof(temp_cred));
5297 temp_cred.cr_posix = *pcred;
5298
5299 return kauth_cred_create(&temp_cred);
5300 }
5301
5302
5303 /*
5304 * posix_cred_get
5305 *
5306 * Description: Given a kauth_cred_t, return the POSIX credential label, if
5307 * any, which is associated with it.
5308 *
5309 * Parameters: cred The credential to obtain the label from
5310 *
5311 * Returns: posix_cred_t The POSIX credential label
5312 *
5313 * Notes: In the event that the policy_posix MACF module IS NOT loaded,
5314 * this function will return a pointer to a posix_cred_t which
5315 * GRANTS all access (effectively, a "root" credential). This is
5316 * necessary to support legacy code which insists on tightly
5317 * integrating POSIX credentails into its APIs, including, but
5318 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms,
5319 * NFSv3, signals, dtrace, and a large number of kauth routines
5320 * used to implement POSIX permissions related system calls.
5321 *
5322 * In the event that the policy_posix MACF module IS loaded, and
5323 * there is no POSIX label on the kauth_cred_t credential, this
5324 * function will return a pointer to a posix_cred_t which DENIES
5325 * all access (effectively, a "deny rights granted by POSIX"
5326 * credential). This is necessary to support the concept of a
5327 * transiently loaded POSIX policy, or kauth_cred_t credentials
5328 * which can not be used in conjunctions with POSIX permissions
5329 * checks.
5330 *
5331 * This function currently returns the address of the cr_posix
5332 * field of the supplied kauth_cred_t credential, and as such
5333 * currently can not fail. In the future, this will not be the
5334 * case.
5335 */
5336 posix_cred_t
5337 posix_cred_get(kauth_cred_t cred)
5338 {
5339 return(&cred->cr_posix);
5340 }
5341
5342
5343 /*
5344 * posix_cred_label
5345 *
5346 * Description: Label a kauth_cred_t with a POSIX credential label
5347 *
5348 * Parameters: cred The credential to label
5349 * pcred The POSIX credential t label it with
5350 *
5351 * Returns: (void)
5352 *
5353 * Notes: This function is currently void in order to permit it to fit
5354 * in with the currrent MACF framework label methods which allow
5355 * labelling to fail silently. This is like acceptable for
5356 * mandatory access controls, but not for POSIX, since those
5357 * access controls are advisory. We will need to consider a
5358 * return value in a future version of the MACF API.
5359 *
5360 * This operation currenty can not fail, as currently the POSIX
5361 * credential is a subfield of the kauth_cred_t (ucred), which
5362 * MUST be valid. In the future, this will not be the case.
5363 */
5364 void
5365 posix_cred_label(kauth_cred_t cred, posix_cred_t pcred)
5366 {
5367 cred->cr_posix = *pcred; /* structure assign for now */
5368 }
5369
5370
5371 /*
5372 * posix_cred_access
5373 *
5374 * Description: Perform a POSIX access check for a protected object
5375 *
5376 * Parameters: cred The credential to check
5377 * object_uid The POSIX UID of the protected object
5378 * object_gid The POSIX GID of the protected object
5379 * object_mode The POSIX mode of the protected object
5380 * mode_req The requested POSIX access rights
5381 *
5382 * Returns 0 Access is granted
5383 * EACCES Access is denied
5384 *
5385 * Notes: This code optimizes the case where the world and group rights
5386 * would both grant the requested rights to avoid making a group
5387 * membership query. This is a big performance win in the case
5388 * where this is true.
5389 */
5390 int
5391 posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req)
5392 {
5393 int is_member;
5394 mode_t mode_owner = (object_mode & S_IRWXU);
5395 mode_t mode_group = (object_mode & S_IRWXG) << 3;
5396 mode_t mode_world = (object_mode & S_IRWXO) << 6;
5397
5398 /*
5399 * Check first for owner rights
5400 */
5401 if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req)
5402 return (0);
5403
5404 /*
5405 * Combined group and world rights check, if we don't have owner rights
5406 *
5407 * OPTIMIZED: If group and world rights would grant the same bits, and
5408 * they set of requested bits is in both, then we can simply check the
5409 * world rights, avoiding a group membership check, which is expensive.
5410 */
5411 if ((mode_req & mode_group & mode_world) == mode_req) {
5412 return (0);
5413 } else {
5414 /*
5415 * NON-OPTIMIZED: requires group membership check.
5416 */
5417 if ((mode_req & mode_group) != mode_req) {
5418 /*
5419 * exclusion group : treat errors as "is a member"
5420 *
5421 * NON-OPTIMIZED: +group would deny; must check group
5422 */
5423 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5424 /*
5425 * DENY: +group denies
5426 */
5427 return (EACCES);
5428 } else {
5429 if ((mode_req & mode_world) != mode_req) {
5430 /*
5431 * DENY: both -group & world would deny
5432 */
5433 return (EACCES);
5434 } else {
5435 /*
5436 * ALLOW: allowed by -group and +world
5437 */
5438 return (0);
5439 }
5440 }
5441 } else {
5442 /*
5443 * inclusion group; treat errors as "not a member"
5444 *
5445 * NON-OPTIMIZED: +group allows, world denies; must
5446 * check group
5447 */
5448 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) {
5449 /*
5450 * ALLOW: allowed by +group
5451 */
5452 return (0);
5453 } else {
5454 if ((mode_req & mode_world) != mode_req) {
5455 /*
5456 * DENY: both -group & world would deny
5457 */
5458 return (EACCES);
5459 } else {
5460 /*
5461 * ALLOW: allowed by -group and +world
5462 */
5463 return (0);
5464 }
5465 }
5466 }
5467 }
5468 }