]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_credential.c
c553ace8ed6314a3c0e43779df2d420ef61e0f91
[apple/xnu.git] / bsd / kern / kern_credential.c
1 /*
2 * Copyright (c) 2004 Apple Computer, 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 /*
30 * Kernel Authorization framework: Management of process/thread credentials and identity information.
31 */
32
33
34 #include <sys/param.h> /* XXX trim includes */
35 #include <sys/acct.h>
36 #include <sys/systm.h>
37 #include <sys/ucred.h>
38 #include <sys/proc_internal.h>
39 #include <sys/user.h>
40 #include <sys/timeb.h>
41 #include <sys/times.h>
42 #include <sys/malloc.h>
43 #include <sys/kauth.h>
44 #include <sys/kernel.h>
45
46 #include <bsm/audit_kernel.h>
47
48 #include <sys/mount.h>
49 #include <sys/sysproto.h>
50 #include <mach/message.h>
51 #include <mach/host_security.h>
52
53 #include <libkern/OSAtomic.h>
54
55 #include <kern/task.h>
56 #include <kern/lock.h>
57 #ifdef MACH_ASSERT
58 # undef MACH_ASSERT
59 #endif
60 #define MACH_ASSERT 1 /* XXX so bogus */
61 #include <kern/assert.h>
62
63 #define CRED_DIAGNOSTIC 1
64
65 # define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("bad credential %p", _c);} while(0)
66
67 /*
68 * Interface to external identity resolver.
69 *
70 * The architecture of the interface is simple; the external resolver calls in to
71 * get work, then calls back with completed work. It also calls us to let us know
72 * that it's (re)started, so that we can resubmit work if it times out.
73 */
74
75 static lck_mtx_t *kauth_resolver_mtx;
76 #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx);
77 #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
78
79 static volatile pid_t kauth_resolver_identity;
80 static int kauth_resolver_registered;
81 static uint32_t kauth_resolver_sequence;
82
83 struct kauth_resolver_work {
84 TAILQ_ENTRY(kauth_resolver_work) kr_link;
85 struct kauth_identity_extlookup kr_work;
86 uint32_t kr_seqno;
87 int kr_refs;
88 int kr_flags;
89 #define KAUTH_REQUEST_UNSUBMITTED (1<<0)
90 #define KAUTH_REQUEST_SUBMITTED (1<<1)
91 #define KAUTH_REQUEST_DONE (1<<2)
92 int kr_result;
93 };
94
95 TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
96 TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted;
97 TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done;
98
99 static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
100 static int kauth_resolver_complete(user_addr_t message);
101 static int kauth_resolver_getwork(user_addr_t message);
102
103 #define KAUTH_CRED_PRIMES_COUNT 7
104 static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = {97, 241, 397, 743, 1499, 3989, 7499};
105 static int kauth_cred_primes_index = 0;
106 static int kauth_cred_table_size = 0;
107
108 TAILQ_HEAD(kauth_cred_entry_head, ucred);
109 static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
110
111 #define KAUTH_CRED_HASH_DEBUG 0
112
113 static int kauth_cred_add(kauth_cred_t new_cred);
114 static void kauth_cred_remove(kauth_cred_t cred);
115 static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
116 static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
117 static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
118 static void kauth_cred_unref_hashlocked(kauth_cred_t *credp);
119
120 #if KAUTH_CRED_HASH_DEBUG
121 static int kauth_cred_count = 0;
122 static void kauth_cred_hash_print(void);
123 static void kauth_cred_print(kauth_cred_t cred);
124 #endif
125
126 void
127 kauth_resolver_init(void)
128 {
129 TAILQ_INIT(&kauth_resolver_unsubmitted);
130 TAILQ_INIT(&kauth_resolver_submitted);
131 TAILQ_INIT(&kauth_resolver_done);
132 kauth_resolver_sequence = 31337;
133 kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
134 }
135
136 /*
137 * Allocate a work queue entry, submit the work and wait for completion.
138 *
139 * XXX do we want an 'interruptible' flag vs. always being interruptible?
140 */
141 static int
142 kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
143 {
144 struct kauth_resolver_work *workp, *killp;
145 struct timespec ts;
146 int error, shouldfree;
147
148 /* no point actually blocking if the resolver isn't up yet */
149 if (kauth_resolver_identity == 0) {
150 /*
151 * We've already waited an initial 30 seconds with no result.
152 * Sleep on a stack address so no one wakes us before timeout;
153 * we sleep a half a second in case we are a high priority
154 * process, so that memberd doesn't starve while we are in a
155 * tight loop between user and kernel, eating all the CPU.
156 */
157 error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2);
158 if (kauth_resolver_identity == 0) {
159 /*
160 * if things haven't changed while we were asleep,
161 * tell the caller we couldn't get an authoritative
162 * answer.
163 */
164 return(EWOULDBLOCK);
165 }
166 }
167
168 MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK);
169 if (workp == NULL)
170 return(ENOMEM);
171
172 workp->kr_work = *lkp;
173 workp->kr_refs = 1;
174 workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
175 workp->kr_result = 0;
176
177 /*
178 * We insert the request onto the unsubmitted queue, the call in from the
179 * resolver will it to the submitted thread when appropriate.
180 */
181 KAUTH_RESOLVER_LOCK();
182 workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++;
183 workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
184
185 /* XXX as an optimisation, we could check the queue for identical items and coalesce */
186 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
187
188 wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
189 for (;;) {
190 /* we could compute a better timeout here */
191 ts.tv_sec = 30;
192 ts.tv_nsec = 0;
193 error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
194 /* request has been completed? */
195 if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
196 break;
197 /* woken because the resolver has died? */
198 if (kauth_resolver_identity == 0) {
199 error = EIO;
200 break;
201 }
202 /* an error? */
203 if (error != 0)
204 break;
205 }
206 /* if the request was processed, copy the result */
207 if (error == 0)
208 *lkp = workp->kr_work;
209
210 /*
211 * If the request timed out and was never collected, the resolver is dead and
212 * probably not coming back anytime soon. In this case we revert to no-resolver
213 * behaviour, and punt all the other sleeping requests to clear the backlog.
214 */
215 if ((error == EWOULDBLOCK) && (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED)) {
216 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
217 kauth_resolver_identity = 0;
218 /* kill all the other requestes that are waiting as well */
219 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
220 wakeup(killp);
221 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
222 wakeup(killp);
223 }
224
225 /* drop our reference on the work item, and note whether we should free it or not */
226 if (--workp->kr_refs <= 0) {
227 /* work out which list we have to remove it from */
228 if (workp->kr_flags & KAUTH_REQUEST_DONE) {
229 TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link);
230 } else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) {
231 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
232 } else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
233 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
234 } else {
235 KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
236 }
237 shouldfree = 1;
238 } else {
239 /* someone else still has a reference on this request */
240 shouldfree = 0;
241 }
242 /* collect request result */
243 if (error == 0)
244 error = workp->kr_result;
245 KAUTH_RESOLVER_UNLOCK();
246 /*
247 * If we dropped the last reference, free the request.
248 */
249 if (shouldfree)
250 FREE(workp, M_KAUTH);
251
252 KAUTH_DEBUG("RESOLVER - returning %d", error);
253 return(error);
254 }
255
256 /*
257 * System call interface for the external identity resolver.
258 */
259 int
260 identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused register_t *retval)
261 {
262 int opcode = uap->opcode;
263 user_addr_t message = uap->message;
264 struct kauth_resolver_work *workp;
265 int error;
266 pid_t new_id;
267
268 /*
269 * New server registering itself.
270 */
271 if (opcode == KAUTH_EXTLOOKUP_REGISTER) {
272 new_id = current_proc()->p_pid;
273 if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) {
274 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id);
275 return(error);
276 }
277 KAUTH_RESOLVER_LOCK();
278 if (kauth_resolver_identity != new_id) {
279 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity);
280 /*
281 * We have a new server, so assume that all the old requests have been lost.
282 */
283 while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) {
284 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
285 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
286 workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED;
287 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link);
288 }
289 kauth_resolver_identity = new_id;
290 kauth_resolver_registered = 1;
291 wakeup(&kauth_resolver_unsubmitted);
292 }
293 KAUTH_RESOLVER_UNLOCK();
294 return(0);
295 }
296
297 /*
298 * Beyond this point, we must be the resolver process.
299 */
300 if (current_proc()->p_pid != kauth_resolver_identity) {
301 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
302 return(EPERM);
303 }
304
305 /*
306 * Got a result returning?
307 */
308 if (opcode & KAUTH_EXTLOOKUP_RESULT) {
309 if ((error = kauth_resolver_complete(message)) != 0)
310 return(error);
311 }
312
313 /*
314 * Caller wants to take more work?
315 */
316 if (opcode & KAUTH_EXTLOOKUP_WORKER) {
317 if ((error = kauth_resolver_getwork(message)) != 0)
318 return(error);
319 }
320
321 return(0);
322 }
323
324 /*
325 * Get work for a caller.
326 */
327 static int
328 kauth_resolver_getwork(user_addr_t message)
329 {
330 struct kauth_resolver_work *workp;
331 int error;
332
333 KAUTH_RESOLVER_LOCK();
334 error = 0;
335 while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) {
336 error = msleep(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0);
337 if (error != 0)
338 break;
339 }
340 if (workp != NULL) {
341 if ((error = copyout(&workp->kr_work, message, sizeof(workp->kr_work))) != 0) {
342 KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
343 goto out;
344 }
345 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
346 workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
347 workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
348 TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
349 }
350
351 out:
352 KAUTH_RESOLVER_UNLOCK();
353 return(error);
354 }
355
356 /*
357 * Return a result from userspace.
358 */
359 static int
360 kauth_resolver_complete(user_addr_t message)
361 {
362 struct kauth_identity_extlookup extl;
363 struct kauth_resolver_work *workp;
364 int error, result;
365
366 if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
367 KAUTH_DEBUG("RESOLVER - error getting completed work\n");
368 return(error);
369 }
370
371 KAUTH_RESOLVER_LOCK();
372
373 error = 0;
374 result = 0;
375 switch (extl.el_result) {
376 case KAUTH_EXTLOOKUP_INPROG:
377 {
378 static int once = 0;
379
380 /* XXX this should go away once memberd is updated */
381 if (!once) {
382 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
383 once = 1;
384 }
385 }
386 /* FALLTHROUGH */
387 case KAUTH_EXTLOOKUP_SUCCESS:
388 break;
389
390 case KAUTH_EXTLOOKUP_FATAL:
391 /* fatal error means the resolver is dead */
392 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity);
393 kauth_resolver_identity = 0;
394 /* XXX should we terminate all outstanding requests? */
395 error = EIO;
396 break;
397 case KAUTH_EXTLOOKUP_BADRQ:
398 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno);
399 result = EINVAL;
400 break;
401 case KAUTH_EXTLOOKUP_FAILURE:
402 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno);
403 result = EIO;
404 break;
405 default:
406 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result);
407 result = EIO;
408 break;
409 }
410
411 /*
412 * In the case of a fatal error, we assume that the resolver will restart
413 * quickly and re-collect all of the outstanding requests. Thus, we don't
414 * complete the request which returned the fatal error status.
415 */
416 if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
417 /* scan our list for this request */
418 TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
419 /* found it? */
420 if (workp->kr_seqno == extl.el_seqno) {
421 /* copy result */
422 workp->kr_work = extl;
423 /* move onto completed list and wake up requester(s) */
424 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
425 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
426 workp->kr_flags |= KAUTH_REQUEST_DONE;
427 workp->kr_result = result;
428 TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
429 wakeup(workp);
430 break;
431 }
432 }
433 }
434 /*
435 * Note that it's OK for us not to find anything; if the request has
436 * timed out the work record will be gone.
437 */
438 KAUTH_RESOLVER_UNLOCK();
439
440 return(error);
441 }
442
443
444 /*
445 * Identity cache.
446 */
447
448 struct kauth_identity {
449 TAILQ_ENTRY(kauth_identity) ki_link;
450 int ki_valid;
451 #define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */
452 #define KI_VALID_GID (1<<1)
453 #define KI_VALID_GUID (1<<2)
454 #define KI_VALID_NTSID (1<<3)
455 uid_t ki_uid;
456 gid_t ki_gid;
457 guid_t ki_guid;
458 ntsid_t ki_ntsid;
459 /*
460 * Expiry times are the earliest time at which we will disregard the cached state and go to
461 * userland. Before then if the valid bit is set, we will return the cached value. If it's
462 * not set, we will not go to userland to resolve, just assume that there is no answer
463 * available.
464 */
465 time_t ki_guid_expiry;
466 time_t ki_ntsid_expiry;
467 };
468
469 static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
470 #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
471 static int kauth_identity_count;
472
473 static lck_mtx_t *kauth_identity_mtx;
474 #define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx);
475 #define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
476
477
478 static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
479 ntsid_t *ntsidp, time_t ntsid_expiry);
480 static void kauth_identity_register(struct kauth_identity *kip);
481 static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
482 static void kauth_identity_lru(struct kauth_identity *kip);
483 static int kauth_identity_guid_expired(struct kauth_identity *kip);
484 static int kauth_identity_ntsid_expired(struct kauth_identity *kip);
485 static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
486 static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
487 static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
488 static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
489
490 void
491 kauth_identity_init(void)
492 {
493 TAILQ_INIT(&kauth_identities);
494 kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
495 }
496
497 static int
498 kauth_identity_resolve(__unused struct kauth_identity_extlookup *el)
499 {
500 return(kauth_resolver_submit(el));
501 }
502
503 static struct kauth_identity *
504 kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, ntsid_t *ntsidp, time_t ntsid_expiry)
505 {
506 struct kauth_identity *kip;
507
508 /* get and fill in a new identity */
509 MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO);
510 if (kip != NULL) {
511 if (gid != KAUTH_GID_NONE) {
512 kip->ki_gid = gid;
513 kip->ki_valid = KI_VALID_GID;
514 }
515 if (uid != KAUTH_UID_NONE) {
516 if (kip->ki_valid & KI_VALID_GID)
517 panic("can't allocate kauth identity with both uid and gid");
518 kip->ki_uid = uid;
519 kip->ki_valid = KI_VALID_UID;
520 }
521 if (guidp != NULL) {
522 kip->ki_guid = *guidp;
523 kip->ki_valid |= KI_VALID_GUID;
524 }
525 kip->ki_guid_expiry = guid_expiry;
526 if (ntsidp != NULL) {
527 kip->ki_ntsid = *ntsidp;
528 kip->ki_valid |= KI_VALID_NTSID;
529 }
530 kip->ki_ntsid_expiry = ntsid_expiry;
531 }
532 return(kip);
533 }
534
535 /*
536 * Register an association between identity tokens.
537 */
538 static void
539 kauth_identity_register(struct kauth_identity *kip)
540 {
541 struct kauth_identity *ip;
542
543 /*
544 * We search the cache for the UID listed in the incoming association. If we
545 * already have an entry, the new information is merged.
546 */
547 ip = NULL;
548 KAUTH_IDENTITY_LOCK();
549 if (kip->ki_valid & KI_VALID_UID) {
550 if (kip->ki_valid & KI_VALID_GID)
551 panic("kauth_identity: can't insert record with both UID and GID as key");
552 TAILQ_FOREACH(ip, &kauth_identities, ki_link)
553 if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid))
554 break;
555 } else if (kip->ki_valid & KI_VALID_GID) {
556 TAILQ_FOREACH(ip, &kauth_identities, ki_link)
557 if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid))
558 break;
559 } else {
560 panic("kauth_identity: can't insert record without UID or GID as key");
561 }
562
563 if (ip != NULL) {
564 /* we already have an entry, merge/overwrite */
565 if (kip->ki_valid & KI_VALID_GUID) {
566 ip->ki_guid = kip->ki_guid;
567 ip->ki_valid |= KI_VALID_GUID;
568 }
569 ip->ki_guid_expiry = kip->ki_guid_expiry;
570 if (kip->ki_valid & KI_VALID_NTSID) {
571 ip->ki_ntsid = kip->ki_ntsid;
572 ip->ki_valid |= KI_VALID_NTSID;
573 }
574 ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
575 /* and discard the incoming identity */
576 FREE(kip, M_KAUTH);
577 ip = NULL;
578 } else {
579 /* don't have any information on this identity, so just add it */
580 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
581 if (++kauth_identity_count > KAUTH_IDENTITY_CACHEMAX) {
582 ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
583 TAILQ_REMOVE(&kauth_identities, ip, ki_link);
584 kauth_identity_count--;
585 }
586 }
587 KAUTH_IDENTITY_UNLOCK();
588 /* have to drop lock before freeing expired entry */
589 if (ip != NULL)
590 FREE(ip, M_KAUTH);
591 }
592
593 /*
594 * Given a lookup result, add any associations that we don't
595 * currently have.
596 */
597 static void
598 kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip)
599 {
600 struct timeval tv;
601 struct kauth_identity *kip;
602
603 microuptime(&tv);
604
605 /* user identity? */
606 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
607 KAUTH_IDENTITY_LOCK();
608 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
609 /* matching record */
610 if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
611 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
612 kip->ki_guid = elp->el_uguid;
613 kip->ki_valid |= KI_VALID_GUID;
614 }
615 kip->ki_guid_expiry = tv.tv_sec + elp->el_uguid_valid;
616 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
617 kip->ki_ntsid = elp->el_usid;
618 kip->ki_valid |= KI_VALID_NTSID;
619 }
620 kip->ki_ntsid_expiry = tv.tv_sec + elp->el_usid_valid;
621 kauth_identity_lru(kip);
622 if (rkip != NULL)
623 *rkip = *kip;
624 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
625 break;
626 }
627 }
628 KAUTH_IDENTITY_UNLOCK();
629 /* not found in cache, add new record */
630 if (kip == NULL) {
631 kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
632 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
633 tv.tv_sec + elp->el_uguid_valid,
634 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
635 tv.tv_sec + elp->el_usid_valid);
636 if (kip != NULL) {
637 if (rkip != NULL)
638 *rkip = *kip;
639 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
640 kauth_identity_register(kip);
641 }
642 }
643 }
644
645 /* group identity? */
646 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID) {
647 KAUTH_IDENTITY_LOCK();
648 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
649 /* matching record */
650 if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) {
651 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) {
652 kip->ki_guid = elp->el_gguid;
653 kip->ki_valid |= KI_VALID_GUID;
654 }
655 kip->ki_guid_expiry = tv.tv_sec + elp->el_gguid_valid;
656 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
657 kip->ki_ntsid = elp->el_gsid;
658 kip->ki_valid |= KI_VALID_NTSID;
659 }
660 kip->ki_ntsid_expiry = tv.tv_sec + elp->el_gsid_valid;
661 kauth_identity_lru(kip);
662 if (rkip != NULL)
663 *rkip = *kip;
664 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
665 break;
666 }
667 }
668 KAUTH_IDENTITY_UNLOCK();
669 /* not found in cache, add new record */
670 if (kip == NULL) {
671 kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
672 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
673 tv.tv_sec + elp->el_gguid_valid,
674 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
675 tv.tv_sec + elp->el_gsid_valid);
676 if (kip != NULL) {
677 if (rkip != NULL)
678 *rkip = *kip;
679 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
680 kauth_identity_register(kip);
681 }
682 }
683 }
684
685 }
686
687 /*
688 * Promote the entry to the head of the LRU, assumes the cache is locked.
689 *
690 * This is called even if the entry has expired; typically an expired entry
691 * that's been looked up is about to be revalidated, and having it closer to
692 * the head of the LRU means finding it quickly again when the revalidation
693 * comes through.
694 */
695 static void
696 kauth_identity_lru(struct kauth_identity *kip)
697 {
698 if (kip != TAILQ_FIRST(&kauth_identities)) {
699 TAILQ_REMOVE(&kauth_identities, kip, ki_link);
700 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
701 }
702 }
703
704 /*
705 * Handly lazy expiration of translations.
706 */
707 static int
708 kauth_identity_guid_expired(struct kauth_identity *kip)
709 {
710 struct timeval tv;
711
712 microuptime(&tv);
713 KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip->ki_guid_expiry, tv.tv_sec);
714 return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
715 }
716
717 static int
718 kauth_identity_ntsid_expired(struct kauth_identity *kip)
719 {
720 struct timeval tv;
721
722 microuptime(&tv);
723 KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip->ki_ntsid_expiry, tv.tv_sec);
724 return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
725 }
726
727 /*
728 * Search for an entry by UID. Returns a copy of the entry, ENOENT if no valid
729 * association exists for the UID.
730 */
731 static int
732 kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
733 {
734 struct kauth_identity *kip;
735
736 KAUTH_IDENTITY_LOCK();
737 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
738 if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) {
739 kauth_identity_lru(kip);
740 *kir = *kip;
741 break;
742 }
743 }
744 KAUTH_IDENTITY_UNLOCK();
745 return((kip == NULL) ? ENOENT : 0);
746 }
747
748
749 /*
750 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
751 * association exists for the GID.
752 */
753 static int
754 kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
755 {
756 struct kauth_identity *kip;
757
758 KAUTH_IDENTITY_LOCK();
759 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
760 if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) {
761 kauth_identity_lru(kip);
762 *kir = *kip;
763 break;
764 }
765 }
766 KAUTH_IDENTITY_UNLOCK();
767 return((kip == NULL) ? ENOENT : 0);
768 }
769
770
771 /*
772 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
773 * association exists for the GUID. Note that the association may be expired,
774 * in which case the caller may elect to call out to userland to revalidate.
775 */
776 static int
777 kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
778 {
779 struct kauth_identity *kip;
780
781 KAUTH_IDENTITY_LOCK();
782 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
783 if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
784 kauth_identity_lru(kip);
785 *kir = *kip;
786 break;
787 }
788 }
789 KAUTH_IDENTITY_UNLOCK();
790 return((kip == NULL) ? ENOENT : 0);
791 }
792
793 /*
794 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
795 * association exists for the SID. Note that the association may be expired,
796 * in which case the caller may elect to call out to userland to revalidate.
797 */
798 static int
799 kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
800 {
801 struct kauth_identity *kip;
802
803 KAUTH_IDENTITY_LOCK();
804 TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
805 if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) {
806 kauth_identity_lru(kip);
807 *kir = *kip;
808 break;
809 }
810 }
811 KAUTH_IDENTITY_UNLOCK();
812 return((kip == NULL) ? ENOENT : 0);
813 }
814
815 /*
816 * GUID handling.
817 */
818 guid_t kauth_null_guid;
819
820 int
821 kauth_guid_equal(guid_t *guid1, guid_t *guid2)
822 {
823 return(!bcmp(guid1, guid2, sizeof(*guid1)));
824 }
825
826 /*
827 * Look for well-known GUIDs.
828 */
829 int
830 kauth_wellknown_guid(guid_t *guid)
831 {
832 static char fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
833 int code;
834 /*
835 * All WKGs begin with the same 12 bytes.
836 */
837 if (!bcmp((void *)guid, fingerprint, 12)) {
838 /*
839 * The final 4 bytes are our code.
840 */
841 code = *(u_int32_t *)&guid->g_guid[12];
842 switch(code) {
843 case 0x0000000c:
844 return(KAUTH_WKG_EVERYBODY);
845 case 0xfffffffe:
846 return(KAUTH_WKG_NOBODY);
847 case 0x0000000a:
848 return(KAUTH_WKG_OWNER);
849 case 0x00000010:
850 return(KAUTH_WKG_GROUP);
851 }
852 }
853 return(KAUTH_WKG_NOT);
854 }
855
856
857 /*
858 * NT Security Identifier handling.
859 */
860 int
861 kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
862 {
863 /* check sizes for equality, also sanity-check size while we're at it */
864 if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) &&
865 (KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) &&
866 !bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)))
867 return(1);
868 return(0);
869 }
870
871 /*
872 * Identity KPI
873 *
874 * We support four tokens representing identity:
875 * - Credential reference
876 * - UID
877 * - GUID
878 * - NT security identifier
879 *
880 * Of these, the UID is the ubiquitous identifier; cross-referencing should
881 * be done using it.
882 */
883
884 static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
885
886 /*
887 * Fetch UID from credential.
888 */
889 uid_t
890 kauth_cred_getuid(kauth_cred_t cred)
891 {
892 NULLCRED_CHECK(cred);
893 return(cred->cr_uid);
894 }
895
896 /*
897 * Fetch GID from credential.
898 */
899 uid_t
900 kauth_cred_getgid(kauth_cred_t cred)
901 {
902 NULLCRED_CHECK(cred);
903 return(cred->cr_gid);
904 }
905
906 /*
907 * Fetch UID from GUID.
908 */
909 int
910 kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
911 {
912 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
913 }
914
915 /*
916 * Fetch GID from GUID.
917 */
918 int
919 kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
920 {
921 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
922 }
923
924 /*
925 * Fetch UID from NT SID.
926 */
927 int
928 kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
929 {
930 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
931 }
932
933 /*
934 * Fetch GID from NT SID.
935 */
936 int
937 kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
938 {
939 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
940 }
941
942 /*
943 * Fetch GUID from NT SID.
944 */
945 int
946 kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
947 {
948 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
949 }
950
951 /*
952 * Fetch GUID from UID.
953 */
954 int
955 kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
956 {
957 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
958 }
959
960 /*
961 * Fetch user GUID from credential.
962 */
963 int
964 kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
965 {
966 NULLCRED_CHECK(cred);
967 return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
968 }
969
970 /*
971 * Fetch GUID from GID.
972 */
973 int
974 kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
975 {
976 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
977 }
978
979 /*
980 * Fetch NT SID from UID.
981 */
982 int
983 kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
984 {
985 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
986 }
987
988 /*
989 * Fetch NT SID from credential.
990 */
991 int
992 kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
993 {
994 NULLCRED_CHECK(cred);
995 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
996 }
997
998 /*
999 * Fetch NT SID from GID.
1000 */
1001 int
1002 kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
1003 {
1004 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
1005 }
1006
1007 /*
1008 * Fetch NT SID from GUID.
1009 */
1010 int
1011 kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
1012 {
1013 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
1014 }
1015
1016
1017
1018 /*
1019 * Lookup a translation in the cache.
1020 */
1021 static int
1022 kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
1023 {
1024 struct kauth_identity ki;
1025 struct kauth_identity_extlookup el;
1026 int error;
1027 int (* expired)(struct kauth_identity *kip);
1028
1029 KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
1030
1031 /*
1032 * Look for an existing cache entry for this association.
1033 * If the entry has not expired, return the cached information.
1034 */
1035 ki.ki_valid = 0;
1036 switch(from) {
1037 case KI_VALID_UID:
1038 error = kauth_identity_find_uid(*(uid_t *)src, &ki);
1039 break;
1040 case KI_VALID_GID:
1041 error = kauth_identity_find_gid(*(gid_t *)src, &ki);
1042 break;
1043 case KI_VALID_GUID:
1044 error = kauth_identity_find_guid((guid_t *)src, &ki);
1045 break;
1046 case KI_VALID_NTSID:
1047 error = kauth_identity_find_ntsid((ntsid_t *)src, &ki);
1048 break;
1049 default:
1050 return(EINVAL);
1051 }
1052 /* lookup failure or error */
1053 if (error != 0) {
1054 /* any other error is fatal */
1055 if (error != ENOENT) {
1056 KAUTH_DEBUG("CACHE - cache search error %d", error);
1057 return(error);
1058 }
1059 } else {
1060 /* do we have a translation? */
1061 if (ki.ki_valid & to) {
1062 /* found a valid cached entry, check expiry */
1063 switch(to) {
1064 case KI_VALID_GUID:
1065 expired = kauth_identity_guid_expired;
1066 break;
1067 case KI_VALID_NTSID:
1068 expired = kauth_identity_ntsid_expired;
1069 break;
1070 default:
1071 switch(from) {
1072 case KI_VALID_GUID:
1073 expired = kauth_identity_guid_expired;
1074 break;
1075 case KI_VALID_NTSID:
1076 expired = kauth_identity_ntsid_expired;
1077 break;
1078 default:
1079 expired = NULL;
1080 }
1081 }
1082 KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki.ki_valid);
1083 /*
1084 * If no expiry function, or not expired, we have found
1085 * a hit.
1086 */
1087 if (!expired) {
1088 KAUTH_DEBUG("CACHE - no expiry function");
1089 goto found;
1090 }
1091 if (!expired(&ki)) {
1092 KAUTH_DEBUG("CACHE - entry valid, unexpired");
1093 goto found;
1094 }
1095 /*
1096 * We leave ki_valid set here; it contains a translation but the TTL has
1097 * expired. If we can't get a result from the resolver, we will
1098 * use it as a better-than nothing alternative.
1099 */
1100 KAUTH_DEBUG("CACHE - expired entry found");
1101 }
1102 }
1103
1104 /*
1105 * Call the resolver. We ask for as much data as we can get.
1106 */
1107 switch(from) {
1108 case KI_VALID_UID:
1109 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
1110 el.el_uid = *(uid_t *)src;
1111 break;
1112 case KI_VALID_GID:
1113 el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
1114 el.el_gid = *(gid_t *)src;
1115 break;
1116 case KI_VALID_GUID:
1117 el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
1118 el.el_uguid = *(guid_t *)src;
1119 el.el_gguid = *(guid_t *)src;
1120 break;
1121 case KI_VALID_NTSID:
1122 el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
1123 el.el_usid = *(ntsid_t *)src;
1124 el.el_gsid = *(ntsid_t *)src;
1125 break;
1126 default:
1127 return(EINVAL);
1128 }
1129 /*
1130 * Here we ask for everything all at once, to avoid having to work
1131 * out what we really want now, or might want soon.
1132 *
1133 * Asking for SID translations when we don't know we need them right
1134 * now is going to cause excess work to be done if we're connected
1135 * to a network that thinks it can translate them. This list needs
1136 * to get smaller/smarter.
1137 */
1138 el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
1139 KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
1140 KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
1141 KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
1142 error = kauth_identity_resolve(&el);
1143 KAUTH_DEBUG("CACHE - resolver returned %d", error);
1144 /* was the lookup successful? */
1145 if (error == 0) {
1146 /*
1147 * Save the results from the lookup - may have other information even if we didn't
1148 * get a guid.
1149 */
1150 kauth_identity_updatecache(&el, &ki);
1151 }
1152 /*
1153 * Check to see if we have a valid result.
1154 */
1155 if (!error && !(ki.ki_valid & to))
1156 error = ENOENT;
1157 if (error)
1158 return(error);
1159 found:
1160 switch(to) {
1161 case KI_VALID_UID:
1162 *(uid_t *)dst = ki.ki_uid;
1163 break;
1164 case KI_VALID_GID:
1165 *(gid_t *)dst = ki.ki_gid;
1166 break;
1167 case KI_VALID_GUID:
1168 *(guid_t *)dst = ki.ki_guid;
1169 break;
1170 case KI_VALID_NTSID:
1171 *(ntsid_t *)dst = ki.ki_ntsid;
1172 break;
1173 default:
1174 return(EINVAL);
1175 }
1176 KAUTH_DEBUG("CACHE - returned successfully");
1177 return(0);
1178 }
1179
1180
1181 /*
1182 * Group membership cache.
1183 *
1184 * XXX the linked-list implementation here needs to be optimized.
1185 */
1186
1187 struct kauth_group_membership {
1188 TAILQ_ENTRY(kauth_group_membership) gm_link;
1189 uid_t gm_uid; /* the identity whose membership we're recording */
1190 gid_t gm_gid; /* group of which they are a member */
1191 time_t gm_expiry; /* TTL for the membership */
1192 int gm_flags;
1193 #define KAUTH_GROUP_ISMEMBER (1<<0)
1194 };
1195
1196 TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
1197 #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1198 static int kauth_groups_count;
1199
1200 static lck_mtx_t *kauth_groups_mtx;
1201 #define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx);
1202 #define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx);
1203
1204 static int kauth_groups_expired(struct kauth_group_membership *gm);
1205 static void kauth_groups_lru(struct kauth_group_membership *gm);
1206 static void kauth_groups_updatecache(struct kauth_identity_extlookup *el);
1207
1208 void
1209 kauth_groups_init(void)
1210 {
1211 TAILQ_INIT(&kauth_groups);
1212 kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
1213 }
1214
1215 static int
1216 kauth_groups_expired(struct kauth_group_membership *gm)
1217 {
1218 struct timeval tv;
1219
1220 microuptime(&tv);
1221 return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
1222 }
1223
1224 static void
1225 kauth_groups_lru(struct kauth_group_membership *gm)
1226 {
1227 if (gm != TAILQ_FIRST(&kauth_groups)) {
1228 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
1229 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
1230 }
1231 }
1232
1233 static void
1234 kauth_groups_updatecache(struct kauth_identity_extlookup *el)
1235 {
1236 struct kauth_group_membership *gm;
1237 struct timeval tv;
1238
1239 /* need a valid response if we are to cache anything */
1240 if ((el->el_flags &
1241 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
1242 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
1243 return;
1244
1245 microuptime(&tv);
1246
1247 /* search for an existing record for this association before inserting */
1248 KAUTH_GROUPS_LOCK();
1249 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
1250 if ((el->el_uid == gm->gm_uid) &&
1251 (el->el_gid == gm->gm_gid)) {
1252 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
1253 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
1254 } else {
1255 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
1256 }
1257 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
1258 kauth_groups_lru(gm);
1259 break;
1260 }
1261 }
1262 KAUTH_GROUPS_UNLOCK();
1263
1264 /* if we found an entry to update, stop here */
1265 if (gm != NULL)
1266 return;
1267
1268 /* allocate a new record */
1269 MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
1270 if (gm != NULL) {
1271 gm->gm_uid = el->el_uid;
1272 gm->gm_gid = el->el_gid;
1273 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
1274 gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
1275 } else {
1276 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
1277 }
1278 gm->gm_expiry = el->el_member_valid + tv.tv_sec;
1279 }
1280
1281 /*
1282 * Insert the new entry. Note that it's possible to race ourselves here
1283 * and end up with duplicate entries in the list. Wasteful, but harmless
1284 * since the first into the list will never be looked up, and thus will
1285 * eventually just fall off the end.
1286 */
1287 KAUTH_GROUPS_LOCK();
1288 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
1289 if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
1290 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
1291 TAILQ_REMOVE(&kauth_groups, gm, gm_link);
1292 kauth_groups_count--;
1293 } else {
1294 gm = NULL;
1295 }
1296 KAUTH_GROUPS_UNLOCK();
1297
1298 /* free expired cache entry */
1299 if (gm != NULL)
1300 FREE(gm, M_KAUTH);
1301 }
1302
1303 /*
1304 * Group membership KPI
1305 */
1306 /*
1307 * This function guarantees not to modify resultp when returning an error.
1308 */
1309 int
1310 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
1311 {
1312 struct kauth_group_membership *gm;
1313 struct kauth_identity_extlookup el;
1314 int i, error;
1315
1316 /*
1317 * Check the per-credential list of override groups.
1318 *
1319 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1320 * the cache should be used for that case.
1321 */
1322 for (i = 0; i < cred->cr_ngroups; i++) {
1323 if (gid == cred->cr_groups[i]) {
1324 *resultp = 1;
1325 return(0);
1326 }
1327 }
1328
1329 /*
1330 * If we don't have a UID for group membership checks, the in-cred list
1331 * was authoritative and we can stop here.
1332 */
1333 if (cred->cr_gmuid == KAUTH_UID_NONE) {
1334 *resultp = 0;
1335 return(0);
1336 }
1337
1338
1339 /*
1340 * If the resolver hasn't checked in yet, we are early in the boot phase and
1341 * the local group list is complete and authoritative.
1342 */
1343 if (!kauth_resolver_registered) {
1344 *resultp = 0;
1345 return(0);
1346 }
1347
1348 /* TODO: */
1349 /* XXX check supplementary groups */
1350 /* XXX check whiteout groups */
1351 /* XXX nesting of supplementary/whiteout groups? */
1352
1353 /*
1354 * Check the group cache.
1355 */
1356 KAUTH_GROUPS_LOCK();
1357 TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
1358 if ((gm->gm_uid == cred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
1359 kauth_groups_lru(gm);
1360 break;
1361 }
1362 }
1363
1364 /* did we find a membership entry? */
1365 if (gm != NULL)
1366 *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
1367 KAUTH_GROUPS_UNLOCK();
1368
1369 /* if we did, we can return now */
1370 if (gm != NULL)
1371 return(0);
1372
1373 /* nothing in the cache, need to go to userland */
1374 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
1375 el.el_uid = cred->cr_gmuid;
1376 el.el_gid = gid;
1377 error = kauth_identity_resolve(&el);
1378 if (error != 0)
1379 return(error);
1380 /* save the results from the lookup */
1381 kauth_groups_updatecache(&el);
1382
1383 /* if we successfully ascertained membership, report */
1384 if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
1385 *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
1386 return(0);
1387 }
1388
1389 return(ENOENT);
1390 }
1391
1392 /*
1393 * Determine whether the supplied credential is a member of the
1394 * group nominated by GUID.
1395 */
1396 int
1397 kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
1398 {
1399 gid_t gid;
1400 int error, wkg;
1401
1402 error = 0;
1403 wkg = kauth_wellknown_guid(guidp);
1404 switch(wkg) {
1405 case KAUTH_WKG_NOBODY:
1406 *resultp = 0;
1407 break;
1408 case KAUTH_WKG_EVERYBODY:
1409 *resultp = 1;
1410 break;
1411 default:
1412 /* translate guid to gid */
1413 if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
1414 /*
1415 * If we have no guid -> gid translation, it's not a group and
1416 * thus the cred can't be a member.
1417 */
1418 if (error == ENOENT) {
1419 *resultp = 0;
1420 error = 0;
1421 }
1422 } else {
1423 error = kauth_cred_ismember_gid(cred, gid, resultp);
1424 }
1425 }
1426 return(error);
1427 }
1428
1429 /*
1430 * Fast replacement for issuser()
1431 */
1432 int
1433 kauth_cred_issuser(kauth_cred_t cred)
1434 {
1435 return(cred->cr_uid == 0);
1436 }
1437
1438 /*
1439 * Credential KPI
1440 */
1441
1442 /* lock protecting credential hash table */
1443 static lck_mtx_t *kauth_cred_hash_mtx;
1444 #define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx);
1445 #define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx);
1446 #if KAUTH_CRED_HASH_DEBUG
1447 #define KAUTH_CRED_HASH_LOCK_ASSERT() _mutex_assert(kauth_cred_hash_mtx, MA_OWNED)
1448 #else /* !KAUTH_CRED_HASH_DEBUG */
1449 #define KAUTH_CRED_HASH_LOCK_ASSERT()
1450 #endif /* !KAUTH_CRED_HASH_DEBUG */
1451
1452 void
1453 kauth_cred_init(void)
1454 {
1455 int i;
1456
1457 kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
1458 kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
1459
1460 /*allocate credential hash table */
1461 MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *,
1462 (sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size),
1463 M_KAUTH, M_WAITOK | M_ZERO);
1464 for (i = 0; i < kauth_cred_table_size; i++) {
1465 TAILQ_INIT(&kauth_cred_table_anchor[i]);
1466 }
1467 }
1468
1469 /*
1470 * Return the current thread's effective UID.
1471 */
1472 uid_t
1473 kauth_getuid(void)
1474 {
1475 return(kauth_cred_get()->cr_uid);
1476 }
1477
1478 /*
1479 * Return the current thread's real UID.
1480 */
1481 uid_t
1482 kauth_getruid(void)
1483 {
1484 return(kauth_cred_get()->cr_ruid);
1485 }
1486
1487 /*
1488 * Return the current thread's effective GID.
1489 */
1490 gid_t
1491 kauth_getgid(void)
1492 {
1493 return(kauth_cred_get()->cr_groups[0]);
1494 }
1495
1496 /*
1497 * Return the current thread's real GID.
1498 */
1499 gid_t
1500 kauth_getrgid(void)
1501 {
1502 return(kauth_cred_get()->cr_rgid);
1503 }
1504
1505 /*
1506 * Returns a pointer to the current thread's credential, does not take a
1507 * reference (so the caller must not do anything that would let the thread's
1508 * credential change while using the returned value).
1509 */
1510 kauth_cred_t
1511 kauth_cred_get(void)
1512 {
1513 struct proc *p;
1514 struct uthread *uthread;
1515
1516 uthread = get_bsdthread_info(current_thread());
1517 /* sanity */
1518 if (uthread == NULL)
1519 panic("thread wants credential but has no BSD thread info");
1520 /*
1521 * We can lazy-bind credentials to threads, as long as their processes have them.
1522 * If we later inline this function, the code in this block should probably be
1523 * called out in a function.
1524 */
1525 if (uthread->uu_ucred == NOCRED) {
1526 if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
1527 panic("thread wants credential but has no BSD process");
1528 uthread->uu_ucred = kauth_cred_proc_ref(p);
1529 }
1530 return(uthread->uu_ucred);
1531 }
1532
1533 /*
1534 * Returns a pointer to the current thread's credential, takes a reference.
1535 */
1536 kauth_cred_t
1537 kauth_cred_get_with_ref(void)
1538 {
1539 struct proc *procp;
1540 struct uthread *uthread;
1541
1542 uthread = get_bsdthread_info(current_thread());
1543 /* sanity checks */
1544 if (uthread == NULL)
1545 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
1546 if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
1547 panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
1548
1549 /*
1550 * We can lazy-bind credentials to threads, as long as their processes have them.
1551 * If we later inline this function, the code in this block should probably be
1552 * called out in a function.
1553 */
1554 if (uthread->uu_ucred == NOCRED) {
1555 /* take reference for new cred in thread */
1556 uthread->uu_ucred = kauth_cred_proc_ref(procp);
1557 }
1558 /* take a reference for our caller */
1559 kauth_cred_ref(uthread->uu_ucred);
1560 return(uthread->uu_ucred);
1561 }
1562
1563 /*
1564 * Returns a pointer to the given process's credential, takes a reference.
1565 */
1566 kauth_cred_t
1567 kauth_cred_proc_ref(proc_t procp)
1568 {
1569 kauth_cred_t cred;
1570
1571 proc_lock(procp);
1572 cred = proc_ucred(procp);
1573 kauth_cred_ref(cred);
1574 proc_unlock(procp);
1575 return(cred);
1576 }
1577
1578 /*
1579 * Allocates a new credential.
1580 */
1581 kauth_cred_t
1582 kauth_cred_alloc(void)
1583 {
1584 kauth_cred_t newcred;
1585
1586 MALLOC(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK | M_ZERO);
1587 if (newcred != 0) {
1588 newcred->cr_ref = 1;
1589 /* must do this, or cred has same group membership as uid 0 */
1590 newcred->cr_gmuid = KAUTH_UID_NONE;
1591 #if CRED_DIAGNOSTIC
1592 } else {
1593 panic("kauth_cred_alloc: couldn't allocate credential");
1594 #endif
1595 }
1596
1597 #if KAUTH_CRED_HASH_DEBUG
1598 kauth_cred_count++;
1599 #endif
1600
1601 return(newcred);
1602 }
1603
1604 /*
1605 * Looks to see if we already have a known credential and if found bumps the
1606 * reference count and returns it. If there are no credentials that match
1607 * the given credential then we allocate a new credential.
1608 *
1609 * Note that the gmuid is hard-defaulted to the UID specified. Since we maintain
1610 * this field, we can't expect callers to know how it needs to be set. Callers
1611 * should be prepared for this field to be overwritten.
1612 */
1613 kauth_cred_t
1614 kauth_cred_create(kauth_cred_t cred)
1615 {
1616 kauth_cred_t found_cred, new_cred = NULL;
1617
1618 cred->cr_gmuid = cred->cr_uid;
1619
1620 for (;;) {
1621 KAUTH_CRED_HASH_LOCK();
1622 found_cred = kauth_cred_find(cred);
1623 if (found_cred != NULL) {
1624 /* found an existing credential so we'll bump reference count and return */
1625 kauth_cred_ref(found_cred);
1626 KAUTH_CRED_HASH_UNLOCK();
1627 return(found_cred);
1628 }
1629 KAUTH_CRED_HASH_UNLOCK();
1630
1631 /* no existing credential found. create one and add it to our hash table */
1632 new_cred = kauth_cred_alloc();
1633 if (new_cred != NULL) {
1634 int err;
1635 new_cred->cr_uid = cred->cr_uid;
1636 new_cred->cr_ruid = cred->cr_ruid;
1637 new_cred->cr_svuid = cred->cr_svuid;
1638 new_cred->cr_rgid = cred->cr_rgid;
1639 new_cred->cr_svgid = cred->cr_svgid;
1640 new_cred->cr_gmuid = cred->cr_gmuid;
1641 new_cred->cr_ngroups = cred->cr_ngroups;
1642 bcopy(&cred->cr_groups[0], &new_cred->cr_groups[0], sizeof(new_cred->cr_groups));
1643 KAUTH_CRED_HASH_LOCK();
1644 err = kauth_cred_add(new_cred);
1645 KAUTH_CRED_HASH_UNLOCK();
1646
1647 /* retry if kauth_cred_add returns non zero value */
1648 if (err == 0)
1649 break;
1650 FREE(new_cred, M_CRED);
1651 new_cred = NULL;
1652 }
1653 }
1654
1655 return(new_cred);
1656 }
1657
1658 /*
1659 * Update the given credential using the uid argument. The given uid is used
1660 * set the effective user ID, real user ID, and saved user ID. We only
1661 * allocate a new credential when the given uid actually results in changes to
1662 * the existing credential.
1663 */
1664 kauth_cred_t
1665 kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
1666 {
1667 struct ucred temp_cred;
1668
1669 NULLCRED_CHECK(cred);
1670
1671 /* don't need to do anything if the effective, real and saved user IDs are
1672 * already the same as the user ID passed in
1673 */
1674 if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid) {
1675 /* no change needed */
1676 return(cred);
1677 }
1678
1679 /* look up in cred hash table to see if we have a matching credential
1680 * with new values.
1681 */
1682 bcopy(cred, &temp_cred, sizeof(temp_cred));
1683 temp_cred.cr_uid = uid;
1684 temp_cred.cr_ruid = uid;
1685 temp_cred.cr_svuid = uid;
1686 temp_cred.cr_gmuid = uid;
1687
1688 return(kauth_cred_update(cred, &temp_cred, TRUE));
1689 }
1690
1691 /*
1692 * Update the given credential using the euid argument. The given uid is used
1693 * set the effective user ID. We only allocate a new credential when the given
1694 * uid actually results in changes to the existing credential.
1695 */
1696 kauth_cred_t
1697 kauth_cred_seteuid(kauth_cred_t cred, uid_t euid)
1698 {
1699 struct ucred temp_cred;
1700
1701 NULLCRED_CHECK(cred);
1702
1703 /* don't need to do anything if the given effective user ID is already the
1704 * same as the effective user ID in the credential.
1705 */
1706 if (cred->cr_uid == euid) {
1707 /* no change needed */
1708 return(cred);
1709 }
1710
1711 /* look up in cred hash table to see if we have a matching credential
1712 * with new values.
1713 */
1714 bcopy(cred, &temp_cred, sizeof(temp_cred));
1715 temp_cred.cr_uid = euid;
1716
1717 return(kauth_cred_update(cred, &temp_cred, TRUE));
1718 }
1719
1720 /*
1721 * Update the given credential using the gid argument. The given gid is used
1722 * set the effective group ID, real group ID, and saved group ID. We only
1723 * allocate a new credential when the given gid actually results in changes to
1724 * the existing credential.
1725 */
1726 kauth_cred_t
1727 kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
1728 {
1729 struct ucred temp_cred;
1730
1731 NULLCRED_CHECK(cred);
1732
1733 /* don't need to do anything if the given group ID is already the
1734 * same as the group ID in the credential.
1735 */
1736 if (cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
1737 /* no change needed */
1738 return(cred);
1739 }
1740
1741 /* look up in cred hash table to see if we have a matching credential
1742 * with new values.
1743 */
1744 bcopy(cred, &temp_cred, sizeof(temp_cred));
1745 temp_cred.cr_groups[0] = gid;
1746 temp_cred.cr_rgid = gid;
1747 temp_cred.cr_svgid = gid;
1748
1749 return(kauth_cred_update(cred, &temp_cred, TRUE));
1750 }
1751
1752
1753 /*
1754 * Update the given credential using the egid argument. The given gid is used
1755 * set the effective user ID. We only allocate a new credential when the given
1756 * gid actually results in changes to the existing credential.
1757 */
1758 kauth_cred_t
1759 kauth_cred_setegid(kauth_cred_t cred, gid_t egid)
1760 {
1761 struct ucred temp_cred;
1762
1763 NULLCRED_CHECK(cred);
1764
1765 /* don't need to do anything if the given group ID is already the
1766 * same as the group Id in the credential.
1767 */
1768 if (cred->cr_groups[0] == egid) {
1769 /* no change needed */
1770 return(cred);
1771 }
1772
1773 /* look up in cred hash table to see if we have a matching credential
1774 * with new values.
1775 */
1776 bcopy(cred, &temp_cred, sizeof(temp_cred));
1777 temp_cred.cr_groups[0] = egid;
1778
1779 return(kauth_cred_update(cred, &temp_cred, TRUE));
1780 }
1781
1782 /*
1783 * Update the given credential with the given groups. We only allocate a new
1784 * credential when the given gid actually results in changes to the existing
1785 * credential.
1786 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1787 * which will be used for group membership checking.
1788 */
1789 kauth_cred_t
1790 kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
1791 {
1792 int i;
1793 struct ucred temp_cred;
1794
1795 NULLCRED_CHECK(cred);
1796
1797 /* don't need to do anything if the given list of groups does not change.
1798 */
1799 if ((cred->cr_gmuid == gmuid) && (cred->cr_ngroups == groupcount)) {
1800 for (i = 0; i < groupcount; i++) {
1801 if (cred->cr_groups[i] != groups[i])
1802 break;
1803 }
1804 if (i == groupcount) {
1805 /* no change needed */
1806 return(cred);
1807 }
1808 }
1809
1810 /* look up in cred hash table to see if we have a matching credential
1811 * with new values.
1812 */
1813 bcopy(cred, &temp_cred, sizeof(temp_cred));
1814 temp_cred.cr_ngroups = groupcount;
1815 bcopy(groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
1816 temp_cred.cr_gmuid = gmuid;
1817
1818 return(kauth_cred_update(cred, &temp_cred, TRUE));
1819 }
1820
1821 /*
1822 * Update the given credential using the uid and gid arguments. The given uid
1823 * is used set the effective user ID, real user ID, and saved user ID.
1824 * The given gid is used set the effective group ID, real group ID, and saved
1825 * group ID.
1826 * We only allocate a new credential when the given uid and gid actually results
1827 * in changes to the existing credential.
1828 */
1829 kauth_cred_t
1830 kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
1831 {
1832 struct ucred temp_cred;
1833
1834 NULLCRED_CHECK(cred);
1835
1836 /* don't need to do anything if the effective, real and saved user IDs are
1837 * already the same as the user ID passed in
1838 */
1839 if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid &&
1840 cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
1841 /* no change needed */
1842 return(cred);
1843 }
1844
1845 /* look up in cred hash table to see if we have a matching credential
1846 * with new values.
1847 */
1848 bzero(&temp_cred, sizeof(temp_cred));
1849 temp_cred.cr_uid = uid;
1850 temp_cred.cr_ruid = uid;
1851 temp_cred.cr_svuid = uid;
1852 temp_cred.cr_gmuid = uid;
1853 temp_cred.cr_ngroups = 1;
1854 temp_cred.cr_groups[0] = gid;
1855 temp_cred.cr_rgid = gid;
1856 temp_cred.cr_svgid = gid;
1857
1858 return(kauth_cred_update(cred, &temp_cred, TRUE));
1859 }
1860
1861 /*
1862 * Update the given credential using the uid and gid arguments. The given uid
1863 * is used to set the saved user ID. The given gid is used to set the
1864 * saved group ID.
1865 * We only allocate a new credential when the given uid and gid actually results
1866 * in changes to the existing credential.
1867 */
1868 kauth_cred_t
1869 kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
1870 {
1871 struct ucred temp_cred;
1872
1873 NULLCRED_CHECK(cred);
1874
1875 /* don't need to do anything if the effective, real and saved user IDs are
1876 * already the same as the user ID passed in
1877 */
1878 if (cred->cr_svuid == uid && cred->cr_svgid == gid) {
1879 /* no change needed */
1880 return(cred);
1881 }
1882
1883 /* look up in cred hash table to see if we have a matching credential
1884 * with new values.
1885 */
1886 bcopy(cred, &temp_cred, sizeof(temp_cred));
1887 temp_cred.cr_svuid = uid;
1888 temp_cred.cr_svgid = gid;
1889
1890 return(kauth_cred_update(cred, &temp_cred, TRUE));
1891 }
1892
1893 /*
1894 * Update the given credential using the given auditinfo_t.
1895 * We only allocate a new credential when the given auditinfo_t actually results
1896 * in changes to the existing credential.
1897 */
1898 kauth_cred_t
1899 kauth_cred_setauditinfo(kauth_cred_t cred, auditinfo_t *auditinfo_p)
1900 {
1901 struct ucred temp_cred;
1902
1903 NULLCRED_CHECK(cred);
1904
1905 /* don't need to do anything if the audit info is already the same as the
1906 * audit info in the credential passed in
1907 */
1908 if (bcmp(&cred->cr_au, auditinfo_p, sizeof(cred->cr_au)) == 0) {
1909 /* no change needed */
1910 return(cred);
1911 }
1912
1913 /* look up in cred hash table to see if we have a matching credential
1914 * with new values.
1915 */
1916 bcopy(cred, &temp_cred, sizeof(temp_cred));
1917 bcopy(auditinfo_p, &temp_cred.cr_au, sizeof(temp_cred.cr_au));
1918
1919 return(kauth_cred_update(cred, &temp_cred, FALSE));
1920 }
1921
1922 /*
1923 * Add a reference to the passed credential.
1924 */
1925 void
1926 kauth_cred_ref(kauth_cred_t cred)
1927 {
1928 int old_value;
1929
1930 NULLCRED_CHECK(cred);
1931
1932 old_value = OSAddAtomic(1, &cred->cr_ref);
1933
1934 if (old_value < 1)
1935 panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1936
1937 return;
1938 }
1939
1940 /*
1941 * Drop a reference from the passed credential, potentially destroying it.
1942 *
1943 * Note: Assumes credential hash is NOT locked
1944 */
1945 void
1946 kauth_cred_unref(kauth_cred_t *credp)
1947 {
1948 KAUTH_CRED_HASH_LOCK();
1949 kauth_cred_unref_hashlocked(credp);
1950 KAUTH_CRED_HASH_UNLOCK();
1951 }
1952
1953 /*
1954 * Drop a reference from the passed credential, potentially destroying it.
1955 *
1956 * Note: Assumes credential hash IS locked
1957 */
1958 static void
1959 kauth_cred_unref_hashlocked(kauth_cred_t *credp)
1960 {
1961 int old_value;
1962
1963 KAUTH_CRED_HASH_LOCK_ASSERT();
1964 NULLCRED_CHECK(*credp);
1965 old_value = OSAddAtomic(-1, &(*credp)->cr_ref);
1966
1967 #if DIAGNOSTIC
1968 if (old_value == 0)
1969 panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no references", current_proc()->p_comm, *credp);
1970 if (old_value == 1)
1971 panic("%s:0x%08x kauth_cred_rele: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp);
1972 #endif
1973
1974 if (old_value < 3) {
1975 /* the last reference is our credential hash table */
1976 kauth_cred_remove(*credp);
1977 }
1978 *credp = NOCRED;
1979 }
1980
1981 void
1982 kauth_cred_rele(kauth_cred_t cred)
1983 {
1984 kauth_cred_unref(&cred);
1985 }
1986
1987 /*
1988 * Duplicate a credential.
1989 * NOTE - caller should call kauth_cred_add after any credential changes are made.
1990 */
1991 kauth_cred_t
1992 kauth_cred_dup(kauth_cred_t cred)
1993 {
1994 kauth_cred_t newcred;
1995
1996 #if CRED_DIAGNOSTIC
1997 if (cred == NOCRED || cred == FSCRED)
1998 panic("kauth_cred_dup: bad credential");
1999 #endif
2000 newcred = kauth_cred_alloc();
2001 if (newcred != NULL) {
2002 bcopy(cred, newcred, sizeof(*newcred));
2003 newcred->cr_ref = 1;
2004 }
2005 return(newcred);
2006 }
2007
2008 /*
2009 * Returns a credential based on the passed credential but which
2010 * reflects the real rather than effective UID and GID.
2011 * NOTE - we do NOT decrement cred reference count on passed in credential
2012 */
2013 kauth_cred_t
2014 kauth_cred_copy_real(kauth_cred_t cred)
2015 {
2016 kauth_cred_t newcred = NULL, found_cred;
2017 struct ucred temp_cred;
2018
2019 /* if the credential is already 'real', just take a reference */
2020 if ((cred->cr_ruid == cred->cr_uid) &&
2021 (cred->cr_rgid == cred->cr_gid)) {
2022 kauth_cred_ref(cred);
2023 return(cred);
2024 }
2025
2026 /* look up in cred hash table to see if we have a matching credential
2027 * with new values.
2028 */
2029 bcopy(cred, &temp_cred, sizeof(temp_cred));
2030 temp_cred.cr_uid = cred->cr_ruid;
2031 temp_cred.cr_groups[0] = cred->cr_rgid;
2032 /*
2033 * if the cred is not opted out, make sure we are using the r/euid
2034 * for group checks
2035 */
2036 if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
2037 temp_cred.cr_gmuid = cred->cr_ruid;
2038
2039 for (;;) {
2040 int err;
2041
2042 KAUTH_CRED_HASH_LOCK();
2043 found_cred = kauth_cred_find(&temp_cred);
2044 if (found_cred == cred) {
2045 /* same cred so just bail */
2046 KAUTH_CRED_HASH_UNLOCK();
2047 return(cred);
2048 }
2049 if (found_cred != NULL) {
2050 /*
2051 * found a match so we bump reference count on new one.
2052 * we leave the old one alone.
2053 */
2054 kauth_cred_ref(found_cred);
2055 KAUTH_CRED_HASH_UNLOCK();
2056 return(found_cred);
2057 }
2058
2059 /* must allocate a new credential, copy in old credential data and update
2060 * with real user and group IDs.
2061 */
2062 newcred = kauth_cred_dup(&temp_cred);
2063 err = kauth_cred_add(newcred);
2064 KAUTH_CRED_HASH_UNLOCK();
2065
2066 /* retry if kauth_cred_add returns non zero value */
2067 if (err == 0)
2068 break;
2069 FREE(newcred, M_CRED);
2070 newcred = NULL;
2071 }
2072
2073 return(newcred);
2074 }
2075
2076 /*
2077 * Common code to update a credential. model_cred is a temporary, non
2078 * reference counted credential used only for comparison and modeling
2079 * purposes. old_cred is a live reference counted credential that we
2080 * intend to update using model_cred as our model.
2081 *
2082 * IMPORTANT: If the old_cred ends up updated by this process, we will, as
2083 * a side effect, drop the reference we held on it going in.
2084 */
2085 static kauth_cred_t
2086 kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred, boolean_t retain_auditinfo)
2087 {
2088 kauth_cred_t found_cred, new_cred = NULL;
2089
2090 /* make sure we carry the auditinfo forward to the new credential unless
2091 * we are actually updating the auditinfo.
2092 */
2093 if (retain_auditinfo)
2094 bcopy(&old_cred->cr_au, &model_cred->cr_au, sizeof(model_cred->cr_au));
2095
2096 for (;;) {
2097 int err;
2098
2099 KAUTH_CRED_HASH_LOCK();
2100 found_cred = kauth_cred_find(model_cred);
2101 if (found_cred == old_cred) {
2102 /* same cred so just bail */
2103 KAUTH_CRED_HASH_UNLOCK();
2104 return(old_cred);
2105 }
2106 if (found_cred != NULL) {
2107 /*
2108 * found a match so we bump reference count on new
2109 * one and decrement reference count on the old one.
2110 */
2111 kauth_cred_ref(found_cred);
2112 kauth_cred_unref_hashlocked(&old_cred);
2113 KAUTH_CRED_HASH_UNLOCK();
2114 return(found_cred);
2115 }
2116
2117 /* must allocate a new credential using the model. also
2118 * adds the new credential to the credential hash table.
2119 */
2120 new_cred = kauth_cred_dup(model_cred);
2121 err = kauth_cred_add(new_cred);
2122 KAUTH_CRED_HASH_UNLOCK();
2123
2124 /* retry if kauth_cred_add returns non zero value */
2125 if (err == 0)
2126 break;
2127 FREE(new_cred, M_CRED);
2128 new_cred = NULL;
2129 }
2130
2131 kauth_cred_unref(&old_cred);
2132 return(new_cred);
2133 }
2134
2135 /*
2136 * Add the given credential to our credential hash table and take an additional
2137 * reference to account for our use of the credential in the hash table.
2138 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2139 */
2140 static int
2141 kauth_cred_add(kauth_cred_t new_cred)
2142 {
2143 u_long hash_key;
2144
2145 KAUTH_CRED_HASH_LOCK_ASSERT();
2146
2147 hash_key = kauth_cred_get_hashkey(new_cred);
2148 hash_key %= kauth_cred_table_size;
2149
2150 /* race fix - there is a window where another matching credential
2151 * could have been inserted between the time this one was created and we
2152 * got the hash lock. If we find a match return an error and have the
2153 * the caller retry.
2154 */
2155 if (kauth_cred_find(new_cred) != NULL) {
2156 return(-1);
2157 }
2158
2159 /* take a reference for our use in credential hash table */
2160 kauth_cred_ref(new_cred);
2161
2162 /* insert the credential into the hash table */
2163 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
2164
2165 return(0);
2166 }
2167
2168
2169 /*
2170 * Remove the given credential from our credential hash table.
2171 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2172 */
2173 static void
2174 kauth_cred_remove(kauth_cred_t cred)
2175 {
2176 u_long hash_key;
2177 kauth_cred_t found_cred;
2178
2179 KAUTH_CRED_HASH_LOCK_ASSERT();
2180
2181 hash_key = kauth_cred_get_hashkey(cred);
2182 hash_key %= kauth_cred_table_size;
2183
2184 /* avoid race */
2185 if (cred->cr_ref < 1)
2186 panic("cred reference underflow");
2187 if (cred->cr_ref > 1) {
2188 return; /* someone else got a ref */
2189 }
2190
2191 /* find cred in the credential hash table */
2192 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
2193 if (found_cred == cred) {
2194 /* found a match, remove it from the hash table */
2195 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
2196 FREE(cred, M_CRED);
2197 #if KAUTH_CRED_HASH_DEBUG
2198 kauth_cred_count--;
2199 #endif
2200 return;
2201 }
2202 }
2203
2204 /* did not find a match. this should not happen! */
2205 printf("%s:%s - %d - %s - did not find a match for 0x%08x\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred);
2206 return;
2207 }
2208
2209 /*
2210 * Using the given credential data, look for a match in our credential hash
2211 * table.
2212 * NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2213 */
2214 kauth_cred_t
2215 kauth_cred_find(kauth_cred_t cred)
2216 {
2217 u_long hash_key;
2218 kauth_cred_t found_cred;
2219
2220 KAUTH_CRED_HASH_LOCK_ASSERT();
2221
2222 #if KAUTH_CRED_HASH_DEBUG
2223 static int test_count = 0;
2224
2225 test_count++;
2226 if ((test_count % 200) == 0) {
2227 kauth_cred_hash_print();
2228 }
2229 #endif
2230
2231 hash_key = kauth_cred_get_hashkey(cred);
2232 hash_key %= kauth_cred_table_size;
2233
2234 /* find cred in the credential hash table */
2235 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
2236 if (bcmp(&found_cred->cr_uid, &cred->cr_uid, (sizeof(struct ucred) - offsetof(struct ucred, cr_uid))) == 0) {
2237 /* found a match */
2238 return(found_cred);
2239 }
2240 }
2241 /* no match found */
2242 return(NULL);
2243 }
2244
2245 /*
2246 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2247 */
2248 static u_long
2249 kauth_cred_get_hashkey(kauth_cred_t cred)
2250 {
2251 u_long hash_key = 0;
2252
2253 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid,
2254 (sizeof(struct ucred) - offsetof(struct ucred, cr_uid)),
2255 hash_key);
2256 return(hash_key);
2257 }
2258
2259 /*
2260 * Generates a hash key using data that makes up a credential. Based on ElfHash.
2261 */
2262 static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
2263 {
2264 u_long hash_key = start_key;
2265 u_long temp;
2266
2267 while (data_len > 0) {
2268 hash_key = (hash_key << 4) + *datap++;
2269 temp = hash_key & 0xF0000000;
2270 if (temp) {
2271 hash_key ^= temp >> 24;
2272 }
2273 hash_key &= ~temp;
2274 data_len--;
2275 }
2276 return(hash_key);
2277 }
2278
2279 #if KAUTH_CRED_HASH_DEBUG
2280 static void kauth_cred_hash_print(void)
2281 {
2282 int i, j;
2283 kauth_cred_t found_cred;
2284
2285 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
2286 /* count slot hits, misses, collisions, and max depth */
2287 for (i = 0; i < kauth_cred_table_size; i++) {
2288 printf("[%02d] ", i);
2289 j = 0;
2290 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
2291 if (j > 0) {
2292 printf("---- ");
2293 }
2294 j++;
2295 kauth_cred_print(found_cred);
2296 printf("\n");
2297 }
2298 if (j == 0) {
2299 printf("NOCRED \n");
2300 }
2301 }
2302 }
2303
2304
2305 static void kauth_cred_print(kauth_cred_t cred)
2306 {
2307 int i;
2308
2309 printf("0x%02X - refs %d uids %d %d %d ", cred, cred->cr_ref, cred->cr_uid, cred->cr_ruid, cred->cr_svuid);
2310 printf("group count %d gids ", cred->cr_ngroups);
2311 for (i = 0; i < NGROUPS; i++) {
2312 printf("%d ", cred->cr_groups[i]);
2313 }
2314 printf("%d %d %d ", cred->cr_rgid, cred->cr_svgid, cred->cr_gmuid);
2315 printf("auditinfo %d %d %d %d %d %d ",
2316 cred->cr_au.ai_auid, cred->cr_au.ai_mask.am_success, cred->cr_au.ai_mask.am_failure,
2317 cred->cr_au.ai_termid.port, cred->cr_au.ai_termid.machine, cred->cr_au.ai_asid);
2318
2319 }
2320 #endif