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