]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_credential.c
xnu-792.2.4.tar.gz
[apple/xnu.git] / bsd / kern / kern_credential.c
CommitLineData
91447636
A
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
69static 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
73static volatile pid_t kauth_resolver_identity;
74static int kauth_resolver_registered;
75static uint32_t kauth_resolver_sequence;
76
77struct 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
89TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
90TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted;
91TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done;
92
93static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
94static int kauth_resolver_complete(user_addr_t message);
95static int kauth_resolver_getwork(user_addr_t message);
96
97#define KAUTH_CRED_PRIMES_COUNT 7
98static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = {97, 241, 397, 743, 1499, 3989, 7499};
99static int kauth_cred_primes_index = 0;
100static int kauth_cred_table_size = 0;
101
102TAILQ_HEAD(kauth_cred_entry_head, ucred);
103static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
104
105#define KAUTH_CRED_HASH_DEBUG 0
106
107static int kauth_cred_add(kauth_cred_t new_cred);
108static void kauth_cred_remove(kauth_cred_t cred);
109static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
110static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
111static 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
114static int kauth_cred_count = 0;
115static void kauth_cred_hash_print(void);
116static void kauth_cred_print(kauth_cred_t cred);
117#endif
118
119void
120kauth_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 */
134static int
135kauth_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 */
252int
253identitysvc(__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 */
320static int
321kauth_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
344out:
345 KAUTH_RESOLVER_UNLOCK();
346 return(error);
347}
348
349/*
350 * Return a result from userspace.
351 */
352static int
353kauth_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
441struct 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
462static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
463#define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */
464static int kauth_identity_count;
465
466static 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
471static 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);
473static void kauth_identity_register(struct kauth_identity *kip);
474static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
475static void kauth_identity_lru(struct kauth_identity *kip);
476static int kauth_identity_guid_expired(struct kauth_identity *kip);
477static int kauth_identity_ntsid_expired(struct kauth_identity *kip);
478static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
479static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
480static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
481static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
482
483void
484kauth_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
490static int
491kauth_identity_resolve(__unused struct kauth_identity_extlookup *el)
492{
493 return(kauth_resolver_submit(el));
494}
495
496static struct kauth_identity *
497kauth_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 */
531static void
532kauth_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 */
590static void
591kauth_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 */
688static void
689kauth_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 */
700static int
701kauth_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
710static int
711kauth_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 */
724static int
725kauth_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 */
746static int
747kauth_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 */
769static int
770kauth_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 */
791static int
792kauth_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 */
811guid_t kauth_null_guid;
812
813int
814kauth_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 */
822int
823kauth_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 */
853int
854kauth_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
877static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
878
879/*
880 * Fetch UID from credential.
881 */
882uid_t
883kauth_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 */
892uid_t
893kauth_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 */
902int
903kauth_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 */
911int
912kauth_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 */
920int
921kauth_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 */
929int
930kauth_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 */
938int
939kauth_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 */
947int
948kauth_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 */
956int
957kauth_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 */
966int
967kauth_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 */
975int
976kauth_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 */
984int
985kauth_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 */
994int
995kauth_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 */
1003int
1004kauth_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 */
1014static int
1015kauth_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);
1152found:
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
1180struct 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
1189TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
1190#define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */
1191static int kauth_groups_count;
1192
1193static 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
1197static int kauth_groups_expired(struct kauth_group_membership *gm);
1198static void kauth_groups_lru(struct kauth_group_membership *gm);
1199static void kauth_groups_updatecache(struct kauth_identity_extlookup *el);
1200
1201void
1202kauth_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
1208static int
1209kauth_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
1217static void
1218kauth_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
1226static void
1227kauth_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 */
1302int
1303kauth_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 */
1389int
1390kauth_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 */
1425int
1426kauth_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 */
1436static 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
1440void
1441kauth_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 */
1460uid_t
1461kauth_getuid(void)
1462{
1463 return(kauth_cred_get()->cr_uid);
1464}
1465
1466/*
1467 * Return the current thread's real UID.
1468 */
1469uid_t
1470kauth_getruid(void)
1471{
1472 return(kauth_cred_get()->cr_ruid);
1473}
1474
1475/*
1476 * Return the current thread's effective GID.
1477 */
1478gid_t
1479kauth_getgid(void)
1480{
1481 return(kauth_cred_get()->cr_groups[0]);
1482}
1483
1484/*
1485 * Return the current thread's real GID.
1486 */
1487gid_t
1488kauth_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 */
1498kauth_cred_t
1499kauth_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 */
1526kauth_cred_t
1527kauth_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 */
1558kauth_cred_t
1559kauth_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 */
1573kauth_cred_t
1574kauth_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 */
1605kauth_cred_t
1606kauth_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 */
1656kauth_cred_t
1657kauth_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 */
1688kauth_cred_t
1689kauth_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 */
1718kauth_cred_t
1719kauth_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 */
1749kauth_cred_t
1750kauth_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 */
1780kauth_cred_t
1781kauth_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 */
1820kauth_cred_t
1821kauth_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 */
1859kauth_cred_t
1860kauth_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 */
1889kauth_cred_t
1890kauth_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 */
1916void
1917kauth_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 */
1934void
1935kauth_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 */
1960kauth_cred_t
1961kauth_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 */
1982kauth_cred_t
1983kauth_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 */
2047static 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 */
2100static 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 */
2129static 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 */
2166kauth_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 */
2197static 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 */
2210static 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
2228static 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
2253static 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