xref: /netbsd-src/sys/kern/kern_auth.c (revision 503611ba29d4c920cb1878a9ece7ebd1e0ac2e16)
1 /* $NetBSD: kern_auth.c,v 1.20 2006/09/08 21:57:38 elad 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Elad Efrat.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Todo:
35  *   - Garbage collection to pool_put() unused scopes/listeners.
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.20 2006/09/08 21:57:38 elad Exp $");
40 
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/time.h>
45 #include <sys/proc.h>
46 #include <sys/ucred.h>
47 #include <sys/pool.h>
48 #include <sys/kauth.h>
49 #include <sys/acct.h>
50 #include <sys/sysctl.h>
51 
52 /*
53  * Credentials.
54  */
55 struct kauth_cred {
56 	struct simplelock cr_lock;	/* lock on cr_refcnt */
57 	u_int cr_refcnt;		/* reference count */
58 	uid_t cr_uid;			/* user id */
59 	uid_t cr_euid;			/* effective user id */
60 	uid_t cr_svuid;			/* saved effective user id */
61 	gid_t cr_gid;			/* group id */
62 	gid_t cr_egid;			/* effective group id */
63 	gid_t cr_svgid;			/* saved effective group id */
64 	u_int cr_ngroups;		/* number of groups */
65 	gid_t cr_groups[NGROUPS];	/* group memberships */
66 };
67 
68 /*
69  * Listener.
70  */
71 struct kauth_listener {
72 	kauth_scope_callback_t		func;		/* callback */
73 	kauth_scope_t			scope;		/* scope backpointer */
74 	u_int				refcnt;		/* reference count */
75 	SIMPLEQ_ENTRY(kauth_listener)	listener_next;	/* listener list */
76 };
77 
78 /*
79  * Scope.
80  */
81 struct kauth_scope {
82 	const char		       *id;		/* scope name */
83 	void			       *cookie;		/* user cookie */
84 	u_int				nlisteners;	/* # of listeners */
85 	SIMPLEQ_HEAD(, kauth_listener)	listenq;	/* listener list */
86 	SIMPLEQ_ENTRY(kauth_scope)	next_scope;	/* scope list */
87 };
88 
89 static POOL_INIT(kauth_scope_pool, sizeof(struct kauth_scope), 0, 0, 0,
90 	  "kauth_scopepl", &pool_allocator_nointr);
91 static POOL_INIT(kauth_listener_pool, sizeof(struct kauth_listener), 0, 0, 0,
92 	  "kauth_listenerpl", &pool_allocator_nointr);
93 static POOL_INIT(kauth_cred_pool, sizeof(struct kauth_cred), 0, 0, 0,
94 	  "kauth_credpl", &pool_allocator_nointr);
95 
96 /* List of scopes and its lock. */
97 static SIMPLEQ_HEAD(, kauth_scope) scope_list;
98 static struct simplelock scopes_lock;
99 
100 /* Built-in scopes: generic, process. */
101 static kauth_scope_t kauth_builtin_scope_generic;
102 static kauth_scope_t kauth_builtin_scope_system;
103 static kauth_scope_t kauth_builtin_scope_process;
104 static kauth_scope_t kauth_builtin_scope_network;
105 static kauth_scope_t kauth_builtin_scope_machdep;
106 
107 /* Allocate new, empty kauth credentials. */
108 kauth_cred_t
109 kauth_cred_alloc(void)
110 {
111 	kauth_cred_t cred;
112 
113 	cred = pool_get(&kauth_cred_pool, PR_WAITOK);
114 	memset(cred, 0, sizeof(*cred));
115 	simple_lock_init(&cred->cr_lock);
116 	cred->cr_refcnt = 1;
117 
118 	return (cred);
119 }
120 
121 /* Increment reference count to cred. */
122 void
123 kauth_cred_hold(kauth_cred_t cred)
124 {
125 	KASSERT(cred != NULL);
126 	KASSERT(cred->cr_refcnt > 0);
127 
128         simple_lock(&cred->cr_lock);
129         cred->cr_refcnt++;
130         simple_unlock(&cred->cr_lock);
131 }
132 
133 /* Decrease reference count to cred. If reached zero, free it. */
134 void
135 kauth_cred_free(kauth_cred_t cred)
136 {
137 	u_int refcnt;
138 
139 	KASSERT(cred != NULL);
140 	KASSERT(cred->cr_refcnt > 0);
141 
142 	simple_lock(&cred->cr_lock);
143 	refcnt = --cred->cr_refcnt;
144 	simple_unlock(&cred->cr_lock);
145 
146 	if (refcnt == 0)
147 		pool_put(&kauth_cred_pool, cred);
148 }
149 
150 void
151 kauth_cred_clone(kauth_cred_t from, kauth_cred_t to)
152 {
153 	KASSERT(from != NULL);
154 	KASSERT(to != NULL);
155 	KASSERT(from->cr_refcnt > 0);
156 
157 	to->cr_uid = from->cr_uid;
158 	to->cr_euid = from->cr_euid;
159 	to->cr_svuid = from->cr_svuid;
160 	to->cr_gid = from->cr_gid;
161 	to->cr_egid = from->cr_egid;
162 	to->cr_svgid = from->cr_svgid;
163 	to->cr_ngroups = from->cr_ngroups;
164 	memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups));
165 }
166 
167 /*
168  * Duplicate cred and return a new kauth_cred_t.
169  */
170 kauth_cred_t
171 kauth_cred_dup(kauth_cred_t cred)
172 {
173 	kauth_cred_t new_cred;
174 
175 	KASSERT(cred != NULL);
176 	KASSERT(cred->cr_refcnt > 0);
177 
178 	new_cred = kauth_cred_alloc();
179 
180 	kauth_cred_clone(cred, new_cred);
181 
182 	return (new_cred);
183 }
184 
185 /*
186  * Similar to crcopy(), only on a kauth_cred_t.
187  * XXX: Is this even needed? [kauth_cred_copy]
188  */
189 kauth_cred_t
190 kauth_cred_copy(kauth_cred_t cred)
191 {
192 	kauth_cred_t new_cred;
193 
194 	KASSERT(cred != NULL);
195 	KASSERT(cred->cr_refcnt > 0);
196 
197 	/* If the provided credentials already have one reference, use them. */
198 	if (cred->cr_refcnt == 1)
199 		return (cred);
200 
201 	new_cred = kauth_cred_alloc();
202 
203 	kauth_cred_clone(cred, new_cred);
204 
205 	kauth_cred_free(cred);
206 
207 	return (new_cred);
208 }
209 
210 uid_t
211 kauth_cred_getuid(kauth_cred_t cred)
212 {
213 	KASSERT(cred != NULL);
214 
215 	return (cred->cr_uid);
216 }
217 
218 uid_t
219 kauth_cred_geteuid(kauth_cred_t cred)
220 {
221 	KASSERT(cred != NULL);
222 
223 	return (cred->cr_euid);
224 }
225 
226 uid_t
227 kauth_cred_getsvuid(kauth_cred_t cred)
228 {
229 	KASSERT(cred != NULL);
230 
231 	return (cred->cr_svuid);
232 }
233 
234 gid_t
235 kauth_cred_getgid(kauth_cred_t cred)
236 {
237 	KASSERT(cred != NULL);
238 
239 	return (cred->cr_gid);
240 }
241 
242 gid_t
243 kauth_cred_getegid(kauth_cred_t cred)
244 {
245 	KASSERT(cred != NULL);
246 
247 	return (cred->cr_egid);
248 }
249 
250 gid_t
251 kauth_cred_getsvgid(kauth_cred_t cred)
252 {
253 	KASSERT(cred != NULL);
254 
255 	return (cred->cr_svgid);
256 }
257 
258 void
259 kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
260 {
261 	KASSERT(cred != NULL);
262 	KASSERT(cred->cr_refcnt == 1);
263 
264 	cred->cr_uid = uid;
265 }
266 
267 void
268 kauth_cred_seteuid(kauth_cred_t cred, uid_t uid)
269 {
270 	KASSERT(cred != NULL);
271 	KASSERT(cred->cr_refcnt == 1);
272 
273 	cred->cr_euid = uid;
274 }
275 
276 void
277 kauth_cred_setsvuid(kauth_cred_t cred, uid_t uid)
278 {
279 	KASSERT(cred != NULL);
280 	KASSERT(cred->cr_refcnt == 1);
281 
282 	cred->cr_svuid = uid;
283 }
284 
285 void
286 kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
287 {
288 	KASSERT(cred != NULL);
289 	KASSERT(cred->cr_refcnt == 1);
290 
291 	cred->cr_gid = gid;
292 }
293 
294 void
295 kauth_cred_setegid(kauth_cred_t cred, gid_t gid)
296 {
297 	KASSERT(cred != NULL);
298 	KASSERT(cred->cr_refcnt == 1);
299 
300 	cred->cr_egid = gid;
301 }
302 
303 void
304 kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid)
305 {
306 	KASSERT(cred != NULL);
307 	KASSERT(cred->cr_refcnt == 1);
308 
309 	cred->cr_svgid = gid;
310 }
311 
312 /* Checks if gid is a member of the groups in cred. */
313 int
314 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
315 {
316 	int i;
317 
318 	KASSERT(cred != NULL);
319 	KASSERT(resultp != NULL);
320 
321 	*resultp = 0;
322 
323 	for (i = 0; i < cred->cr_ngroups; i++)
324 		if (cred->cr_groups[i] == gid) {
325 			*resultp = 1;
326 			break;
327 		}
328 
329 	return (0);
330 }
331 
332 u_int
333 kauth_cred_ngroups(kauth_cred_t cred)
334 {
335 	KASSERT(cred != NULL);
336 
337 	return (cred->cr_ngroups);
338 }
339 
340 /*
341  * Return the group at index idx from the groups in cred.
342  */
343 gid_t
344 kauth_cred_group(kauth_cred_t cred, u_int idx)
345 {
346 	KASSERT(cred != NULL);
347 	KASSERT(idx < cred->cr_ngroups);
348 
349 	return (cred->cr_groups[idx]);
350 }
351 
352 /* XXX elad: gmuid is unused for now. */
353 int
354 kauth_cred_setgroups(kauth_cred_t cred, gid_t *grbuf, size_t len, uid_t gmuid)
355 {
356 	KASSERT(cred != NULL);
357 	KASSERT(cred->cr_refcnt == 1);
358 	KASSERT(len <= sizeof(cred->cr_groups) / sizeof(cred->cr_groups[0]));
359 
360 	if (len)
361 		memcpy(cred->cr_groups, grbuf, len * sizeof(cred->cr_groups[0]));
362 	memset(cred->cr_groups + len, 0xff,
363 	    sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0])));
364 
365 	cred->cr_ngroups = len;
366 
367 	return (0);
368 }
369 
370 int
371 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len)
372 {
373 	KASSERT(cred != NULL);
374 	KASSERT(len <= cred->cr_ngroups);
375 
376 	memset(grbuf, 0xff, sizeof(*grbuf) * len);
377 	memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len);
378 
379 	return (0);
380 }
381 
382 /*
383  * Match uids in two credentials.
384  */
385 int
386 kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2)
387 {
388 	KASSERT(cred1 != NULL);
389 	KASSERT(cred2 != NULL);
390 
391 	if (cred1->cr_uid == cred2->cr_uid ||
392 	    cred1->cr_euid == cred2->cr_uid ||
393 	    cred1->cr_uid == cred2->cr_euid ||
394 	    cred1->cr_euid == cred2->cr_euid)
395 		return (1);
396 
397 	return (0);
398 }
399 
400 u_int
401 kauth_cred_getrefcnt(kauth_cred_t cred)
402 {
403 	KASSERT(cred != NULL);
404 
405 	return (cred->cr_refcnt);
406 }
407 
408 /*
409  * Convert userland credentials (struct uucred) to kauth_cred_t.
410  * XXX: For NFS code.
411  */
412 void
413 kauth_cred_uucvt(kauth_cred_t cred, const struct uucred *uuc)
414 {
415 	KASSERT(cred != NULL);
416 	KASSERT(uuc != NULL);
417 
418 	cred->cr_refcnt = 1;
419 	cred->cr_uid = uuc->cr_uid;
420 	cred->cr_euid = uuc->cr_uid;
421 	cred->cr_svuid = uuc->cr_uid;
422 	cred->cr_gid = uuc->cr_gid;
423 	cred->cr_egid = uuc->cr_gid;
424 	cred->cr_svgid = uuc->cr_gid;
425 	cred->cr_ngroups = min(uuc->cr_ngroups, NGROUPS);
426 	kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
427 	    cred->cr_ngroups, -1);
428 }
429 
430 /*
431  * Compare kauth_cred_t and uucred credentials.
432  * XXX: Modelled after crcmp() for NFS.
433  */
434 int
435 kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
436 {
437 	KASSERT(cred != NULL);
438 	KASSERT(uuc != NULL);
439 
440 	if (cred->cr_euid == uuc->cr_uid &&
441 	    cred->cr_egid == uuc->cr_gid &&
442 	    cred->cr_ngroups == uuc->cr_ngroups) {
443 		int i;
444 
445 		/* Check if all groups from uuc appear in cred. */
446 		for (i = 0; i < uuc->cr_ngroups; i++) {
447 			int ismember;
448 
449 			ismember = 0;
450 			if (kauth_cred_ismember_gid(cred, uuc->cr_groups[i],
451 			    &ismember) != 0 || !ismember)
452 				return (1);
453 		}
454 
455 		return (0);
456 	}
457 
458 	return (1);
459 }
460 
461 /*
462  * Make a struct ucred out of a kauth_cred_t.  For compatibility.
463  */
464 void
465 kauth_cred_toucred(kauth_cred_t cred, struct ucred *uc)
466 {
467 	KASSERT(cred != NULL);
468 	KASSERT(uc != NULL);
469 
470 	uc->cr_ref = cred->cr_refcnt;
471 	uc->cr_uid = cred->cr_euid;
472 	uc->cr_gid = cred->cr_egid;
473 	uc->cr_ngroups = min(cred->cr_ngroups,
474 			     sizeof(uc->cr_groups) / sizeof(uc->cr_groups[0]));
475 	memcpy(uc->cr_groups, cred->cr_groups,
476 	       uc->cr_ngroups * sizeof(uc->cr_groups[0]));
477 }
478 
479 /*
480  * Make a struct pcred out of a kauth_cred_t.  For compatibility.
481  */
482 void
483 kauth_cred_topcred(kauth_cred_t cred, struct pcred *pc)
484 {
485 	KASSERT(cred != NULL);
486 	KASSERT(pc != NULL);
487 
488 	pc->pc_ucred = NULL;
489 	pc->p_ruid = cred->cr_uid;
490 	pc->p_svuid = cred->cr_svuid;
491 	pc->p_rgid = cred->cr_gid;
492 	pc->p_svgid = cred->cr_svgid;
493 	pc->p_refcnt = cred->cr_refcnt;
494 }
495 
496 /*
497  * Return kauth_cred_t for the current LWP.
498  */
499 kauth_cred_t
500 kauth_cred_get(void)
501 {
502 	return (curlwp->l_cred);
503 }
504 
505 /*
506  * Returns a scope matching the provided id.
507  * Requires the scope list lock to be held by the caller.
508  */
509 static kauth_scope_t
510 kauth_ifindscope(const char *id)
511 {
512 	kauth_scope_t scope;
513 
514 	/* XXX: assert lock on scope list? */
515 
516 	scope = NULL;
517 	SIMPLEQ_FOREACH(scope, &scope_list, next_scope) {
518 		if (strcmp(scope->id, id) == 0)
519 			break;
520 	}
521 
522 	return (scope);
523 }
524 
525 /*
526  * Register a new scope.
527  *
528  * id - identifier for the scope
529  * callback - the scope's default listener
530  * cookie - cookie to be passed to the listener(s)
531  */
532 kauth_scope_t
533 kauth_register_scope(const char *id, kauth_scope_callback_t callback,
534     void *cookie)
535 {
536 	kauth_scope_t scope;
537 	kauth_listener_t listener;
538 
539 	/* Sanitize input */
540 	if (id == NULL)
541 		return (NULL);
542 
543 	/* Allocate space for a new scope and listener. */
544 	scope = pool_get(&kauth_scope_pool, PR_WAITOK);
545 	listener = pool_get(&kauth_listener_pool, PR_WAITOK);
546 
547 	/* Acquire scope list lock. */
548 	simple_lock(&scopes_lock);
549 
550 	/* Check we don't already have a scope with the same id */
551 	if (kauth_ifindscope(id) != NULL) {
552 		simple_unlock(&scopes_lock);
553 
554 		pool_put(&kauth_scope_pool, scope);
555 		pool_put(&kauth_listener_pool, listener);
556 
557 		return (NULL);
558 	}
559 
560 	/* Initialize new scope with parameters */
561 	scope->id = id;
562 	scope->cookie = cookie;
563 	scope->nlisteners = 1;
564 
565 	SIMPLEQ_INIT(&scope->listenq);
566 
567 	/* Add default listener */
568 	if (callback != NULL) {
569 		listener->func = callback;
570 		listener->scope = scope;
571 		listener->refcnt = 0;
572 		SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next);
573 	}
574 
575 	/* Insert scope to scopes list */
576 	SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope);
577 
578 	simple_unlock(&scopes_lock);
579 
580 	return (scope);
581 }
582 
583 /*
584  * Initialize the kernel authorization subsystem.
585  *
586  * Initialize the scopes list lock.
587  * Register built-in scopes: generic, process.
588  */
589 void
590 kauth_init(void)
591 {
592 	SIMPLEQ_INIT(&scope_list);
593 	simple_lock_init(&scopes_lock);
594 
595 	/* Register generic scope. */
596 	kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC,
597 	    NULL, NULL);
598 
599 	/* Register system scope. */
600 	kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM,
601 	    NULL, NULL);
602 
603 	/* Register process scope. */
604 	kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS,
605 	    NULL, NULL);
606 
607 	/* Register network scope. */
608 	kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK,
609 	    NULL, NULL);
610 
611 	/* Register machdep scope. */
612 	kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP,
613 	    NULL, NULL);
614 }
615 
616 /*
617  * Deregister a scope.
618  * Requires scope list lock to be held by the caller.
619  *
620  * scope - the scope to deregister
621  */
622 void
623 kauth_deregister_scope(kauth_scope_t scope)
624 {
625 	if (scope != NULL) {
626 		/* Remove scope from list */
627 		SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope);
628 	}
629 }
630 
631 /*
632  * Register a listener.
633  *
634  * id - scope identifier.
635  * callback - the callback routine for the listener.
636  * cookie - cookie to pass unmoidfied to the callback.
637  */
638 kauth_listener_t
639 kauth_listen_scope(const char *id, kauth_scope_callback_t callback,
640 		   void *cookie)
641 {
642 	kauth_scope_t scope;
643 	kauth_listener_t listener;
644 
645 	/* Find scope struct */
646 	simple_lock(&scopes_lock);
647 	scope = kauth_ifindscope(id);
648 	simple_unlock(&scopes_lock);
649 	if (scope == NULL)
650 		return (NULL);
651 
652 	/* Allocate listener */
653 	listener = pool_get(&kauth_listener_pool, PR_WAITOK);
654 
655 	/* Initialize listener with parameters */
656 	listener->func = callback;
657 	listener->refcnt = 0;
658 
659 	/* Add listener to scope */
660 	SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next);
661 
662 	/* Raise number of listeners on scope. */
663 	scope->nlisteners++;
664 	listener->scope = scope;
665 
666 	return (listener);
667 }
668 
669 /*
670  * Deregister a listener.
671  *
672  * listener - listener reference as returned from kauth_listen_scope().
673  */
674 void
675 kauth_unlisten_scope(kauth_listener_t listener)
676 {
677 	if (listener != NULL) {
678 		SIMPLEQ_REMOVE(&listener->scope->listenq, listener,
679 		    kauth_listener, listener_next);
680 		listener->scope->nlisteners--;
681 	}
682 }
683 
684 /*
685  * Authorize a request.
686  *
687  * scope - the scope of the request as defined by KAUTH_SCOPE_* or as
688  *	   returned from kauth_register_scope().
689  * credential - credentials of the user ("actor") making the request.
690  * action - request identifier.
691  * arg[0-3] - passed unmodified to listener(s).
692  */
693 int
694 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred,
695 		       kauth_action_t action, void *arg0, void *arg1,
696 		       void *arg2, void *arg3)
697 {
698 	kauth_listener_t listener;
699 	int error, allow, fail;
700 
701 #if 0 /* defined(LOCKDEBUG) */
702 	spinlock_switchcheck();
703 	simple_lock_only_held(NULL, "kauth_authorize_action");
704 #endif
705 
706 	/* Sanitize input */
707 	if (scope == NULL || cred == NULL)
708 		return (EFAULT);
709 	if (!action)
710 		return (EINVAL);
711 
712 	/* Short-circuit requests coming from the kernel. */
713 	if (cred == NOCRED || cred == FSCRED)
714 		return (0);
715 
716 	/* Short-circuit requests when there are no listeners. */
717 	if (SIMPLEQ_EMPTY(&scope->listenq))
718 		return (0);
719 
720 	fail = 0;
721 	allow = 0;
722 	SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) {
723 		error = listener->func(cred, action, scope->cookie, arg0,
724 				       arg1, arg2, arg3);
725 
726 		if (error == KAUTH_RESULT_ALLOW)
727 			allow = 1;
728 		else if (error == KAUTH_RESULT_DENY)
729 			fail = 1;
730 	}
731 
732 	return ((allow && !fail) ? 0 : EPERM);
733 };
734 
735 /*
736  * Generic scope authorization wrapper.
737  */
738 int
739 kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0)
740 {
741 	return (kauth_authorize_action(kauth_builtin_scope_generic, cred,
742 	    action, arg0, NULL, NULL, NULL));
743 }
744 
745 /*
746  * System scope authorization wrapper.
747  */
748 int
749 kauth_authorize_system(kauth_cred_t cred, kauth_action_t action,
750     enum kauth_system_req req, void *arg1, void *arg2, void *arg3)
751 {
752 	return (kauth_authorize_action(kauth_builtin_scope_system, cred,
753 	    action, (void *)req, arg1, arg2, arg3));
754 }
755 
756 /*
757  * Process scope authorization wrapper.
758  */
759 int
760 kauth_authorize_process(kauth_cred_t cred, kauth_action_t action,
761     struct proc *p, void *arg1, void *arg2, void *arg3)
762 {
763 	return (kauth_authorize_action(kauth_builtin_scope_process, cred,
764 	    action, p, arg1, arg2, arg3));
765 }
766 
767 /*
768  * Network scope authorization wrapper.
769  */
770 int
771 kauth_authorize_network(kauth_cred_t cred, kauth_action_t action,
772     void *arg0, void *arg1, void *arg2, void *arg3)
773 {
774 	return (kauth_authorize_action(kauth_builtin_scope_network, cred,
775 	    action, arg0, arg1, arg2, arg3));
776 }
777 
778 int
779 kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action,
780     void *arg0, void *arg1, void *arg2, void *arg3)
781 {
782 	return (kauth_authorize_action(kauth_builtin_scope_machdep, cred,
783 	    action, arg0, arg1, arg2, arg3));
784 }
785