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