xref: /netbsd-src/sys/kern/kern_auth.c (revision c29d51755812ace2e87aeefdb06cb2b4dac7087a)
1 /* $NetBSD: kern_auth.c,v 1.74 2015/08/08 07:53:51 mlelstv Exp $ */
2 
3 /*-
4  * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.74 2015/08/08 07:53:51 mlelstv Exp $");
32 
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/proc.h>
37 #include <sys/ucred.h>
38 #include <sys/pool.h>
39 #include <sys/kauth.h>
40 #include <sys/kmem.h>
41 #include <sys/rwlock.h>
42 #include <sys/sysctl.h>
43 #include <sys/atomic.h>
44 #include <sys/specificdata.h>
45 #include <sys/vnode.h>
46 
47 #include <secmodel/secmodel.h>
48 
49 /*
50  * Secmodel-specific credentials.
51  */
52 struct kauth_key {
53 	secmodel_t ks_secmodel;		/* secmodel */
54 	specificdata_key_t ks_key;	/* key */
55 };
56 
57 /*
58  * Credentials.
59  *
60  * A subset of this structure is used in kvm(3) (src/lib/libkvm/kvm_proc.c)
61  * and should be synchronized with this structure when the update is
62  * relevant.
63  */
64 struct kauth_cred {
65 	/*
66 	 * Ensure that the first part of the credential resides in its own
67 	 * cache line.  Due to sharing there aren't many kauth_creds in a
68 	 * typical system, but the reference counts change very often.
69 	 * Keeping it separate from the rest of the data prevents false
70 	 * sharing between CPUs.
71 	 */
72 	u_int cr_refcnt;		/* reference count */
73 #if COHERENCY_UNIT > 4
74 	uint8_t cr_pad[COHERENCY_UNIT - 4];
75 #endif
76 	uid_t cr_uid;			/* user id */
77 	uid_t cr_euid;			/* effective user id */
78 	uid_t cr_svuid;			/* saved effective user id */
79 	gid_t cr_gid;			/* group id */
80 	gid_t cr_egid;			/* effective group id */
81 	gid_t cr_svgid;			/* saved effective group id */
82 	u_int cr_ngroups;		/* number of groups */
83 	gid_t cr_groups[NGROUPS];	/* group memberships */
84 	specificdata_reference cr_sd;	/* specific data */
85 };
86 
87 /*
88  * Listener.
89  */
90 struct kauth_listener {
91 	kauth_scope_callback_t		func;		/* callback */
92 	kauth_scope_t			scope;		/* scope backpointer */
93 	u_int				refcnt;		/* reference count */
94 	SIMPLEQ_ENTRY(kauth_listener)	listener_next;	/* listener list */
95 };
96 
97 /*
98  * Scope.
99  */
100 struct kauth_scope {
101 	const char		       *id;		/* scope name */
102 	void			       *cookie;		/* user cookie */
103 	u_int				nlisteners;	/* # of listeners */
104 	SIMPLEQ_HEAD(, kauth_listener)	listenq;	/* listener list */
105 	SIMPLEQ_ENTRY(kauth_scope)	next_scope;	/* scope list */
106 };
107 
108 static int kauth_cred_hook(kauth_cred_t, kauth_action_t, void *, void *);
109 
110 /* List of scopes and its lock. */
111 static SIMPLEQ_HEAD(, kauth_scope) scope_list =
112     SIMPLEQ_HEAD_INITIALIZER(scope_list);
113 
114 /* Built-in scopes: generic, process. */
115 static kauth_scope_t kauth_builtin_scope_generic;
116 static kauth_scope_t kauth_builtin_scope_system;
117 static kauth_scope_t kauth_builtin_scope_process;
118 static kauth_scope_t kauth_builtin_scope_network;
119 static kauth_scope_t kauth_builtin_scope_machdep;
120 static kauth_scope_t kauth_builtin_scope_device;
121 static kauth_scope_t kauth_builtin_scope_cred;
122 static kauth_scope_t kauth_builtin_scope_vnode;
123 
124 static specificdata_domain_t kauth_domain;
125 static pool_cache_t kauth_cred_cache;
126 
127 krwlock_t	kauth_lock;
128 
129 /* Allocate new, empty kauth credentials. */
130 kauth_cred_t
131 kauth_cred_alloc(void)
132 {
133 	kauth_cred_t cred;
134 
135 	cred = pool_cache_get(kauth_cred_cache, PR_WAITOK);
136 
137 	cred->cr_refcnt = 1;
138 	cred->cr_uid = 0;
139 	cred->cr_euid = 0;
140 	cred->cr_svuid = 0;
141 	cred->cr_gid = 0;
142 	cred->cr_egid = 0;
143 	cred->cr_svgid = 0;
144 	cred->cr_ngroups = 0;
145 
146 	specificdata_init(kauth_domain, &cred->cr_sd);
147 	kauth_cred_hook(cred, KAUTH_CRED_INIT, NULL, NULL);
148 
149 	return (cred);
150 }
151 
152 /* Increment reference count to cred. */
153 void
154 kauth_cred_hold(kauth_cred_t cred)
155 {
156 	KASSERT(cred != NULL);
157 	KASSERT(cred != NOCRED);
158 	KASSERT(cred != FSCRED);
159 	KASSERT(cred->cr_refcnt > 0);
160 
161 	atomic_inc_uint(&cred->cr_refcnt);
162 }
163 
164 /* Decrease reference count to cred. If reached zero, free it. */
165 void
166 kauth_cred_free(kauth_cred_t cred)
167 {
168 
169 	KASSERT(cred != NULL);
170 	KASSERT(cred != NOCRED);
171 	KASSERT(cred != FSCRED);
172 	KASSERT(cred->cr_refcnt > 0);
173 	ASSERT_SLEEPABLE();
174 
175 	if (atomic_dec_uint_nv(&cred->cr_refcnt) > 0)
176 		return;
177 
178 	kauth_cred_hook(cred, KAUTH_CRED_FREE, NULL, NULL);
179 	specificdata_fini(kauth_domain, &cred->cr_sd);
180 	pool_cache_put(kauth_cred_cache, cred);
181 }
182 
183 static void
184 kauth_cred_clone1(kauth_cred_t from, kauth_cred_t to, bool copy_groups)
185 {
186 	KASSERT(from != NULL);
187 	KASSERT(from != NOCRED);
188 	KASSERT(from != FSCRED);
189 	KASSERT(to != NULL);
190 	KASSERT(to != NOCRED);
191 	KASSERT(to != FSCRED);
192 	KASSERT(from->cr_refcnt > 0);
193 
194 	to->cr_uid = from->cr_uid;
195 	to->cr_euid = from->cr_euid;
196 	to->cr_svuid = from->cr_svuid;
197 	to->cr_gid = from->cr_gid;
198 	to->cr_egid = from->cr_egid;
199 	to->cr_svgid = from->cr_svgid;
200 	if (copy_groups) {
201 		to->cr_ngroups = from->cr_ngroups;
202 		memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups));
203 	}
204 
205 	kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL);
206 }
207 
208 void
209 kauth_cred_clone(kauth_cred_t from, kauth_cred_t to)
210 {
211 	kauth_cred_clone1(from, to, true);
212 }
213 
214 /*
215  * Duplicate cred and return a new kauth_cred_t.
216  */
217 kauth_cred_t
218 kauth_cred_dup(kauth_cred_t cred)
219 {
220 	kauth_cred_t new_cred;
221 
222 	KASSERT(cred != NULL);
223 	KASSERT(cred != NOCRED);
224 	KASSERT(cred != FSCRED);
225 	KASSERT(cred->cr_refcnt > 0);
226 
227 	new_cred = kauth_cred_alloc();
228 
229 	kauth_cred_clone(cred, new_cred);
230 
231 	return (new_cred);
232 }
233 
234 /*
235  * Similar to crcopy(), only on a kauth_cred_t.
236  * XXX: Is this even needed? [kauth_cred_copy]
237  */
238 kauth_cred_t
239 kauth_cred_copy(kauth_cred_t cred)
240 {
241 	kauth_cred_t new_cred;
242 
243 	KASSERT(cred != NULL);
244 	KASSERT(cred != NOCRED);
245 	KASSERT(cred != FSCRED);
246 	KASSERT(cred->cr_refcnt > 0);
247 
248 	/* If the provided credentials already have one reference, use them. */
249 	if (cred->cr_refcnt == 1)
250 		return (cred);
251 
252 	new_cred = kauth_cred_alloc();
253 
254 	kauth_cred_clone(cred, new_cred);
255 
256 	kauth_cred_free(cred);
257 
258 	return (new_cred);
259 }
260 
261 void
262 kauth_proc_fork(struct proc *parent, struct proc *child)
263 {
264 
265 	mutex_enter(parent->p_lock);
266 	kauth_cred_hold(parent->p_cred);
267 	child->p_cred = parent->p_cred;
268 	mutex_exit(parent->p_lock);
269 
270 	/* XXX: relies on parent process stalling during fork() */
271 	kauth_cred_hook(parent->p_cred, KAUTH_CRED_FORK, parent,
272 	    child);
273 }
274 
275 void
276 kauth_proc_chroot(kauth_cred_t cred, struct cwdinfo *cwdi)
277 {
278 	kauth_cred_hook(cred, KAUTH_CRED_CHROOT, cwdi, NULL);
279 }
280 
281 uid_t
282 kauth_cred_getuid(kauth_cred_t cred)
283 {
284 	KASSERT(cred != NULL);
285 	KASSERT(cred != NOCRED);
286 	KASSERT(cred != FSCRED);
287 
288 	return (cred->cr_uid);
289 }
290 
291 uid_t
292 kauth_cred_geteuid(kauth_cred_t cred)
293 {
294 	KASSERT(cred != NULL);
295 	KASSERT(cred != NOCRED);
296 	KASSERT(cred != FSCRED);
297 
298 	return (cred->cr_euid);
299 }
300 
301 uid_t
302 kauth_cred_getsvuid(kauth_cred_t cred)
303 {
304 	KASSERT(cred != NULL);
305 	KASSERT(cred != NOCRED);
306 	KASSERT(cred != FSCRED);
307 
308 	return (cred->cr_svuid);
309 }
310 
311 gid_t
312 kauth_cred_getgid(kauth_cred_t cred)
313 {
314 	KASSERT(cred != NULL);
315 	KASSERT(cred != NOCRED);
316 	KASSERT(cred != FSCRED);
317 
318 	return (cred->cr_gid);
319 }
320 
321 gid_t
322 kauth_cred_getegid(kauth_cred_t cred)
323 {
324 	KASSERT(cred != NULL);
325 	KASSERT(cred != NOCRED);
326 	KASSERT(cred != FSCRED);
327 
328 	return (cred->cr_egid);
329 }
330 
331 gid_t
332 kauth_cred_getsvgid(kauth_cred_t cred)
333 {
334 	KASSERT(cred != NULL);
335 	KASSERT(cred != NOCRED);
336 	KASSERT(cred != FSCRED);
337 
338 	return (cred->cr_svgid);
339 }
340 
341 void
342 kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
343 {
344 	KASSERT(cred != NULL);
345 	KASSERT(cred != NOCRED);
346 	KASSERT(cred != FSCRED);
347 	KASSERT(cred->cr_refcnt == 1);
348 
349 	cred->cr_uid = uid;
350 }
351 
352 void
353 kauth_cred_seteuid(kauth_cred_t cred, uid_t uid)
354 {
355 	KASSERT(cred != NULL);
356 	KASSERT(cred != NOCRED);
357 	KASSERT(cred != FSCRED);
358 	KASSERT(cred->cr_refcnt == 1);
359 
360 	cred->cr_euid = uid;
361 }
362 
363 void
364 kauth_cred_setsvuid(kauth_cred_t cred, uid_t uid)
365 {
366 	KASSERT(cred != NULL);
367 	KASSERT(cred != NOCRED);
368 	KASSERT(cred != FSCRED);
369 	KASSERT(cred->cr_refcnt == 1);
370 
371 	cred->cr_svuid = uid;
372 }
373 
374 void
375 kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
376 {
377 	KASSERT(cred != NULL);
378 	KASSERT(cred != NOCRED);
379 	KASSERT(cred != FSCRED);
380 	KASSERT(cred->cr_refcnt == 1);
381 
382 	cred->cr_gid = gid;
383 }
384 
385 void
386 kauth_cred_setegid(kauth_cred_t cred, gid_t gid)
387 {
388 	KASSERT(cred != NULL);
389 	KASSERT(cred != NOCRED);
390 	KASSERT(cred != FSCRED);
391 	KASSERT(cred->cr_refcnt == 1);
392 
393 	cred->cr_egid = gid;
394 }
395 
396 void
397 kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid)
398 {
399 	KASSERT(cred != NULL);
400 	KASSERT(cred != NOCRED);
401 	KASSERT(cred != FSCRED);
402 	KASSERT(cred->cr_refcnt == 1);
403 
404 	cred->cr_svgid = gid;
405 }
406 
407 /* Checks if gid is a member of the groups in cred. */
408 int
409 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
410 {
411 	uint32_t i;
412 
413 	KASSERT(cred != NULL);
414 	KASSERT(cred != NOCRED);
415 	KASSERT(cred != FSCRED);
416 	KASSERT(resultp != NULL);
417 
418 	*resultp = 0;
419 
420 	for (i = 0; i < cred->cr_ngroups; i++)
421 		if (cred->cr_groups[i] == gid) {
422 			*resultp = 1;
423 			break;
424 		}
425 
426 	return (0);
427 }
428 
429 u_int
430 kauth_cred_ngroups(kauth_cred_t cred)
431 {
432 	KASSERT(cred != NULL);
433 	KASSERT(cred != NOCRED);
434 	KASSERT(cred != FSCRED);
435 
436 	return (cred->cr_ngroups);
437 }
438 
439 /*
440  * Return the group at index idx from the groups in cred.
441  */
442 gid_t
443 kauth_cred_group(kauth_cred_t cred, u_int idx)
444 {
445 	KASSERT(cred != NULL);
446 	KASSERT(cred != NOCRED);
447 	KASSERT(cred != FSCRED);
448 	KASSERT(idx < cred->cr_ngroups);
449 
450 	return (cred->cr_groups[idx]);
451 }
452 
453 /* XXX elad: gmuid is unused for now. */
454 int
455 kauth_cred_setgroups(kauth_cred_t cred, const gid_t *grbuf, size_t len,
456     uid_t gmuid, enum uio_seg seg)
457 {
458 	int error = 0;
459 
460 	KASSERT(cred != NULL);
461 	KASSERT(cred != NOCRED);
462 	KASSERT(cred != FSCRED);
463 	KASSERT(cred->cr_refcnt == 1);
464 
465 	if (len > __arraycount(cred->cr_groups))
466 		return EINVAL;
467 
468 	if (len) {
469 		if (seg == UIO_SYSSPACE) {
470 			memcpy(cred->cr_groups, grbuf,
471 			    len * sizeof(cred->cr_groups[0]));
472 		} else {
473 			error = copyin(grbuf, cred->cr_groups,
474 			    len * sizeof(cred->cr_groups[0]));
475 			if (error != 0)
476 				len = 0;
477 		}
478 	}
479 	memset(cred->cr_groups + len, 0xff,
480 	    sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0])));
481 
482 	cred->cr_ngroups = len;
483 
484 	return error;
485 }
486 
487 /* This supports sys_setgroups() */
488 int
489 kauth_proc_setgroups(struct lwp *l, kauth_cred_t ncred)
490 {
491 	kauth_cred_t cred;
492 	int error;
493 
494 	/*
495 	 * At this point we could delete duplicate groups from ncred,
496 	 * and plausibly sort the list - but in general the later is
497 	 * a bad idea.
498 	 */
499 	proc_crmod_enter();
500 	/* Maybe we should use curproc here ? */
501 	cred = l->l_proc->p_cred;
502 
503 	kauth_cred_clone1(cred, ncred, false);
504 
505 	error = kauth_authorize_process(cred, KAUTH_PROCESS_SETID,
506 	    l->l_proc, NULL, NULL, NULL);
507 	if (error != 0) {
508 		proc_crmod_leave(cred, ncred, false);
509 			return error;
510 	}
511 
512 	/* Broadcast our credentials to the process and other LWPs. */
513  	proc_crmod_leave(ncred, cred, true);
514 	return 0;
515 }
516 
517 int
518 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len,
519     enum uio_seg seg)
520 {
521 	KASSERT(cred != NULL);
522 
523 	if (len > cred->cr_ngroups)
524 		return EINVAL;
525 
526 	if (seg == UIO_USERSPACE)
527 		return copyout(cred->cr_groups, grbuf, sizeof(*grbuf) * len);
528 	memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len);
529 
530 	return 0;
531 }
532 
533 int
534 kauth_register_key(secmodel_t secmodel, kauth_key_t *result)
535 {
536 	kauth_key_t k;
537 	specificdata_key_t key;
538 	int error;
539 
540 	KASSERT(result != NULL);
541 
542 	error = specificdata_key_create(kauth_domain, &key, NULL);
543 	if (error)
544 		return (error);
545 
546 	k = kmem_alloc(sizeof(*k), KM_SLEEP);
547 	k->ks_secmodel = secmodel;
548 	k->ks_key = key;
549 
550 	*result = k;
551 
552 	return (0);
553 }
554 
555 int
556 kauth_deregister_key(kauth_key_t key)
557 {
558 	KASSERT(key != NULL);
559 
560 	specificdata_key_delete(kauth_domain, key->ks_key);
561 	kmem_free(key, sizeof(*key));
562 
563 	return (0);
564 }
565 
566 void *
567 kauth_cred_getdata(kauth_cred_t cred, kauth_key_t key)
568 {
569 	KASSERT(cred != NULL);
570 	KASSERT(cred != NOCRED);
571 	KASSERT(cred != FSCRED);
572 	KASSERT(key != NULL);
573 
574 	return (specificdata_getspecific(kauth_domain, &cred->cr_sd,
575 	    key->ks_key));
576 }
577 
578 void
579 kauth_cred_setdata(kauth_cred_t cred, kauth_key_t key, void *data)
580 {
581 	KASSERT(cred != NULL);
582 	KASSERT(cred != NOCRED);
583 	KASSERT(cred != FSCRED);
584 	KASSERT(key != NULL);
585 
586 	specificdata_setspecific(kauth_domain, &cred->cr_sd, key->ks_key, data);
587 }
588 
589 /*
590  * Match uids in two credentials.
591  */
592 int
593 kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2)
594 {
595 	KASSERT(cred1 != NULL);
596 	KASSERT(cred1 != NOCRED);
597 	KASSERT(cred1 != FSCRED);
598 	KASSERT(cred2 != NULL);
599 	KASSERT(cred2 != NOCRED);
600 	KASSERT(cred2 != FSCRED);
601 
602 	if (cred1->cr_uid == cred2->cr_uid ||
603 	    cred1->cr_euid == cred2->cr_uid ||
604 	    cred1->cr_uid == cred2->cr_euid ||
605 	    cred1->cr_euid == cred2->cr_euid)
606 		return (1);
607 
608 	return (0);
609 }
610 
611 u_int
612 kauth_cred_getrefcnt(kauth_cred_t cred)
613 {
614 	KASSERT(cred != NULL);
615 	KASSERT(cred != NOCRED);
616 	KASSERT(cred != FSCRED);
617 
618 	return (cred->cr_refcnt);
619 }
620 
621 /*
622  * Convert userland credentials (struct uucred) to kauth_cred_t.
623  * XXX: For NFS & puffs
624  */
625 void
626 kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc)
627 {
628 	KASSERT(cred != NULL);
629 	KASSERT(cred != NOCRED);
630 	KASSERT(cred != FSCRED);
631 	KASSERT(uuc != NULL);
632 
633 	cred->cr_refcnt = 1;
634 	cred->cr_uid = uuc->cr_uid;
635 	cred->cr_euid = uuc->cr_uid;
636 	cred->cr_svuid = uuc->cr_uid;
637 	cred->cr_gid = uuc->cr_gid;
638 	cred->cr_egid = uuc->cr_gid;
639 	cred->cr_svgid = uuc->cr_gid;
640 	cred->cr_ngroups = min(uuc->cr_ngroups, NGROUPS);
641 	kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
642 	    cred->cr_ngroups, -1, UIO_SYSSPACE);
643 }
644 
645 /*
646  * Convert kauth_cred_t to userland credentials (struct uucred).
647  * XXX: For NFS & puffs
648  */
649 void
650 kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred)
651 {
652 	KASSERT(cred != NULL);
653 	KASSERT(cred != NOCRED);
654 	KASSERT(cred != FSCRED);
655 	KASSERT(uuc != NULL);
656 	int ng;
657 
658 	ng = min(cred->cr_ngroups, NGROUPS);
659 	uuc->cr_uid = cred->cr_euid;
660 	uuc->cr_gid = cred->cr_egid;
661 	uuc->cr_ngroups = ng;
662 	kauth_cred_getgroups(cred, uuc->cr_groups, ng, UIO_SYSSPACE);
663 }
664 
665 /*
666  * Compare kauth_cred_t and uucred credentials.
667  * XXX: Modelled after crcmp() for NFS.
668  */
669 int
670 kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
671 {
672 	KASSERT(cred != NULL);
673 	KASSERT(cred != NOCRED);
674 	KASSERT(cred != FSCRED);
675 	KASSERT(uuc != NULL);
676 
677 	if (cred->cr_euid == uuc->cr_uid &&
678 	    cred->cr_egid == uuc->cr_gid &&
679 	    cred->cr_ngroups == (uint32_t)uuc->cr_ngroups) {
680 		int i;
681 
682 		/* Check if all groups from uuc appear in cred. */
683 		for (i = 0; i < uuc->cr_ngroups; i++) {
684 			int ismember;
685 
686 			ismember = 0;
687 			if (kauth_cred_ismember_gid(cred, uuc->cr_groups[i],
688 			    &ismember) != 0 || !ismember)
689 				return (1);
690 		}
691 
692 		return (0);
693 	}
694 
695 	return (1);
696 }
697 
698 /*
699  * Make a struct ucred out of a kauth_cred_t.  For compatibility.
700  */
701 void
702 kauth_cred_toucred(kauth_cred_t cred, struct ki_ucred *uc)
703 {
704 	KASSERT(cred != NULL);
705 	KASSERT(cred != NOCRED);
706 	KASSERT(cred != FSCRED);
707 	KASSERT(uc != NULL);
708 
709 	uc->cr_ref = cred->cr_refcnt;
710 	uc->cr_uid = cred->cr_euid;
711 	uc->cr_gid = cred->cr_egid;
712 	uc->cr_ngroups = min(cred->cr_ngroups, __arraycount(uc->cr_groups));
713 	memcpy(uc->cr_groups, cred->cr_groups,
714 	       uc->cr_ngroups * sizeof(uc->cr_groups[0]));
715 }
716 
717 /*
718  * Make a struct pcred out of a kauth_cred_t.  For compatibility.
719  */
720 void
721 kauth_cred_topcred(kauth_cred_t cred, struct ki_pcred *pc)
722 {
723 	KASSERT(cred != NULL);
724 	KASSERT(cred != NOCRED);
725 	KASSERT(cred != FSCRED);
726 	KASSERT(pc != NULL);
727 
728 	pc->p_pad = NULL;
729 	pc->p_ruid = cred->cr_uid;
730 	pc->p_svuid = cred->cr_svuid;
731 	pc->p_rgid = cred->cr_gid;
732 	pc->p_svgid = cred->cr_svgid;
733 	pc->p_refcnt = cred->cr_refcnt;
734 }
735 
736 /*
737  * Return kauth_cred_t for the current LWP.
738  */
739 kauth_cred_t
740 kauth_cred_get(void)
741 {
742 	return (curlwp->l_cred);
743 }
744 
745 /*
746  * Returns a scope matching the provided id.
747  * Requires the scope list lock to be held by the caller.
748  */
749 static kauth_scope_t
750 kauth_ifindscope(const char *id)
751 {
752 	kauth_scope_t scope;
753 
754 	KASSERT(rw_lock_held(&kauth_lock));
755 
756 	scope = NULL;
757 	SIMPLEQ_FOREACH(scope, &scope_list, next_scope) {
758 		if (strcmp(scope->id, id) == 0)
759 			break;
760 	}
761 
762 	return (scope);
763 }
764 
765 /*
766  * Register a new scope.
767  *
768  * id - identifier for the scope
769  * callback - the scope's default listener
770  * cookie - cookie to be passed to the listener(s)
771  */
772 kauth_scope_t
773 kauth_register_scope(const char *id, kauth_scope_callback_t callback,
774     void *cookie)
775 {
776 	kauth_scope_t scope;
777 	kauth_listener_t listener = NULL; /* XXX gcc */
778 
779 	/* Sanitize input */
780 	if (id == NULL)
781 		return (NULL);
782 
783 	/* Allocate space for a new scope and listener. */
784 	scope = kmem_alloc(sizeof(*scope), KM_SLEEP);
785 	if (scope == NULL)
786 		return NULL;
787 	if (callback != NULL) {
788 		listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
789 		if (listener == NULL) {
790 			kmem_free(scope, sizeof(*scope));
791 			return (NULL);
792 		}
793 	}
794 
795 	/*
796 	 * Acquire scope list lock.
797 	 */
798 	rw_enter(&kauth_lock, RW_WRITER);
799 
800 	/* Check we don't already have a scope with the same id */
801 	if (kauth_ifindscope(id) != NULL) {
802 		rw_exit(&kauth_lock);
803 
804 		kmem_free(scope, sizeof(*scope));
805 		if (callback != NULL)
806 			kmem_free(listener, sizeof(*listener));
807 
808 		return (NULL);
809 	}
810 
811 	/* Initialize new scope with parameters */
812 	scope->id = id;
813 	scope->cookie = cookie;
814 	scope->nlisteners = 1;
815 
816 	SIMPLEQ_INIT(&scope->listenq);
817 
818 	/* Add default listener */
819 	if (callback != NULL) {
820 		listener->func = callback;
821 		listener->scope = scope;
822 		listener->refcnt = 0;
823 		SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next);
824 	}
825 
826 	/* Insert scope to scopes list */
827 	SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope);
828 
829 	rw_exit(&kauth_lock);
830 
831 	return (scope);
832 }
833 
834 /*
835  * Initialize the kernel authorization subsystem.
836  *
837  * Initialize the scopes list lock.
838  * Create specificdata domain.
839  * Register the credentials scope, used in kauth(9) internally.
840  * Register built-in scopes: generic, system, process, network, machdep, device.
841  */
842 void
843 kauth_init(void)
844 {
845 	rw_init(&kauth_lock);
846 
847 	kauth_cred_cache = pool_cache_init(sizeof(struct kauth_cred),
848 	    coherency_unit, 0, 0, "kcredpl", NULL, IPL_NONE,
849 	    NULL, NULL, NULL);
850 
851 	/* Create specificdata domain. */
852 	kauth_domain = specificdata_domain_create();
853 
854 	/* Register credentials scope. */
855 	kauth_builtin_scope_cred =
856 	    kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL);
857 
858 	/* Register generic scope. */
859 	kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC,
860 	    NULL, NULL);
861 
862 	/* Register system scope. */
863 	kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM,
864 	    NULL, NULL);
865 
866 	/* Register process scope. */
867 	kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS,
868 	    NULL, NULL);
869 
870 	/* Register network scope. */
871 	kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK,
872 	    NULL, NULL);
873 
874 	/* Register machdep scope. */
875 	kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP,
876 	    NULL, NULL);
877 
878 	/* Register device scope. */
879 	kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE,
880 	    NULL, NULL);
881 
882 	/* Register vnode scope. */
883 	kauth_builtin_scope_vnode = kauth_register_scope(KAUTH_SCOPE_VNODE,
884 	    NULL, NULL);
885 }
886 
887 /*
888  * Deregister a scope.
889  * Requires scope list lock to be held by the caller.
890  *
891  * scope - the scope to deregister
892  */
893 void
894 kauth_deregister_scope(kauth_scope_t scope)
895 {
896 	if (scope != NULL) {
897 		/* Remove scope from list */
898 		SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope);
899 		kmem_free(scope, sizeof(*scope));
900 	}
901 }
902 
903 /*
904  * Register a listener.
905  *
906  * id - scope identifier.
907  * callback - the callback routine for the listener.
908  * cookie - cookie to pass unmoidfied to the callback.
909  */
910 kauth_listener_t
911 kauth_listen_scope(const char *id, kauth_scope_callback_t callback,
912    void *cookie)
913 {
914 	kauth_scope_t scope;
915 	kauth_listener_t listener;
916 
917 	listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
918 	if (listener == NULL)
919 		return (NULL);
920 
921 	rw_enter(&kauth_lock, RW_WRITER);
922 
923 	/*
924 	 * Find scope struct.
925 	 */
926 	scope = kauth_ifindscope(id);
927 	if (scope == NULL) {
928 		rw_exit(&kauth_lock);
929 		kmem_free(listener, sizeof(*listener));
930 		return (NULL);
931 	}
932 
933 	/* Allocate listener */
934 
935 	/* Initialize listener with parameters */
936 	listener->func = callback;
937 	listener->refcnt = 0;
938 
939 	/* Add listener to scope */
940 	SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next);
941 
942 	/* Raise number of listeners on scope. */
943 	scope->nlisteners++;
944 	listener->scope = scope;
945 
946 	rw_exit(&kauth_lock);
947 
948 	return (listener);
949 }
950 
951 /*
952  * Deregister a listener.
953  *
954  * listener - listener reference as returned from kauth_listen_scope().
955  */
956 void
957 kauth_unlisten_scope(kauth_listener_t listener)
958 {
959 
960 	if (listener != NULL) {
961 		rw_enter(&kauth_lock, RW_WRITER);
962 		SIMPLEQ_REMOVE(&listener->scope->listenq, listener,
963 		    kauth_listener, listener_next);
964 		listener->scope->nlisteners--;
965 		rw_exit(&kauth_lock);
966 		kmem_free(listener, sizeof(*listener));
967 	}
968 }
969 
970 /*
971  * Authorize a request.
972  *
973  * scope - the scope of the request as defined by KAUTH_SCOPE_* or as
974  *	   returned from kauth_register_scope().
975  * credential - credentials of the user ("actor") making the request.
976  * action - request identifier.
977  * arg[0-3] - passed unmodified to listener(s).
978  *
979  * Returns the aggregated result:
980  *     - KAUTH_RESULT_ALLOW if there is at least one KAUTH_RESULT_ALLOW and
981  *       zero KAUTH_DESULT_DENY
982  *     - KAUTH_RESULT_DENY if there is at least one KAUTH_RESULT_DENY
983  *     - KAUTH_RESULT_DEFER if there is nothing but KAUTH_RESULT_DEFER
984  */
985 static int
986 kauth_authorize_action_internal(kauth_scope_t scope, kauth_cred_t cred,
987     kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3)
988 {
989 	kauth_listener_t listener;
990 	int error, allow, fail;
991 
992 	KASSERT(cred != NULL);
993 	KASSERT(action != 0);
994 
995 	/* Short-circuit requests coming from the kernel. */
996 	if (cred == NOCRED || cred == FSCRED)
997 		return KAUTH_RESULT_ALLOW;
998 
999 	KASSERT(scope != NULL);
1000 
1001 	fail = 0;
1002 	allow = 0;
1003 
1004 	/* rw_enter(&kauth_lock, RW_READER); XXX not yet */
1005 	SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) {
1006 		error = listener->func(cred, action, scope->cookie, arg0,
1007 		    arg1, arg2, arg3);
1008 
1009 		if (error == KAUTH_RESULT_ALLOW)
1010 			allow = 1;
1011 		else if (error == KAUTH_RESULT_DENY)
1012 			fail = 1;
1013 	}
1014 	/* rw_exit(&kauth_lock); */
1015 
1016 	if (fail)
1017 		return (KAUTH_RESULT_DENY);
1018 
1019 	if (allow)
1020 		return (KAUTH_RESULT_ALLOW);
1021 
1022 	return (KAUTH_RESULT_DEFER);
1023 };
1024 
1025 int
1026 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred,
1027     kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3)
1028 {
1029 	int r;
1030 
1031 	r = kauth_authorize_action_internal(scope, cred, action, arg0, arg1,
1032 	    arg2, arg3);
1033 
1034 	if (r == KAUTH_RESULT_DENY)
1035 		return (EPERM);
1036 
1037 	if (r == KAUTH_RESULT_ALLOW)
1038 		return (0);
1039 
1040 	if (secmodel_nsecmodels() == 0)
1041 		return (0);
1042 
1043 	return (EPERM);
1044 }
1045 
1046 /*
1047  * Generic scope authorization wrapper.
1048  */
1049 int
1050 kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0)
1051 {
1052 	return (kauth_authorize_action(kauth_builtin_scope_generic, cred,
1053 	    action, arg0, NULL, NULL, NULL));
1054 }
1055 
1056 /*
1057  * System scope authorization wrapper.
1058  */
1059 int
1060 kauth_authorize_system(kauth_cred_t cred, kauth_action_t action,
1061     enum kauth_system_req req, void *arg1, void *arg2, void *arg3)
1062 {
1063 	return (kauth_authorize_action(kauth_builtin_scope_system, cred,
1064 	    action, (void *)req, arg1, arg2, arg3));
1065 }
1066 
1067 /*
1068  * Process scope authorization wrapper.
1069  */
1070 int
1071 kauth_authorize_process(kauth_cred_t cred, kauth_action_t action,
1072     struct proc *p, void *arg1, void *arg2, void *arg3)
1073 {
1074 	return (kauth_authorize_action(kauth_builtin_scope_process, cred,
1075 	    action, p, arg1, arg2, arg3));
1076 }
1077 
1078 /*
1079  * Network scope authorization wrapper.
1080  */
1081 int
1082 kauth_authorize_network(kauth_cred_t cred, kauth_action_t action,
1083     enum kauth_network_req req, void *arg1, void *arg2, void *arg3)
1084 {
1085 	return (kauth_authorize_action(kauth_builtin_scope_network, cred,
1086 	    action, (void *)req, arg1, arg2, arg3));
1087 }
1088 
1089 int
1090 kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action,
1091     void *arg0, void *arg1, void *arg2, void *arg3)
1092 {
1093 	return (kauth_authorize_action(kauth_builtin_scope_machdep, cred,
1094 	    action, arg0, arg1, arg2, arg3));
1095 }
1096 
1097 int
1098 kauth_authorize_device(kauth_cred_t cred, kauth_action_t action,
1099     void *arg0, void *arg1, void *arg2, void *arg3)
1100 {
1101 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1102 	    action, arg0, arg1, arg2, arg3));
1103 }
1104 
1105 int
1106 kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action,
1107     struct tty *tty)
1108 {
1109 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1110 	    action, tty, NULL, NULL, NULL));
1111 }
1112 
1113 int
1114 kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req,
1115     struct vnode *vp)
1116 {
1117 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1118 	    KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL));
1119 }
1120 
1121 int
1122 kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits,
1123     void *data)
1124 {
1125 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1126 	    KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev,
1127 	    data, NULL));
1128 }
1129 
1130 kauth_action_t
1131 kauth_mode_to_action(mode_t mode)
1132 {
1133 	kauth_action_t action = 0;
1134 
1135 	if (mode & VREAD)
1136 		action |= KAUTH_VNODE_READ_DATA;
1137 	if (mode & VWRITE)
1138 		action |= KAUTH_VNODE_WRITE_DATA;
1139 	if (mode & VEXEC)
1140 		action |= KAUTH_VNODE_EXECUTE;
1141 
1142 	return action;
1143 }
1144 
1145 kauth_action_t
1146 kauth_extattr_action(mode_t access_mode)
1147 {
1148 	kauth_action_t action = 0;
1149 
1150 	if (access_mode & VREAD)
1151 		action |= KAUTH_VNODE_READ_EXTATTRIBUTES;
1152 	if (access_mode & VWRITE)
1153 		action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
1154 
1155 	return action;
1156 }
1157 
1158 int
1159 kauth_authorize_vnode(kauth_cred_t cred, kauth_action_t action,
1160     struct vnode *vp, struct vnode *dvp, int fs_decision)
1161 {
1162 	int error;
1163 
1164 	error = kauth_authorize_action_internal(kauth_builtin_scope_vnode, cred,
1165 	    action, vp, dvp, NULL, NULL);
1166 
1167 	if (error == KAUTH_RESULT_DENY)
1168 		return (EACCES);
1169 
1170 	if (error == KAUTH_RESULT_ALLOW)
1171 		return (0);
1172 
1173 	/*
1174 	 * If the file-system does not support decision-before-action, we can
1175 	 * only short-circuit the operation (deny). If we're here, it means no
1176 	 * listener denied it, so our only alternative is to supposedly-allow
1177 	 * it and let the file-system have the last word.
1178 	 */
1179 	if (fs_decision == KAUTH_VNODE_REMOTEFS)
1180 		return (0);
1181 
1182 	return (fs_decision);
1183 }
1184 
1185 static int
1186 kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0,
1187     void *arg1)
1188 {
1189 	int r;
1190 
1191 	r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action,
1192 	    arg0, arg1, NULL, NULL);
1193 
1194 #ifdef DIAGNOSTIC
1195 	if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq))
1196 		KASSERT(r == 0);
1197 #endif /* DIAGNOSTIC */
1198 
1199 	return (r);
1200 }
1201