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