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