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