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