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