xref: /netbsd-src/sys/kern/kern_auth.c (revision ce099b40997c43048fb78bd578195f81d2456523)
1 /* $NetBSD: kern_auth.c,v 1.60 2008/04/28 20:24:02 martin 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.60 2008/04/28 20:24:02 martin 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 	int 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 > sizeof(cred->cr_groups) / sizeof(cred->cr_groups[0]))
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 == 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,
667 			     sizeof(uc->cr_groups) / sizeof(uc->cr_groups[0]));
668 	memcpy(uc->cr_groups, cred->cr_groups,
669 	       uc->cr_ngroups * sizeof(uc->cr_groups[0]));
670 }
671 
672 /*
673  * Make a struct pcred out of a kauth_cred_t.  For compatibility.
674  */
675 void
676 kauth_cred_topcred(kauth_cred_t cred, struct ki_pcred *pc)
677 {
678 	KASSERT(cred != NULL);
679 	KASSERT(pc != NULL);
680 
681 	pc->p_pad = NULL;
682 	pc->p_ruid = cred->cr_uid;
683 	pc->p_svuid = cred->cr_svuid;
684 	pc->p_rgid = cred->cr_gid;
685 	pc->p_svgid = cred->cr_svgid;
686 	pc->p_refcnt = cred->cr_refcnt;
687 }
688 
689 /*
690  * Return kauth_cred_t for the current LWP.
691  */
692 kauth_cred_t
693 kauth_cred_get(void)
694 {
695 	return (curlwp->l_cred);
696 }
697 
698 /*
699  * Returns a scope matching the provided id.
700  * Requires the scope list lock to be held by the caller.
701  */
702 static kauth_scope_t
703 kauth_ifindscope(const char *id)
704 {
705 	kauth_scope_t scope;
706 
707 	KASSERT(rw_lock_held(&kauth_lock));
708 
709 	scope = NULL;
710 	SIMPLEQ_FOREACH(scope, &scope_list, next_scope) {
711 		if (strcmp(scope->id, id) == 0)
712 			break;
713 	}
714 
715 	return (scope);
716 }
717 
718 /*
719  * Register a new scope.
720  *
721  * id - identifier for the scope
722  * callback - the scope's default listener
723  * cookie - cookie to be passed to the listener(s)
724  */
725 kauth_scope_t
726 kauth_register_scope(const char *id, kauth_scope_callback_t callback,
727     void *cookie)
728 {
729 	kauth_scope_t scope;
730 	kauth_listener_t listener = NULL; /* XXX gcc */
731 
732 	/* Sanitize input */
733 	if (id == NULL)
734 		return (NULL);
735 
736 	/* Allocate space for a new scope and listener. */
737 	scope = kmem_alloc(sizeof(*scope), KM_SLEEP);
738 	if (scope == NULL)
739 		return NULL;
740 	if (callback != NULL) {
741 		listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
742 		if (listener == NULL) {
743 			kmem_free(scope, sizeof(*scope));
744 			return (NULL);
745 		}
746 	}
747 
748 	/*
749 	 * Acquire scope list lock.
750 	 */
751 	rw_enter(&kauth_lock, RW_WRITER);
752 
753 	/* Check we don't already have a scope with the same id */
754 	if (kauth_ifindscope(id) != NULL) {
755 		rw_exit(&kauth_lock);
756 
757 		kmem_free(scope, sizeof(*scope));
758 		if (callback != NULL)
759 			kmem_free(listener, sizeof(*listener));
760 
761 		return (NULL);
762 	}
763 
764 	/* Initialize new scope with parameters */
765 	scope->id = id;
766 	scope->cookie = cookie;
767 	scope->nlisteners = 1;
768 
769 	SIMPLEQ_INIT(&scope->listenq);
770 
771 	/* Add default listener */
772 	if (callback != NULL) {
773 		listener->func = callback;
774 		listener->scope = scope;
775 		listener->refcnt = 0;
776 		SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next);
777 	}
778 
779 	/* Insert scope to scopes list */
780 	SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope);
781 
782 	rw_exit(&kauth_lock);
783 
784 	return (scope);
785 }
786 
787 /*
788  * Initialize the kernel authorization subsystem.
789  *
790  * Initialize the scopes list lock.
791  * Create specificdata domain.
792  * Register the credentials scope, used in kauth(9) internally.
793  * Register built-in scopes: generic, system, process, network, machdep, device.
794  */
795 void
796 kauth_init(void)
797 {
798 	rw_init(&kauth_lock);
799 
800 	kauth_cred_cache = pool_cache_init(sizeof(struct kauth_cred),
801 	    coherency_unit, 0, 0, "kcredpl", NULL, IPL_NONE,
802 	    NULL, NULL, NULL);
803 
804 	/* Create specificdata domain. */
805 	kauth_domain = specificdata_domain_create();
806 
807 	/* Register credentials scope. */
808 	kauth_builtin_scope_cred =
809 	    kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL);
810 
811 	/* Register generic scope. */
812 	kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC,
813 	    NULL, NULL);
814 
815 	/* Register system scope. */
816 	kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM,
817 	    NULL, NULL);
818 
819 	/* Register process scope. */
820 	kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS,
821 	    NULL, NULL);
822 
823 	/* Register network scope. */
824 	kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK,
825 	    NULL, NULL);
826 
827 	/* Register machdep scope. */
828 	kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP,
829 	    NULL, NULL);
830 
831 	/* Register device scope. */
832 	kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE,
833 	    NULL, NULL);
834 }
835 
836 /*
837  * Deregister a scope.
838  * Requires scope list lock to be held by the caller.
839  *
840  * scope - the scope to deregister
841  */
842 void
843 kauth_deregister_scope(kauth_scope_t scope)
844 {
845 	if (scope != NULL) {
846 		/* Remove scope from list */
847 		SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope);
848 		kmem_free(scope, sizeof(*scope));
849 	}
850 }
851 
852 /*
853  * Register a listener.
854  *
855  * id - scope identifier.
856  * callback - the callback routine for the listener.
857  * cookie - cookie to pass unmoidfied to the callback.
858  */
859 kauth_listener_t
860 kauth_listen_scope(const char *id, kauth_scope_callback_t callback,
861    void *cookie)
862 {
863 	kauth_scope_t scope;
864 	kauth_listener_t listener;
865 
866 	listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
867 	if (listener == NULL)
868 		return (NULL);
869 
870 	rw_enter(&kauth_lock, RW_WRITER);
871 
872 	/*
873 	 * Find scope struct.
874 	 */
875 	scope = kauth_ifindscope(id);
876 	if (scope == NULL) {
877 		rw_exit(&kauth_lock);
878 		kmem_free(listener, sizeof(*listener));
879 		return (NULL);
880 	}
881 
882 	/* Allocate listener */
883 
884 	/* Initialize listener with parameters */
885 	listener->func = callback;
886 	listener->refcnt = 0;
887 
888 	/* Add listener to scope */
889 	SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next);
890 
891 	/* Raise number of listeners on scope. */
892 	scope->nlisteners++;
893 	listener->scope = scope;
894 
895 	rw_exit(&kauth_lock);
896 
897 	return (listener);
898 }
899 
900 /*
901  * Deregister a listener.
902  *
903  * listener - listener reference as returned from kauth_listen_scope().
904  */
905 void
906 kauth_unlisten_scope(kauth_listener_t listener)
907 {
908 
909 	if (listener != NULL) {
910 		rw_enter(&kauth_lock, RW_WRITER);
911 		SIMPLEQ_REMOVE(&listener->scope->listenq, listener,
912 		    kauth_listener, listener_next);
913 		listener->scope->nlisteners--;
914 		rw_exit(&kauth_lock);
915 		kmem_free(listener, sizeof(*listener));
916 	}
917 }
918 
919 /*
920  * Authorize a request.
921  *
922  * scope - the scope of the request as defined by KAUTH_SCOPE_* or as
923  *	   returned from kauth_register_scope().
924  * credential - credentials of the user ("actor") making the request.
925  * action - request identifier.
926  * arg[0-3] - passed unmodified to listener(s).
927  */
928 int
929 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred,
930 		       kauth_action_t action, void *arg0, void *arg1,
931 		       void *arg2, void *arg3)
932 {
933 	kauth_listener_t listener;
934 	int error, allow, fail;
935 
936 	KASSERT(cred != NULL);
937 	KASSERT(action != 0);
938 
939 	/* Short-circuit requests coming from the kernel. */
940 	if (cred == NOCRED || cred == FSCRED)
941 		return (0);
942 
943 	KASSERT(scope != NULL);
944 
945 	fail = 0;
946 	allow = 0;
947 
948 	/* rw_enter(&kauth_lock, RW_READER); XXX not yet */
949 	SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) {
950 		error = listener->func(cred, action, scope->cookie, arg0,
951 		    arg1, arg2, arg3);
952 
953 		if (error == KAUTH_RESULT_ALLOW)
954 			allow = 1;
955 		else if (error == KAUTH_RESULT_DENY)
956 			fail = 1;
957 	}
958 	/* rw_exit(&kauth_lock); */
959 
960 	if (fail)
961 		return (EPERM);
962 
963 	if (allow)
964 		return (0);
965 
966 	if (!nsecmodels)
967 		return (0);
968 
969 	return (EPERM);
970 };
971 
972 /*
973  * Generic scope authorization wrapper.
974  */
975 int
976 kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0)
977 {
978 	return (kauth_authorize_action(kauth_builtin_scope_generic, cred,
979 	    action, arg0, NULL, NULL, NULL));
980 }
981 
982 /*
983  * System scope authorization wrapper.
984  */
985 int
986 kauth_authorize_system(kauth_cred_t cred, kauth_action_t action,
987     enum kauth_system_req req, void *arg1, void *arg2, void *arg3)
988 {
989 	return (kauth_authorize_action(kauth_builtin_scope_system, cred,
990 	    action, (void *)req, arg1, arg2, arg3));
991 }
992 
993 /*
994  * Process scope authorization wrapper.
995  */
996 int
997 kauth_authorize_process(kauth_cred_t cred, kauth_action_t action,
998     struct proc *p, void *arg1, void *arg2, void *arg3)
999 {
1000 	return (kauth_authorize_action(kauth_builtin_scope_process, cred,
1001 	    action, p, arg1, arg2, arg3));
1002 }
1003 
1004 /*
1005  * Network scope authorization wrapper.
1006  */
1007 int
1008 kauth_authorize_network(kauth_cred_t cred, kauth_action_t action,
1009     enum kauth_network_req req, void *arg1, void *arg2, void *arg3)
1010 {
1011 	return (kauth_authorize_action(kauth_builtin_scope_network, cred,
1012 	    action, (void *)req, arg1, arg2, arg3));
1013 }
1014 
1015 int
1016 kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action,
1017     void *arg0, void *arg1, void *arg2, void *arg3)
1018 {
1019 	return (kauth_authorize_action(kauth_builtin_scope_machdep, cred,
1020 	    action, arg0, arg1, arg2, arg3));
1021 }
1022 
1023 int
1024 kauth_authorize_device(kauth_cred_t cred, kauth_action_t action,
1025     void *arg0, void *arg1, void *arg2, void *arg3)
1026 {
1027 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1028 	    action, arg0, arg1, arg2, arg3));
1029 }
1030 
1031 int
1032 kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action,
1033     struct tty *tty)
1034 {
1035 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1036 	    action, tty, NULL, NULL, NULL));
1037 }
1038 
1039 int
1040 kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req,
1041     struct vnode *vp)
1042 {
1043 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1044 	    KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL));
1045 }
1046 
1047 int
1048 kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits,
1049     void *data)
1050 {
1051 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1052 	    KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev,
1053 	    data, NULL));
1054 }
1055 
1056 static int
1057 kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0,
1058     void *arg1)
1059 {
1060 	int r;
1061 
1062 	r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action,
1063 	    arg0, arg1, NULL, NULL);
1064 
1065 #ifdef DIAGNOSTIC
1066 	if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq))
1067 		KASSERT(r == 0);
1068 #endif /* DIAGNOSTIC */
1069 
1070 	return (r);
1071 }
1072 
1073 void
1074 secmodel_register(void)
1075 {
1076 	KASSERT(nsecmodels + 1 != 0);
1077 
1078 	rw_enter(&kauth_lock, RW_WRITER);
1079 	nsecmodels++;
1080 	rw_exit(&kauth_lock);
1081 }
1082 
1083 void
1084 secmodel_deregister(void)
1085 {
1086 	KASSERT(nsecmodels != 0);
1087 
1088 	rw_enter(&kauth_lock, RW_WRITER);
1089 	nsecmodels--;
1090 	rw_exit(&kauth_lock);
1091 }
1092