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