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