]>
Commit | Line | Data |
---|---|---|
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 | ||
75 | static lck_mtx_t *kauth_resolver_mtx; | |
76 | #define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx); | |
77 | #define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx); | |
78 | ||
79 | static volatile pid_t kauth_resolver_identity; | |
80 | static int kauth_resolver_registered; | |
81 | static uint32_t kauth_resolver_sequence; | |
82 | ||
83 | struct kauth_resolver_work { | |
84 | TAILQ_ENTRY(kauth_resolver_work) kr_link; | |
85 | struct kauth_identity_extlookup kr_work; | |
86 | uint32_t kr_seqno; | |
87 | int kr_refs; | |
88 | int kr_flags; | |
89 | #define KAUTH_REQUEST_UNSUBMITTED (1<<0) | |
90 | #define KAUTH_REQUEST_SUBMITTED (1<<1) | |
91 | #define KAUTH_REQUEST_DONE (1<<2) | |
92 | int kr_result; | |
93 | }; | |
94 | ||
95 | TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted; | |
96 | TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted; | |
97 | TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done; | |
98 | ||
99 | static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp); | |
100 | static int kauth_resolver_complete(user_addr_t message); | |
101 | static int kauth_resolver_getwork(user_addr_t message); | |
102 | ||
103 | #define KAUTH_CRED_PRIMES_COUNT 7 | |
104 | static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = {97, 241, 397, 743, 1499, 3989, 7499}; | |
105 | static int kauth_cred_primes_index = 0; | |
106 | static int kauth_cred_table_size = 0; | |
107 | ||
108 | TAILQ_HEAD(kauth_cred_entry_head, ucred); | |
109 | static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL; | |
110 | ||
111 | #define KAUTH_CRED_HASH_DEBUG 0 | |
112 | ||
113 | static int kauth_cred_add(kauth_cred_t new_cred); | |
114 | static void kauth_cred_remove(kauth_cred_t cred); | |
115 | static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key); | |
116 | static u_long kauth_cred_get_hashkey(kauth_cred_t cred); | |
117 | static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo); | |
118 | ||
119 | #if KAUTH_CRED_HASH_DEBUG | |
120 | static int kauth_cred_count = 0; | |
121 | static void kauth_cred_hash_print(void); | |
122 | static void kauth_cred_print(kauth_cred_t cred); | |
123 | #endif | |
124 | ||
125 | void | |
126 | kauth_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 | */ | |
140 | static int | |
141 | kauth_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 | */ | |
258 | int | |
259 | identitysvc(__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 | */ | |
326 | static int | |
327 | kauth_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 | ||
350 | out: | |
351 | KAUTH_RESOLVER_UNLOCK(); | |
352 | return(error); | |
353 | } | |
354 | ||
355 | /* | |
356 | * Return a result from userspace. | |
357 | */ | |
358 | static int | |
359 | kauth_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 | ||
447 | struct 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 | ||
468 | static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities; | |
469 | #define KAUTH_IDENTITY_CACHEMAX 100 /* XXX sizing? */ | |
470 | static int kauth_identity_count; | |
471 | ||
472 | static 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 | ||
477 | static 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); | |
479 | static void kauth_identity_register(struct kauth_identity *kip); | |
480 | static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip); | |
481 | static void kauth_identity_lru(struct kauth_identity *kip); | |
482 | static int kauth_identity_guid_expired(struct kauth_identity *kip); | |
483 | static int kauth_identity_ntsid_expired(struct kauth_identity *kip); | |
484 | static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir); | |
485 | static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir); | |
486 | static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir); | |
487 | static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir); | |
488 | ||
489 | void | |
490 | kauth_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 | ||
496 | static int | |
497 | kauth_identity_resolve(__unused struct kauth_identity_extlookup *el) | |
498 | { | |
499 | return(kauth_resolver_submit(el)); | |
500 | } | |
501 | ||
502 | static struct kauth_identity * | |
503 | kauth_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 | */ | |
537 | static void | |
538 | kauth_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 | */ | |
596 | static void | |
597 | kauth_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 | */ | |
694 | static void | |
695 | kauth_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 | */ | |
706 | static int | |
707 | kauth_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 | ||
716 | static int | |
717 | kauth_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 | */ | |
730 | static int | |
731 | kauth_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 | */ | |
752 | static int | |
753 | kauth_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 | */ | |
775 | static int | |
776 | kauth_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 | */ | |
797 | static int | |
798 | kauth_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 | */ | |
817 | guid_t kauth_null_guid; | |
818 | ||
819 | int | |
820 | kauth_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 | */ | |
828 | int | |
829 | kauth_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 | */ | |
859 | int | |
860 | kauth_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 | ||
883 | static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst); | |
884 | ||
885 | /* | |
886 | * Fetch UID from credential. | |
887 | */ | |
888 | uid_t | |
889 | kauth_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 | */ | |
898 | uid_t | |
899 | kauth_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 | */ | |
908 | int | |
909 | kauth_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 | */ | |
917 | int | |
918 | kauth_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 | */ | |
926 | int | |
927 | kauth_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 | */ | |
935 | int | |
936 | kauth_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 | */ | |
944 | int | |
945 | kauth_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 | */ | |
953 | int | |
954 | kauth_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 | */ | |
962 | int | |
963 | kauth_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 | */ | |
972 | int | |
973 | kauth_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 | */ | |
981 | int | |
982 | kauth_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 | */ | |
990 | int | |
991 | kauth_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 | */ | |
1000 | int | |
1001 | kauth_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 | */ | |
1009 | int | |
1010 | kauth_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 | */ | |
1020 | static int | |
1021 | kauth_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); | |
1158 | found: | |
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 | ||
1186 | struct 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 | ||
1195 | TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups; | |
1196 | #define KAUTH_GROUPS_CACHEMAX 100 /* XXX sizing? */ | |
1197 | static int kauth_groups_count; | |
1198 | ||
1199 | static 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 | ||
1203 | static int kauth_groups_expired(struct kauth_group_membership *gm); | |
1204 | static void kauth_groups_lru(struct kauth_group_membership *gm); | |
1205 | static void kauth_groups_updatecache(struct kauth_identity_extlookup *el); | |
1206 | ||
1207 | void | |
1208 | kauth_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 | ||
1214 | static int | |
1215 | kauth_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 | ||
1223 | static void | |
1224 | kauth_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 | ||
1232 | static void | |
1233 | kauth_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 | */ | |
1308 | int | |
1309 | kauth_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 | */ | |
1395 | int | |
1396 | kauth_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 | */ | |
1431 | int | |
1432 | kauth_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 */ | |
1442 | static 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 | ||
1446 | void | |
1447 | kauth_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 | */ | |
1466 | uid_t | |
1467 | kauth_getuid(void) | |
1468 | { | |
1469 | return(kauth_cred_get()->cr_uid); | |
1470 | } | |
1471 | ||
1472 | /* | |
1473 | * Return the current thread's real UID. | |
1474 | */ | |
1475 | uid_t | |
1476 | kauth_getruid(void) | |
1477 | { | |
1478 | return(kauth_cred_get()->cr_ruid); | |
1479 | } | |
1480 | ||
1481 | /* | |
1482 | * Return the current thread's effective GID. | |
1483 | */ | |
1484 | gid_t | |
1485 | kauth_getgid(void) | |
1486 | { | |
1487 | return(kauth_cred_get()->cr_groups[0]); | |
1488 | } | |
1489 | ||
1490 | /* | |
1491 | * Return the current thread's real GID. | |
1492 | */ | |
1493 | gid_t | |
1494 | kauth_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 | */ | |
1504 | kauth_cred_t | |
1505 | kauth_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 | */ | |
1532 | kauth_cred_t | |
1533 | kauth_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 | */ | |
1564 | kauth_cred_t | |
1565 | kauth_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 | */ | |
1579 | kauth_cred_t | |
1580 | kauth_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 | */ | |
1611 | kauth_cred_t | |
1612 | kauth_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 | */ | |
1662 | kauth_cred_t | |
1663 | kauth_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 | */ | |
1694 | kauth_cred_t | |
1695 | kauth_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 | */ | |
1724 | kauth_cred_t | |
1725 | kauth_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 | */ | |
1755 | kauth_cred_t | |
1756 | kauth_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 | */ | |
1786 | kauth_cred_t | |
1787 | kauth_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 | */ | |
1826 | kauth_cred_t | |
1827 | kauth_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 | */ | |
1865 | kauth_cred_t | |
1866 | kauth_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 | */ | |
1895 | kauth_cred_t | |
1896 | kauth_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 | */ | |
1922 | void | |
1923 | kauth_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 | */ | |
1940 | void | |
1941 | kauth_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 | */ | |
1966 | kauth_cred_t | |
1967 | kauth_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 | */ | |
1988 | kauth_cred_t | |
1989 | kauth_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 | */ | |
2053 | static 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 | */ | |
2106 | static 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 | */ | |
2135 | static 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 | */ | |
2172 | kauth_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 | */ | |
2203 | static 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 | */ | |
2216 | static 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 | |
2234 | static 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 | ||
2259 | static 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 |