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