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