xref: /netbsd-src/sys/kern/kern_auth.c (revision de2138164cd16145ee5fd4d0aef1e8f952c1a9fb)
1 /* $NetBSD: kern_auth.c,v 1.46 2007/02/24 20:41:33 christos 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.46 2007/02/24 20:41:33 christos 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);
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 void
168 kauth_cred_clone(kauth_cred_t from, kauth_cred_t to)
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 	to->cr_ngroups = from->cr_ngroups;
181 	memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups));
182 
183 	kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL);
184 }
185 
186 /*
187  * Duplicate cred and return a new kauth_cred_t.
188  */
189 kauth_cred_t
190 kauth_cred_dup(kauth_cred_t cred)
191 {
192 	kauth_cred_t new_cred;
193 
194 	KASSERT(cred != NULL);
195 	KASSERT(cred->cr_refcnt > 0);
196 
197 	new_cred = kauth_cred_alloc();
198 
199 	kauth_cred_clone(cred, new_cred);
200 
201 	return (new_cred);
202 }
203 
204 /*
205  * Similar to crcopy(), only on a kauth_cred_t.
206  * XXX: Is this even needed? [kauth_cred_copy]
207  */
208 kauth_cred_t
209 kauth_cred_copy(kauth_cred_t cred)
210 {
211 	kauth_cred_t new_cred;
212 
213 	KASSERT(cred != NULL);
214 	KASSERT(cred->cr_refcnt > 0);
215 
216 	/* If the provided credentials already have one reference, use them. */
217 	if (cred->cr_refcnt == 1)
218 		return (cred);
219 
220 	new_cred = kauth_cred_alloc();
221 
222 	kauth_cred_clone(cred, new_cred);
223 
224 	kauth_cred_free(cred);
225 
226 	return (new_cred);
227 }
228 
229 void
230 kauth_proc_fork(struct proc *parent, struct proc *child)
231 {
232 
233 	mutex_enter(&parent->p_mutex);
234 	kauth_cred_hold(parent->p_cred);
235 	child->p_cred = parent->p_cred;
236 	mutex_exit(&parent->p_mutex);
237 
238 	/* XXX: relies on parent process stalling during fork() */
239 	kauth_cred_hook(parent->p_cred, KAUTH_CRED_FORK, parent,
240 	    child);
241 }
242 
243 uid_t
244 kauth_cred_getuid(kauth_cred_t cred)
245 {
246 	KASSERT(cred != NULL);
247 
248 	return (cred->cr_uid);
249 }
250 
251 uid_t
252 kauth_cred_geteuid(kauth_cred_t cred)
253 {
254 	KASSERT(cred != NULL);
255 
256 	return (cred->cr_euid);
257 }
258 
259 uid_t
260 kauth_cred_getsvuid(kauth_cred_t cred)
261 {
262 	KASSERT(cred != NULL);
263 
264 	return (cred->cr_svuid);
265 }
266 
267 gid_t
268 kauth_cred_getgid(kauth_cred_t cred)
269 {
270 	KASSERT(cred != NULL);
271 
272 	return (cred->cr_gid);
273 }
274 
275 gid_t
276 kauth_cred_getegid(kauth_cred_t cred)
277 {
278 	KASSERT(cred != NULL);
279 
280 	return (cred->cr_egid);
281 }
282 
283 gid_t
284 kauth_cred_getsvgid(kauth_cred_t cred)
285 {
286 	KASSERT(cred != NULL);
287 
288 	return (cred->cr_svgid);
289 }
290 
291 void
292 kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
293 {
294 	KASSERT(cred != NULL);
295 	KASSERT(cred->cr_refcnt == 1);
296 
297 	cred->cr_uid = uid;
298 }
299 
300 void
301 kauth_cred_seteuid(kauth_cred_t cred, uid_t uid)
302 {
303 	KASSERT(cred != NULL);
304 	KASSERT(cred->cr_refcnt == 1);
305 
306 	cred->cr_euid = uid;
307 }
308 
309 void
310 kauth_cred_setsvuid(kauth_cred_t cred, uid_t uid)
311 {
312 	KASSERT(cred != NULL);
313 	KASSERT(cred->cr_refcnt == 1);
314 
315 	cred->cr_svuid = uid;
316 }
317 
318 void
319 kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
320 {
321 	KASSERT(cred != NULL);
322 	KASSERT(cred->cr_refcnt == 1);
323 
324 	cred->cr_gid = gid;
325 }
326 
327 void
328 kauth_cred_setegid(kauth_cred_t cred, gid_t gid)
329 {
330 	KASSERT(cred != NULL);
331 	KASSERT(cred->cr_refcnt == 1);
332 
333 	cred->cr_egid = gid;
334 }
335 
336 void
337 kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid)
338 {
339 	KASSERT(cred != NULL);
340 	KASSERT(cred->cr_refcnt == 1);
341 
342 	cred->cr_svgid = gid;
343 }
344 
345 /* Checks if gid is a member of the groups in cred. */
346 int
347 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
348 {
349 	int i;
350 
351 	KASSERT(cred != NULL);
352 	KASSERT(resultp != NULL);
353 
354 	*resultp = 0;
355 
356 	for (i = 0; i < cred->cr_ngroups; i++)
357 		if (cred->cr_groups[i] == gid) {
358 			*resultp = 1;
359 			break;
360 		}
361 
362 	return (0);
363 }
364 
365 u_int
366 kauth_cred_ngroups(kauth_cred_t cred)
367 {
368 	KASSERT(cred != NULL);
369 
370 	return (cred->cr_ngroups);
371 }
372 
373 /*
374  * Return the group at index idx from the groups in cred.
375  */
376 gid_t
377 kauth_cred_group(kauth_cred_t cred, u_int idx)
378 {
379 	KASSERT(cred != NULL);
380 	KASSERT(idx < cred->cr_ngroups);
381 
382 	return (cred->cr_groups[idx]);
383 }
384 
385 /* XXX elad: gmuid is unused for now. */
386 int
387 kauth_cred_setgroups(kauth_cred_t cred, gid_t *grbuf, size_t len, uid_t gmuid)
388 {
389 	KASSERT(cred != NULL);
390 	KASSERT(cred->cr_refcnt == 1);
391 	KASSERT(len <= sizeof(cred->cr_groups) / sizeof(cred->cr_groups[0]));
392 
393 	if (len)
394 		memcpy(cred->cr_groups, grbuf, len * sizeof(cred->cr_groups[0]));
395 	memset(cred->cr_groups + len, 0xff,
396 	    sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0])));
397 
398 	cred->cr_ngroups = len;
399 
400 	return (0);
401 }
402 
403 int
404 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len)
405 {
406 	KASSERT(cred != NULL);
407 	KASSERT(len <= cred->cr_ngroups);
408 
409 	memset(grbuf, 0xff, sizeof(*grbuf) * len);
410 	memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len);
411 
412 	return (0);
413 }
414 
415 int
416 kauth_register_key(const char *secmodel, kauth_key_t *result)
417 {
418 	kauth_key_t k;
419 	specificdata_key_t key;
420 	int error;
421 
422 	KASSERT(result != NULL);
423 
424 	error = specificdata_key_create(kauth_domain, &key, NULL);
425 	if (error)
426 		return (error);
427 
428 	k = kmem_alloc(sizeof(*k), KM_SLEEP);
429 	k->ks_secmodel = secmodel;
430 	k->ks_key = key;
431 
432 	*result = k;
433 
434 	return (0);
435 }
436 
437 int
438 kauth_deregister_key(kauth_key_t key)
439 {
440 	KASSERT(key != NULL);
441 
442 	specificdata_key_delete(kauth_domain, key->ks_key);
443 	kmem_free(key, sizeof(*key));
444 
445 	return (0);
446 }
447 
448 void *
449 kauth_cred_getdata(kauth_cred_t cred, kauth_key_t key)
450 {
451 	KASSERT(cred != NULL);
452 	KASSERT(key != NULL);
453 
454 	return (specificdata_getspecific(kauth_domain, &cred->cr_sd,
455 	    key->ks_key));
456 }
457 
458 void
459 kauth_cred_setdata(kauth_cred_t cred, kauth_key_t key, void *data)
460 {
461 	KASSERT(cred != NULL);
462 	KASSERT(key != NULL);
463 
464 	specificdata_setspecific(kauth_domain, &cred->cr_sd, key->ks_key, data);
465 }
466 
467 /*
468  * Match uids in two credentials.
469  */
470 int
471 kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2)
472 {
473 	KASSERT(cred1 != NULL);
474 	KASSERT(cred2 != NULL);
475 
476 	if (cred1->cr_uid == cred2->cr_uid ||
477 	    cred1->cr_euid == cred2->cr_uid ||
478 	    cred1->cr_uid == cred2->cr_euid ||
479 	    cred1->cr_euid == cred2->cr_euid)
480 		return (1);
481 
482 	return (0);
483 }
484 
485 u_int
486 kauth_cred_getrefcnt(kauth_cred_t cred)
487 {
488 	KASSERT(cred != NULL);
489 
490 	return (cred->cr_refcnt);
491 }
492 
493 /*
494  * Convert userland credentials (struct uucred) to kauth_cred_t.
495  * XXX: For NFS & puffs
496  */
497 void
498 kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc)
499 {
500 	KASSERT(cred != NULL);
501 	KASSERT(uuc != NULL);
502 
503 	cred->cr_refcnt = 1;
504 	cred->cr_uid = uuc->cr_uid;
505 	cred->cr_euid = uuc->cr_uid;
506 	cred->cr_svuid = uuc->cr_uid;
507 	cred->cr_gid = uuc->cr_gid;
508 	cred->cr_egid = uuc->cr_gid;
509 	cred->cr_svgid = uuc->cr_gid;
510 	cred->cr_ngroups = min(uuc->cr_ngroups, NGROUPS);
511 	kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
512 	    cred->cr_ngroups, -1);
513 }
514 
515 /*
516  * Convert kauth_cred_t to userland credentials (struct uucred).
517  * XXX: For NFS & puffs
518  */
519 void
520 kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred)
521 {
522 	KASSERT(cred != NULL);
523 	KASSERT(uuc != NULL);
524 	int ng;
525 
526 	ng = min(cred->cr_ngroups, NGROUPS);
527 	uuc->cr_uid = cred->cr_euid;
528 	uuc->cr_gid = cred->cr_egid;
529 	uuc->cr_ngroups = ng;
530 	kauth_cred_getgroups(cred, uuc->cr_groups, ng);
531 }
532 
533 /*
534  * Compare kauth_cred_t and uucred credentials.
535  * XXX: Modelled after crcmp() for NFS.
536  */
537 int
538 kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
539 {
540 	KASSERT(cred != NULL);
541 	KASSERT(uuc != NULL);
542 
543 	if (cred->cr_euid == uuc->cr_uid &&
544 	    cred->cr_egid == uuc->cr_gid &&
545 	    cred->cr_ngroups == uuc->cr_ngroups) {
546 		int i;
547 
548 		/* Check if all groups from uuc appear in cred. */
549 		for (i = 0; i < uuc->cr_ngroups; i++) {
550 			int ismember;
551 
552 			ismember = 0;
553 			if (kauth_cred_ismember_gid(cred, uuc->cr_groups[i],
554 			    &ismember) != 0 || !ismember)
555 				return (1);
556 		}
557 
558 		return (0);
559 	}
560 
561 	return (1);
562 }
563 
564 /*
565  * Make a struct ucred out of a kauth_cred_t.  For compatibility.
566  */
567 void
568 kauth_cred_toucred(kauth_cred_t cred, struct ki_ucred *uc)
569 {
570 	KASSERT(cred != NULL);
571 	KASSERT(uc != NULL);
572 
573 	uc->cr_ref = cred->cr_refcnt;
574 	uc->cr_uid = cred->cr_euid;
575 	uc->cr_gid = cred->cr_egid;
576 	uc->cr_ngroups = min(cred->cr_ngroups,
577 			     sizeof(uc->cr_groups) / sizeof(uc->cr_groups[0]));
578 	memcpy(uc->cr_groups, cred->cr_groups,
579 	       uc->cr_ngroups * sizeof(uc->cr_groups[0]));
580 }
581 
582 /*
583  * Make a struct pcred out of a kauth_cred_t.  For compatibility.
584  */
585 void
586 kauth_cred_topcred(kauth_cred_t cred, struct ki_pcred *pc)
587 {
588 	KASSERT(cred != NULL);
589 	KASSERT(pc != NULL);
590 
591 	pc->p_pad = NULL;
592 	pc->p_ruid = cred->cr_uid;
593 	pc->p_svuid = cred->cr_svuid;
594 	pc->p_rgid = cred->cr_gid;
595 	pc->p_svgid = cred->cr_svgid;
596 	pc->p_refcnt = cred->cr_refcnt;
597 }
598 
599 /*
600  * Return kauth_cred_t for the current LWP.
601  */
602 kauth_cred_t
603 kauth_cred_get(void)
604 {
605 	return (curlwp->l_cred);
606 }
607 
608 /*
609  * Returns a scope matching the provided id.
610  * Requires the scope list lock to be held by the caller.
611  */
612 static kauth_scope_t
613 kauth_ifindscope(const char *id)
614 {
615 	kauth_scope_t scope;
616 
617 	KASSERT(rw_lock_held(&kauth_lock));
618 
619 	scope = NULL;
620 	SIMPLEQ_FOREACH(scope, &scope_list, next_scope) {
621 		if (strcmp(scope->id, id) == 0)
622 			break;
623 	}
624 
625 	return (scope);
626 }
627 
628 /*
629  * Register a new scope.
630  *
631  * id - identifier for the scope
632  * callback - the scope's default listener
633  * cookie - cookie to be passed to the listener(s)
634  */
635 kauth_scope_t
636 kauth_register_scope(const char *id, kauth_scope_callback_t callback,
637     void *cookie)
638 {
639 	kauth_scope_t scope;
640 	kauth_listener_t listener = NULL; /* XXX gcc */
641 
642 	/* Sanitize input */
643 	if (id == NULL)
644 		return (NULL);
645 
646 	/* Allocate space for a new scope and listener. */
647 	scope = kmem_alloc(sizeof(*scope), KM_SLEEP);
648 	if (scope == NULL)
649 		return NULL;
650 	if (callback != NULL) {
651 		listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
652 		if (listener == NULL) {
653 			kmem_free(scope, sizeof(*scope));
654 			return (NULL);
655 		}
656 	}
657 
658 	/*
659 	 * Acquire scope list lock.
660 	 */
661 	rw_enter(&kauth_lock, RW_WRITER);
662 
663 	/* Check we don't already have a scope with the same id */
664 	if (kauth_ifindscope(id) != NULL) {
665 		rw_exit(&kauth_lock);
666 
667 		kmem_free(scope, sizeof(*scope));
668 		if (callback != NULL)
669 			kmem_free(listener, sizeof(*listener));
670 
671 		return (NULL);
672 	}
673 
674 	/* Initialize new scope with parameters */
675 	scope->id = id;
676 	scope->cookie = cookie;
677 	scope->nlisteners = 1;
678 
679 	SIMPLEQ_INIT(&scope->listenq);
680 
681 	/* Add default listener */
682 	if (callback != NULL) {
683 		listener->func = callback;
684 		listener->scope = scope;
685 		listener->refcnt = 0;
686 		SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next);
687 	}
688 
689 	/* Insert scope to scopes list */
690 	SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope);
691 
692 	rw_exit(&kauth_lock);
693 
694 	return (scope);
695 }
696 
697 /*
698  * Initialize the kernel authorization subsystem.
699  *
700  * Initialize the scopes list lock.
701  * Create specificdata domain.
702  * Register the credentials scope, used in kauth(9) internally.
703  * Register built-in scopes: generic, system, process, network, machdep, device.
704  */
705 void
706 kauth_init(void)
707 {
708 	SIMPLEQ_INIT(&scope_list);
709 	rw_init(&kauth_lock);
710 
711 	/* Create specificdata domain. */
712 	kauth_domain = specificdata_domain_create();
713 
714 	/* Register credentials scope. */
715 	kauth_builtin_scope_cred =
716 	    kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL);
717 
718 	/* Register generic scope. */
719 	kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC,
720 	    NULL, NULL);
721 
722 	/* Register system scope. */
723 	kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM,
724 	    NULL, NULL);
725 
726 	/* Register process scope. */
727 	kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS,
728 	    NULL, NULL);
729 
730 	/* Register network scope. */
731 	kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK,
732 	    NULL, NULL);
733 
734 	/* Register machdep scope. */
735 	kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP,
736 	    NULL, NULL);
737 
738 	/* Register device scope. */
739 	kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE,
740 	    NULL, NULL);
741 }
742 
743 /*
744  * Deregister a scope.
745  * Requires scope list lock to be held by the caller.
746  *
747  * scope - the scope to deregister
748  */
749 void
750 kauth_deregister_scope(kauth_scope_t scope)
751 {
752 	if (scope != NULL) {
753 		/* Remove scope from list */
754 		SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope);
755 		kmem_free(scope, sizeof(*scope));
756 	}
757 }
758 
759 /*
760  * Register a listener.
761  *
762  * id - scope identifier.
763  * callback - the callback routine for the listener.
764  * cookie - cookie to pass unmoidfied to the callback.
765  */
766 kauth_listener_t
767 kauth_listen_scope(const char *id, kauth_scope_callback_t callback,
768    void *cookie)
769 {
770 	kauth_scope_t scope;
771 	kauth_listener_t listener;
772 
773 	listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
774 	if (listener == NULL)
775 		return (NULL);
776 
777 	rw_enter(&kauth_lock, RW_WRITER);
778 
779 	/*
780 	 * Find scope struct.
781 	 */
782 	scope = kauth_ifindscope(id);
783 	if (scope == NULL) {
784 		rw_exit(&kauth_lock);
785 		kmem_free(listener, sizeof(*listener));
786 		return (NULL);
787 	}
788 
789 	/* Allocate listener */
790 
791 	/* Initialize listener with parameters */
792 	listener->func = callback;
793 	listener->refcnt = 0;
794 
795 	/* Add listener to scope */
796 	SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next);
797 
798 	/* Raise number of listeners on scope. */
799 	scope->nlisteners++;
800 	listener->scope = scope;
801 
802 	rw_exit(&kauth_lock);
803 
804 	return (listener);
805 }
806 
807 /*
808  * Deregister a listener.
809  *
810  * listener - listener reference as returned from kauth_listen_scope().
811  */
812 void
813 kauth_unlisten_scope(kauth_listener_t listener)
814 {
815 
816 	if (listener != NULL) {
817 		rw_enter(&kauth_lock, RW_WRITER);
818 		SIMPLEQ_REMOVE(&listener->scope->listenq, listener,
819 		    kauth_listener, listener_next);
820 		listener->scope->nlisteners--;
821 		rw_exit(&kauth_lock);
822 		kmem_free(listener, sizeof(*listener));
823 	}
824 }
825 
826 /*
827  * Authorize a request.
828  *
829  * scope - the scope of the request as defined by KAUTH_SCOPE_* or as
830  *	   returned from kauth_register_scope().
831  * credential - credentials of the user ("actor") making the request.
832  * action - request identifier.
833  * arg[0-3] - passed unmodified to listener(s).
834  */
835 int
836 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred,
837 		       kauth_action_t action, void *arg0, void *arg1,
838 		       void *arg2, void *arg3)
839 {
840 	kauth_listener_t listener;
841 	int error, allow, fail;
842 
843 #if 0 /* defined(LOCKDEBUG) */
844 	spinlock_switchcheck();
845 	simple_lock_only_held(NULL, "kauth_authorize_action");
846 #endif
847 
848 	KASSERT(cred != NULL);
849 	KASSERT(action != 0);
850 
851 	/* Short-circuit requests coming from the kernel. */
852 	if (cred == NOCRED || cred == FSCRED)
853 		return (0);
854 
855 	KASSERT(scope != NULL);
856 
857 	fail = 0;
858 	allow = 0;
859 
860 	/* rw_enter(&kauth_lock, RW_READER); XXX not yet */
861 	SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) {
862 		error = listener->func(cred, action, scope->cookie, arg0,
863 		    arg1, arg2, arg3);
864 
865 		if (error == KAUTH_RESULT_ALLOW)
866 			allow = 1;
867 		else if (error == KAUTH_RESULT_DENY)
868 			fail = 1;
869 	}
870 	/* rw_exit(&kauth_lock); */
871 
872 	if (fail)
873 		return (EPERM);
874 
875 	if (allow)
876 		return (0);
877 
878 	if (!nsecmodels)
879 		return (0);
880 
881 	return (EPERM);
882 };
883 
884 /*
885  * Generic scope authorization wrapper.
886  */
887 int
888 kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0)
889 {
890 	return (kauth_authorize_action(kauth_builtin_scope_generic, cred,
891 	    action, arg0, NULL, NULL, NULL));
892 }
893 
894 /*
895  * System scope authorization wrapper.
896  */
897 int
898 kauth_authorize_system(kauth_cred_t cred, kauth_action_t action,
899     enum kauth_system_req req, void *arg1, void *arg2, void *arg3)
900 {
901 	return (kauth_authorize_action(kauth_builtin_scope_system, cred,
902 	    action, (void *)req, arg1, arg2, arg3));
903 }
904 
905 /*
906  * Process scope authorization wrapper.
907  */
908 int
909 kauth_authorize_process(kauth_cred_t cred, kauth_action_t action,
910     struct proc *p, void *arg1, void *arg2, void *arg3)
911 {
912 	return (kauth_authorize_action(kauth_builtin_scope_process, cred,
913 	    action, p, arg1, arg2, arg3));
914 }
915 
916 /*
917  * Network scope authorization wrapper.
918  */
919 int
920 kauth_authorize_network(kauth_cred_t cred, kauth_action_t action,
921     enum kauth_network_req req, void *arg1, void *arg2, void *arg3)
922 {
923 	return (kauth_authorize_action(kauth_builtin_scope_network, cred,
924 	    action, (void *)req, arg1, arg2, arg3));
925 }
926 
927 int
928 kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action,
929     void *arg0, void *arg1, void *arg2, void *arg3)
930 {
931 	return (kauth_authorize_action(kauth_builtin_scope_machdep, cred,
932 	    action, arg0, arg1, arg2, arg3));
933 }
934 
935 int
936 kauth_authorize_device(kauth_cred_t cred, kauth_action_t action,
937     void *arg0, void *arg1, void *arg2, void *arg3)
938 {
939 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
940 	    action, arg0, arg1, arg2, arg3));
941 }
942 
943 int
944 kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action,
945     struct tty *tty)
946 {
947 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
948 	    action, tty, NULL, NULL, NULL));
949 }
950 
951 int
952 kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req,
953     struct vnode *vp)
954 {
955 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
956 	    KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL));
957 }
958 
959 int
960 kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits,
961     void *data)
962 {
963 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
964 	    KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev,
965 	    data, NULL));
966 }
967 
968 static int
969 kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0,
970     void *arg1)
971 {
972 	int r;
973 
974 	r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action,
975 	    arg0, arg1, NULL, NULL);
976 
977 #ifdef DIAGNOSTIC
978 	if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq))
979 		KASSERT(r == 0);
980 #endif /* DIAGNOSTIC */
981 
982 	return (r);
983 }
984 
985 void
986 secmodel_register(void)
987 {
988 	KASSERT(nsecmodels + 1 != 0);
989 
990 	rw_enter(&kauth_lock, RW_WRITER);
991 	nsecmodels++;
992 	rw_exit(&kauth_lock);
993 }
994 
995 void
996 secmodel_deregister(void)
997 {
998 	KASSERT(nsecmodels != 0);
999 
1000 	rw_enter(&kauth_lock, RW_WRITER);
1001 	nsecmodels--;
1002 	rw_exit(&kauth_lock);
1003 }
1004