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