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