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