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