xref: /netbsd/sys/kern/kern_auth.c (revision e9b81bf0)
1*e9b81bf0Sriastradh /* $NetBSD: kern_auth.c,v 1.82 2023/02/24 11:02:27 riastradh Exp $ */
233b0a10dSelad 
333b0a10dSelad /*-
433b0a10dSelad  * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
533b0a10dSelad  * All rights reserved.
633b0a10dSelad  *
733b0a10dSelad  * Redistribution and use in source and binary forms, with or without
833b0a10dSelad  * modification, are permitted provided that the following conditions
933b0a10dSelad  * are met:
1033b0a10dSelad  * 1. Redistributions of source code must retain the above copyright
1133b0a10dSelad  *    notice, this list of conditions and the following disclaimer.
1233b0a10dSelad  * 2. Redistributions in binary form must reproduce the above copyright
1333b0a10dSelad  *    notice, this list of conditions and the following disclaimer in the
1433b0a10dSelad  *    documentation and/or other materials provided with the distribution.
15d2e4f716Selad  * 3. The name of the author may not be used to endorse or promote products
1633b0a10dSelad  *    derived from this software without specific prior written permission.
1733b0a10dSelad  *
1833b0a10dSelad  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1933b0a10dSelad  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2033b0a10dSelad  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2133b0a10dSelad  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2233b0a10dSelad  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2333b0a10dSelad  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2433b0a10dSelad  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2533b0a10dSelad  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2633b0a10dSelad  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2733b0a10dSelad  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2833b0a10dSelad  */
2933b0a10dSelad 
30c2ea23c5Selad #include <sys/cdefs.h>
31*e9b81bf0Sriastradh __KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.82 2023/02/24 11:02:27 riastradh Exp $");
32c2ea23c5Selad 
332a84b2c2Schristos #include <sys/types.h>
3433b0a10dSelad #include <sys/param.h>
3533b0a10dSelad #include <sys/queue.h>
3633b0a10dSelad #include <sys/proc.h>
3733b0a10dSelad #include <sys/ucred.h>
3833b0a10dSelad #include <sys/pool.h>
39bddf1b64Schristos #define __KAUTH_PRIVATE
4033b0a10dSelad #include <sys/kauth.h>
41d635d897Sad #include <sys/kmem.h>
42b07ec3fcSad #include <sys/rwlock.h>
43a0c69419Selad #include <sys/sysctl.h>
447ec1c5e6Sad #include <sys/atomic.h>
452a84b2c2Schristos #include <sys/specificdata.h>
46a1621401Selad #include <sys/vnode.h>
47c439bcfeSelad 
486519e39dSjym #include <secmodel/secmodel.h>
496519e39dSjym 
50c439bcfeSelad /*
51c439bcfeSelad  * Secmodel-specific credentials.
52c439bcfeSelad  */
53c439bcfeSelad struct kauth_key {
546519e39dSjym 	secmodel_t ks_secmodel;		/* secmodel */
55c439bcfeSelad 	specificdata_key_t ks_key;	/* key */
56c439bcfeSelad };
5733b0a10dSelad 
582a84b2c2Schristos 
592a84b2c2Schristos /*
6033b0a10dSelad  * Listener.
6133b0a10dSelad  */
6233b0a10dSelad struct kauth_listener {
6333b0a10dSelad 	kauth_scope_callback_t		func;		/* callback */
6433b0a10dSelad 	kauth_scope_t			scope;		/* scope backpointer */
6555a5faa1Sad 	u_int				refcnt;		/* reference count */
6633b0a10dSelad 	SIMPLEQ_ENTRY(kauth_listener)	listener_next;	/* listener list */
6733b0a10dSelad };
6833b0a10dSelad 
6933b0a10dSelad /*
7033b0a10dSelad  * Scope.
7133b0a10dSelad  */
7233b0a10dSelad struct kauth_scope {
7333b0a10dSelad 	const char		       *id;		/* scope name */
7433b0a10dSelad 	void			       *cookie;		/* user cookie */
7555a5faa1Sad 	u_int				nlisteners;	/* # of listeners */
7633b0a10dSelad 	SIMPLEQ_HEAD(, kauth_listener)	listenq;	/* listener list */
7733b0a10dSelad 	SIMPLEQ_ENTRY(kauth_scope)	next_scope;	/* scope list */
7833b0a10dSelad };
7933b0a10dSelad 
80c439bcfeSelad static int kauth_cred_hook(kauth_cred_t, kauth_action_t, void *, void *);
8133b0a10dSelad 
8233b0a10dSelad /* List of scopes and its lock. */
8311910619Smatt static SIMPLEQ_HEAD(, kauth_scope) scope_list =
8411910619Smatt     SIMPLEQ_HEAD_INITIALIZER(scope_list);
8533b0a10dSelad 
8633b0a10dSelad /* Built-in scopes: generic, process. */
8733b0a10dSelad static kauth_scope_t kauth_builtin_scope_generic;
885f7169ccSelad static kauth_scope_t kauth_builtin_scope_system;
8933b0a10dSelad static kauth_scope_t kauth_builtin_scope_process;
905f7169ccSelad static kauth_scope_t kauth_builtin_scope_network;
915f7169ccSelad static kauth_scope_t kauth_builtin_scope_machdep;
92b8a33934Selad static kauth_scope_t kauth_builtin_scope_device;
93c439bcfeSelad static kauth_scope_t kauth_builtin_scope_cred;
94a1621401Selad static kauth_scope_t kauth_builtin_scope_vnode;
9533b0a10dSelad 
96c439bcfeSelad static specificdata_domain_t kauth_domain;
97d18c6ca4Sad static pool_cache_t kauth_cred_cache;
98a0c69419Selad 
99b07ec3fcSad krwlock_t	kauth_lock;
100c439bcfeSelad 
10133b0a10dSelad /* Allocate new, empty kauth credentials. */
10233b0a10dSelad kauth_cred_t
kauth_cred_alloc(void)103be46b8e4Syamt kauth_cred_alloc(void)
104be46b8e4Syamt {
10533b0a10dSelad 	kauth_cred_t cred;
10633b0a10dSelad 
107d18c6ca4Sad 	cred = pool_cache_get(kauth_cred_cache, PR_WAITOK);
108d18c6ca4Sad 
10933b0a10dSelad 	cred->cr_refcnt = 1;
110d18c6ca4Sad 	cred->cr_uid = 0;
111d18c6ca4Sad 	cred->cr_euid = 0;
112d18c6ca4Sad 	cred->cr_svuid = 0;
113d18c6ca4Sad 	cred->cr_gid = 0;
114d18c6ca4Sad 	cred->cr_egid = 0;
115d18c6ca4Sad 	cred->cr_svgid = 0;
116d18c6ca4Sad 	cred->cr_ngroups = 0;
117d18c6ca4Sad 
118c439bcfeSelad 	specificdata_init(kauth_domain, &cred->cr_sd);
119c439bcfeSelad 	kauth_cred_hook(cred, KAUTH_CRED_INIT, NULL, NULL);
12033b0a10dSelad 
12133b0a10dSelad 	return (cred);
12233b0a10dSelad }
12333b0a10dSelad 
12433b0a10dSelad /* Increment reference count to cred. */
12533b0a10dSelad void
kauth_cred_hold(kauth_cred_t cred)12633b0a10dSelad kauth_cred_hold(kauth_cred_t cred)
12733b0a10dSelad {
12833b0a10dSelad 	KASSERT(cred != NULL);
1291da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
1301da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
13155a5faa1Sad 	KASSERT(cred->cr_refcnt > 0);
13233b0a10dSelad 
1337ec1c5e6Sad 	atomic_inc_uint(&cred->cr_refcnt);
13433b0a10dSelad }
13533b0a10dSelad 
13633b0a10dSelad /* Decrease reference count to cred. If reached zero, free it. */
13733b0a10dSelad void
kauth_cred_free(kauth_cred_t cred)138be46b8e4Syamt kauth_cred_free(kauth_cred_t cred)
139be46b8e4Syamt {
14055a5faa1Sad 
14133b0a10dSelad 	KASSERT(cred != NULL);
1421da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
1431da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
14455a5faa1Sad 	KASSERT(cred->cr_refcnt > 0);
145273f17a1Syamt 	ASSERT_SLEEPABLE();
14633b0a10dSelad 
1471e11a48fSriastradh 	membar_release();
148045db07aSad 	if (atomic_dec_uint_nv(&cred->cr_refcnt) > 0)
1497ec1c5e6Sad 		return;
1501e11a48fSriastradh 	membar_acquire();
15133b0a10dSelad 
152c439bcfeSelad 	kauth_cred_hook(cred, KAUTH_CRED_FREE, NULL, NULL);
153c439bcfeSelad 	specificdata_fini(kauth_domain, &cred->cr_sd);
154d18c6ca4Sad 	pool_cache_put(kauth_cred_cache, cred);
15533b0a10dSelad }
15633b0a10dSelad 
15704c196a6Sdsl static void
kauth_cred_clone1(kauth_cred_t from,kauth_cred_t to,bool copy_groups)158a254a27eSdsl kauth_cred_clone1(kauth_cred_t from, kauth_cred_t to, bool copy_groups)
159a254a27eSdsl {
16033b0a10dSelad 	KASSERT(from != NULL);
1611da7d2a2Smlelstv 	KASSERT(from != NOCRED);
1621da7d2a2Smlelstv 	KASSERT(from != FSCRED);
16333b0a10dSelad 	KASSERT(to != NULL);
1641da7d2a2Smlelstv 	KASSERT(to != NOCRED);
1651da7d2a2Smlelstv 	KASSERT(to != FSCRED);
16655a5faa1Sad 	KASSERT(from->cr_refcnt > 0);
16733b0a10dSelad 
16833b0a10dSelad 	to->cr_uid = from->cr_uid;
16933b0a10dSelad 	to->cr_euid = from->cr_euid;
17033b0a10dSelad 	to->cr_svuid = from->cr_svuid;
17133b0a10dSelad 	to->cr_gid = from->cr_gid;
17233b0a10dSelad 	to->cr_egid = from->cr_egid;
17333b0a10dSelad 	to->cr_svgid = from->cr_svgid;
174a254a27eSdsl 	if (copy_groups) {
17533b0a10dSelad 		to->cr_ngroups = from->cr_ngroups;
17655a5faa1Sad 		memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups));
177a254a27eSdsl 	}
178c439bcfeSelad 
179c439bcfeSelad 	kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL);
18033b0a10dSelad }
18133b0a10dSelad 
18204c196a6Sdsl void
kauth_cred_clone(kauth_cred_t from,kauth_cred_t to)18304c196a6Sdsl kauth_cred_clone(kauth_cred_t from, kauth_cred_t to)
18404c196a6Sdsl {
18504c196a6Sdsl 	kauth_cred_clone1(from, to, true);
18604c196a6Sdsl }
18704c196a6Sdsl 
18833b0a10dSelad /*
18933b0a10dSelad  * Duplicate cred and return a new kauth_cred_t.
19033b0a10dSelad  */
19133b0a10dSelad kauth_cred_t
kauth_cred_dup(kauth_cred_t cred)19233b0a10dSelad kauth_cred_dup(kauth_cred_t cred)
19333b0a10dSelad {
19433b0a10dSelad 	kauth_cred_t new_cred;
19533b0a10dSelad 
19633b0a10dSelad 	KASSERT(cred != NULL);
1971da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
1981da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
19955a5faa1Sad 	KASSERT(cred->cr_refcnt > 0);
20033b0a10dSelad 
20133b0a10dSelad 	new_cred = kauth_cred_alloc();
20233b0a10dSelad 
20333b0a10dSelad 	kauth_cred_clone(cred, new_cred);
20433b0a10dSelad 
20533b0a10dSelad 	return (new_cred);
20633b0a10dSelad }
20733b0a10dSelad 
20833b0a10dSelad /*
20933b0a10dSelad  * Similar to crcopy(), only on a kauth_cred_t.
21033b0a10dSelad  * XXX: Is this even needed? [kauth_cred_copy]
21133b0a10dSelad  */
21233b0a10dSelad kauth_cred_t
kauth_cred_copy(kauth_cred_t cred)21333b0a10dSelad kauth_cred_copy(kauth_cred_t cred)
21433b0a10dSelad {
21533b0a10dSelad 	kauth_cred_t new_cred;
21633b0a10dSelad 
21733b0a10dSelad 	KASSERT(cred != NULL);
2181da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
2191da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
22055a5faa1Sad 	KASSERT(cred->cr_refcnt > 0);
22133b0a10dSelad 
22233b0a10dSelad 	/* If the provided credentials already have one reference, use them. */
22333b0a10dSelad 	if (cred->cr_refcnt == 1)
22433b0a10dSelad 		return (cred);
22533b0a10dSelad 
22633b0a10dSelad 	new_cred = kauth_cred_alloc();
22733b0a10dSelad 
22833b0a10dSelad 	kauth_cred_clone(cred, new_cred);
22933b0a10dSelad 
23033b0a10dSelad 	kauth_cred_free(cred);
23133b0a10dSelad 
23233b0a10dSelad 	return (new_cred);
23333b0a10dSelad }
23433b0a10dSelad 
2356df6f0eaSelad void
kauth_proc_fork(struct proc * parent,struct proc * child)2366df6f0eaSelad kauth_proc_fork(struct proc *parent, struct proc *child)
2376df6f0eaSelad {
238b07ec3fcSad 
239284c2b9aSad 	mutex_enter(parent->p_lock);
2406df6f0eaSelad 	kauth_cred_hold(parent->p_cred);
2416df6f0eaSelad 	child->p_cred = parent->p_cred;
242284c2b9aSad 	mutex_exit(parent->p_lock);
243c439bcfeSelad 
244b07ec3fcSad 	/* XXX: relies on parent process stalling during fork() */
245c439bcfeSelad 	kauth_cred_hook(parent->p_cred, KAUTH_CRED_FORK, parent,
246c439bcfeSelad 	    child);
2476df6f0eaSelad }
2486df6f0eaSelad 
2490bc14945Scheusov void
kauth_proc_chroot(kauth_cred_t cred,struct cwdinfo * cwdi)2500bc14945Scheusov kauth_proc_chroot(kauth_cred_t cred, struct cwdinfo *cwdi)
2510bc14945Scheusov {
2520bc14945Scheusov 	kauth_cred_hook(cred, KAUTH_CRED_CHROOT, cwdi, NULL);
2530bc14945Scheusov }
2540bc14945Scheusov 
25533b0a10dSelad uid_t
kauth_cred_getuid(kauth_cred_t cred)25633b0a10dSelad kauth_cred_getuid(kauth_cred_t cred)
25733b0a10dSelad {
25833b0a10dSelad 	KASSERT(cred != NULL);
2591da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
2601da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
26133b0a10dSelad 
26233b0a10dSelad 	return (cred->cr_uid);
26333b0a10dSelad }
26433b0a10dSelad 
26533b0a10dSelad uid_t
kauth_cred_geteuid(kauth_cred_t cred)26633b0a10dSelad kauth_cred_geteuid(kauth_cred_t cred)
26733b0a10dSelad {
26833b0a10dSelad 	KASSERT(cred != NULL);
2691da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
2701da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
27133b0a10dSelad 
27233b0a10dSelad 	return (cred->cr_euid);
27333b0a10dSelad }
27433b0a10dSelad 
27533b0a10dSelad uid_t
kauth_cred_getsvuid(kauth_cred_t cred)27633b0a10dSelad kauth_cred_getsvuid(kauth_cred_t cred)
27733b0a10dSelad {
27833b0a10dSelad 	KASSERT(cred != NULL);
2791da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
2801da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
28133b0a10dSelad 
28233b0a10dSelad 	return (cred->cr_svuid);
28333b0a10dSelad }
28433b0a10dSelad 
28533b0a10dSelad gid_t
kauth_cred_getgid(kauth_cred_t cred)28633b0a10dSelad kauth_cred_getgid(kauth_cred_t cred)
28733b0a10dSelad {
28833b0a10dSelad 	KASSERT(cred != NULL);
2891da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
2901da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
29133b0a10dSelad 
29233b0a10dSelad 	return (cred->cr_gid);
29333b0a10dSelad }
29433b0a10dSelad 
29533b0a10dSelad gid_t
kauth_cred_getegid(kauth_cred_t cred)29633b0a10dSelad kauth_cred_getegid(kauth_cred_t cred)
29733b0a10dSelad {
29833b0a10dSelad 	KASSERT(cred != NULL);
2991da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3001da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
30133b0a10dSelad 
30233b0a10dSelad 	return (cred->cr_egid);
30333b0a10dSelad }
30433b0a10dSelad 
30533b0a10dSelad gid_t
kauth_cred_getsvgid(kauth_cred_t cred)30633b0a10dSelad kauth_cred_getsvgid(kauth_cred_t cred)
30733b0a10dSelad {
30833b0a10dSelad 	KASSERT(cred != NULL);
3091da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3101da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
31133b0a10dSelad 
31233b0a10dSelad 	return (cred->cr_svgid);
31333b0a10dSelad }
31433b0a10dSelad 
31533b0a10dSelad void
kauth_cred_setuid(kauth_cred_t cred,uid_t uid)31633b0a10dSelad kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
31733b0a10dSelad {
31833b0a10dSelad 	KASSERT(cred != NULL);
3191da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3201da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
32155a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
32233b0a10dSelad 
32333b0a10dSelad 	cred->cr_uid = uid;
32433b0a10dSelad }
32533b0a10dSelad 
32633b0a10dSelad void
kauth_cred_seteuid(kauth_cred_t cred,uid_t uid)32733b0a10dSelad kauth_cred_seteuid(kauth_cred_t cred, uid_t uid)
32833b0a10dSelad {
32933b0a10dSelad 	KASSERT(cred != NULL);
3301da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3311da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
33255a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
33333b0a10dSelad 
33433b0a10dSelad 	cred->cr_euid = uid;
33533b0a10dSelad }
33633b0a10dSelad 
33733b0a10dSelad void
kauth_cred_setsvuid(kauth_cred_t cred,uid_t uid)33833b0a10dSelad kauth_cred_setsvuid(kauth_cred_t cred, uid_t uid)
33933b0a10dSelad {
34033b0a10dSelad 	KASSERT(cred != NULL);
3411da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3421da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
34355a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
34433b0a10dSelad 
34533b0a10dSelad 	cred->cr_svuid = uid;
34633b0a10dSelad }
34733b0a10dSelad 
34833b0a10dSelad void
kauth_cred_setgid(kauth_cred_t cred,gid_t gid)34933b0a10dSelad kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
35033b0a10dSelad {
35133b0a10dSelad 	KASSERT(cred != NULL);
3521da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3531da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
35455a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
35533b0a10dSelad 
35633b0a10dSelad 	cred->cr_gid = gid;
35733b0a10dSelad }
35833b0a10dSelad 
35933b0a10dSelad void
kauth_cred_setegid(kauth_cred_t cred,gid_t gid)36033b0a10dSelad kauth_cred_setegid(kauth_cred_t cred, gid_t gid)
36133b0a10dSelad {
36233b0a10dSelad 	KASSERT(cred != NULL);
3631da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3641da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
36555a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
36633b0a10dSelad 
36733b0a10dSelad 	cred->cr_egid = gid;
36833b0a10dSelad }
36933b0a10dSelad 
37033b0a10dSelad void
kauth_cred_setsvgid(kauth_cred_t cred,gid_t gid)37133b0a10dSelad kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid)
37233b0a10dSelad {
37333b0a10dSelad 	KASSERT(cred != NULL);
3741da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3751da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
37655a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
37733b0a10dSelad 
37833b0a10dSelad 	cred->cr_svgid = gid;
37933b0a10dSelad }
38033b0a10dSelad 
38133b0a10dSelad /* Checks if gid is a member of the groups in cred. */
38233b0a10dSelad int
kauth_cred_ismember_gid(kauth_cred_t cred,gid_t gid,int * resultp)38333b0a10dSelad kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
38433b0a10dSelad {
3852b2f4703Slukem 	uint32_t i;
38633b0a10dSelad 
38733b0a10dSelad 	KASSERT(cred != NULL);
3881da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
3891da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
39033b0a10dSelad 	KASSERT(resultp != NULL);
39133b0a10dSelad 
39233b0a10dSelad 	*resultp = 0;
39333b0a10dSelad 
39433b0a10dSelad 	for (i = 0; i < cred->cr_ngroups; i++)
39533b0a10dSelad 		if (cred->cr_groups[i] == gid) {
39633b0a10dSelad 			*resultp = 1;
39733b0a10dSelad 			break;
39833b0a10dSelad 		}
39933b0a10dSelad 
40033b0a10dSelad 	return (0);
40133b0a10dSelad }
40233b0a10dSelad 
40379aa17d9Schristos int
kauth_cred_groupmember(kauth_cred_t cred,gid_t gid)40479aa17d9Schristos kauth_cred_groupmember(kauth_cred_t cred, gid_t gid)
40579aa17d9Schristos {
40679aa17d9Schristos 	int ismember, error;
40779aa17d9Schristos 
40879aa17d9Schristos 	KASSERT(cred != NULL);
40979aa17d9Schristos 	KASSERT(cred != NOCRED);
41079aa17d9Schristos 	KASSERT(cred != FSCRED);
41179aa17d9Schristos 
41279aa17d9Schristos 	error = kauth_cred_ismember_gid(cred, gid, &ismember);
41379aa17d9Schristos 	if (error)
41479aa17d9Schristos 		return error;
41579aa17d9Schristos 
41679aa17d9Schristos 	if (kauth_cred_getegid(cred) == gid || ismember)
41779aa17d9Schristos 		return 0;
41879aa17d9Schristos 
41979aa17d9Schristos 	return -1;
42079aa17d9Schristos }
42179aa17d9Schristos 
42255a5faa1Sad u_int
kauth_cred_ngroups(kauth_cred_t cred)42333b0a10dSelad kauth_cred_ngroups(kauth_cred_t cred)
42433b0a10dSelad {
42533b0a10dSelad 	KASSERT(cred != NULL);
4261da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
4271da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
42833b0a10dSelad 
42933b0a10dSelad 	return (cred->cr_ngroups);
43033b0a10dSelad }
43133b0a10dSelad 
43233b0a10dSelad /*
43333b0a10dSelad  * Return the group at index idx from the groups in cred.
43433b0a10dSelad  */
43533b0a10dSelad gid_t
kauth_cred_group(kauth_cred_t cred,u_int idx)43655a5faa1Sad kauth_cred_group(kauth_cred_t cred, u_int idx)
43733b0a10dSelad {
43833b0a10dSelad 	KASSERT(cred != NULL);
4391da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
4401da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
44133b0a10dSelad 	KASSERT(idx < cred->cr_ngroups);
44233b0a10dSelad 
44333b0a10dSelad 	return (cred->cr_groups[idx]);
44433b0a10dSelad }
44533b0a10dSelad 
44633b0a10dSelad /* XXX elad: gmuid is unused for now. */
44733b0a10dSelad int
kauth_cred_setgroups(kauth_cred_t cred,const gid_t * grbuf,size_t len,uid_t gmuid,enum uio_seg seg)44804c196a6Sdsl kauth_cred_setgroups(kauth_cred_t cred, const gid_t *grbuf, size_t len,
44997427fb0Syamt     uid_t gmuid, enum uio_seg seg)
45033b0a10dSelad {
45104c196a6Sdsl 	int error = 0;
45204c196a6Sdsl 
45333b0a10dSelad 	KASSERT(cred != NULL);
4541da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
4551da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
45655a5faa1Sad 	KASSERT(cred->cr_refcnt == 1);
45733b0a10dSelad 
458726d1181Smatt 	if (len > __arraycount(cred->cr_groups))
45904c196a6Sdsl 		return EINVAL;
46004c196a6Sdsl 
46104c196a6Sdsl 	if (len) {
46297427fb0Syamt 		if (seg == UIO_SYSSPACE) {
46304c196a6Sdsl 			memcpy(cred->cr_groups, grbuf,
46404c196a6Sdsl 			    len * sizeof(cred->cr_groups[0]));
46597427fb0Syamt 		} else {
46604c196a6Sdsl 			error = copyin(grbuf, cred->cr_groups,
46704c196a6Sdsl 			    len * sizeof(cred->cr_groups[0]));
46804c196a6Sdsl 			if (error != 0)
46904c196a6Sdsl 				len = 0;
47004c196a6Sdsl 		}
47104c196a6Sdsl 	}
47233b0a10dSelad 	memset(cred->cr_groups + len, 0xff,
47333b0a10dSelad 	    sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0])));
47433b0a10dSelad 
47533b0a10dSelad 	cred->cr_ngroups = len;
47633b0a10dSelad 
47704c196a6Sdsl 	return error;
47833b0a10dSelad }
47933b0a10dSelad 
48004c196a6Sdsl /* This supports sys_setgroups() */
481a254a27eSdsl int
kauth_proc_setgroups(struct lwp * l,kauth_cred_t ncred)482a254a27eSdsl kauth_proc_setgroups(struct lwp *l, kauth_cred_t ncred)
483a254a27eSdsl {
484a254a27eSdsl 	kauth_cred_t cred;
485a254a27eSdsl 	int error;
486a254a27eSdsl 
487a254a27eSdsl 	/*
488a254a27eSdsl 	 * At this point we could delete duplicate groups from ncred,
489a254a27eSdsl 	 * and plausibly sort the list - but in general the later is
490a254a27eSdsl 	 * a bad idea.
491a254a27eSdsl 	 */
492a254a27eSdsl 	proc_crmod_enter();
493a254a27eSdsl 	/* Maybe we should use curproc here ? */
494a254a27eSdsl 	cred = l->l_proc->p_cred;
495a254a27eSdsl 
496a254a27eSdsl 	kauth_cred_clone1(cred, ncred, false);
497a254a27eSdsl 
498a254a27eSdsl 	error = kauth_authorize_process(cred, KAUTH_PROCESS_SETID,
499a254a27eSdsl 	    l->l_proc, NULL, NULL, NULL);
500a254a27eSdsl 	if (error != 0) {
501a254a27eSdsl 		proc_crmod_leave(cred, ncred, false);
502a254a27eSdsl 			return error;
503a254a27eSdsl 	}
504a254a27eSdsl 
505a254a27eSdsl 	/* Broadcast our credentials to the process and other LWPs. */
506a254a27eSdsl  	proc_crmod_leave(ncred, cred, true);
507a254a27eSdsl 	return 0;
508a254a27eSdsl }
509a254a27eSdsl 
51033b0a10dSelad int
kauth_cred_getgroups(kauth_cred_t cred,gid_t * grbuf,size_t len,enum uio_seg seg)51104c196a6Sdsl kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len,
51297427fb0Syamt     enum uio_seg seg)
51333b0a10dSelad {
51433b0a10dSelad 	KASSERT(cred != NULL);
51533b0a10dSelad 
51604c196a6Sdsl 	if (len > cred->cr_ngroups)
51704c196a6Sdsl 		return EINVAL;
51804c196a6Sdsl 
51997427fb0Syamt 	if (seg == UIO_USERSPACE)
52004c196a6Sdsl 		return copyout(cred->cr_groups, grbuf, sizeof(*grbuf) * len);
52133b0a10dSelad 	memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len);
52233b0a10dSelad 
52304c196a6Sdsl 	return 0;
524a254a27eSdsl }
525a254a27eSdsl 
526c439bcfeSelad int
kauth_register_key(secmodel_t secmodel,kauth_key_t * result)5276519e39dSjym kauth_register_key(secmodel_t secmodel, kauth_key_t *result)
528c439bcfeSelad {
529c439bcfeSelad 	kauth_key_t k;
530c439bcfeSelad 	specificdata_key_t key;
531c439bcfeSelad 	int error;
532c439bcfeSelad 
533c439bcfeSelad 	KASSERT(result != NULL);
534c439bcfeSelad 
535c439bcfeSelad 	error = specificdata_key_create(kauth_domain, &key, NULL);
536c439bcfeSelad 	if (error)
537c439bcfeSelad 		return (error);
538c439bcfeSelad 
539c439bcfeSelad 	k = kmem_alloc(sizeof(*k), KM_SLEEP);
540c439bcfeSelad 	k->ks_secmodel = secmodel;
541c439bcfeSelad 	k->ks_key = key;
542c439bcfeSelad 
543c439bcfeSelad 	*result = k;
544c439bcfeSelad 
545c439bcfeSelad 	return (0);
546c439bcfeSelad }
547c439bcfeSelad 
548c439bcfeSelad int
kauth_deregister_key(kauth_key_t key)549c439bcfeSelad kauth_deregister_key(kauth_key_t key)
550c439bcfeSelad {
551c439bcfeSelad 	KASSERT(key != NULL);
552c439bcfeSelad 
553c439bcfeSelad 	specificdata_key_delete(kauth_domain, key->ks_key);
554c439bcfeSelad 	kmem_free(key, sizeof(*key));
555c439bcfeSelad 
556c439bcfeSelad 	return (0);
557c439bcfeSelad }
558c439bcfeSelad 
559c439bcfeSelad void *
kauth_cred_getdata(kauth_cred_t cred,kauth_key_t key)560c439bcfeSelad kauth_cred_getdata(kauth_cred_t cred, kauth_key_t key)
561c439bcfeSelad {
562c439bcfeSelad 	KASSERT(cred != NULL);
5631da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
5641da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
565c439bcfeSelad 	KASSERT(key != NULL);
566c439bcfeSelad 
567c439bcfeSelad 	return (specificdata_getspecific(kauth_domain, &cred->cr_sd,
568c439bcfeSelad 	    key->ks_key));
569c439bcfeSelad }
570c439bcfeSelad 
571c439bcfeSelad void
kauth_cred_setdata(kauth_cred_t cred,kauth_key_t key,void * data)572c439bcfeSelad kauth_cred_setdata(kauth_cred_t cred, kauth_key_t key, void *data)
573c439bcfeSelad {
574c439bcfeSelad 	KASSERT(cred != NULL);
5751da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
5761da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
577c439bcfeSelad 	KASSERT(key != NULL);
578c439bcfeSelad 
579c439bcfeSelad 	specificdata_setspecific(kauth_domain, &cred->cr_sd, key->ks_key, data);
580c439bcfeSelad }
581c439bcfeSelad 
58233b0a10dSelad /*
5835f7169ccSelad  * Match uids in two credentials.
58433b0a10dSelad  */
5855f7169ccSelad int
kauth_cred_uidmatch(kauth_cred_t cred1,kauth_cred_t cred2)58633b0a10dSelad kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2)
58733b0a10dSelad {
58833b0a10dSelad 	KASSERT(cred1 != NULL);
5891da7d2a2Smlelstv 	KASSERT(cred1 != NOCRED);
5901da7d2a2Smlelstv 	KASSERT(cred1 != FSCRED);
59133b0a10dSelad 	KASSERT(cred2 != NULL);
5921da7d2a2Smlelstv 	KASSERT(cred2 != NOCRED);
5931da7d2a2Smlelstv 	KASSERT(cred2 != FSCRED);
59433b0a10dSelad 
59533b0a10dSelad 	if (cred1->cr_uid == cred2->cr_uid ||
59633b0a10dSelad 	    cred1->cr_euid == cred2->cr_uid ||
59733b0a10dSelad 	    cred1->cr_uid == cred2->cr_euid ||
59833b0a10dSelad 	    cred1->cr_euid == cred2->cr_euid)
59933b0a10dSelad 		return (1);
60033b0a10dSelad 
60133b0a10dSelad 	return (0);
60233b0a10dSelad }
60333b0a10dSelad 
60455a5faa1Sad u_int
kauth_cred_getrefcnt(kauth_cred_t cred)60533b0a10dSelad kauth_cred_getrefcnt(kauth_cred_t cred)
60633b0a10dSelad {
60733b0a10dSelad 	KASSERT(cred != NULL);
6081da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
6091da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
61033b0a10dSelad 
61133b0a10dSelad 	return (cred->cr_refcnt);
61233b0a10dSelad }
61333b0a10dSelad 
61433b0a10dSelad /*
61533b0a10dSelad  * Convert userland credentials (struct uucred) to kauth_cred_t.
61696d83359Spooka  * XXX: For NFS & puffs
61733b0a10dSelad  */
61833b0a10dSelad void
kauth_uucred_to_cred(kauth_cred_t cred,const struct uucred * uuc)61996d83359Spooka kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc)
62033b0a10dSelad {
62133b0a10dSelad 	KASSERT(cred != NULL);
6221da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
6231da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
62433b0a10dSelad 	KASSERT(uuc != NULL);
62533b0a10dSelad 
62633b0a10dSelad 	cred->cr_refcnt = 1;
62733b0a10dSelad 	cred->cr_uid = uuc->cr_uid;
62833b0a10dSelad 	cred->cr_euid = uuc->cr_uid;
62933b0a10dSelad 	cred->cr_svuid = uuc->cr_uid;
63033b0a10dSelad 	cred->cr_gid = uuc->cr_gid;
63133b0a10dSelad 	cred->cr_egid = uuc->cr_gid;
63233b0a10dSelad 	cred->cr_svgid = uuc->cr_gid;
633a8a5c538Sriastradh 	cred->cr_ngroups = uimin(uuc->cr_ngroups, NGROUPS);
63433b0a10dSelad 	kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups),
63504c196a6Sdsl 	    cred->cr_ngroups, -1, UIO_SYSSPACE);
63633b0a10dSelad }
63733b0a10dSelad 
63833b0a10dSelad /*
63996d83359Spooka  * Convert kauth_cred_t to userland credentials (struct uucred).
64096d83359Spooka  * XXX: For NFS & puffs
64196d83359Spooka  */
64296d83359Spooka void
kauth_cred_to_uucred(struct uucred * uuc,const kauth_cred_t cred)64396d83359Spooka kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred)
64496d83359Spooka {
64596d83359Spooka 	KASSERT(cred != NULL);
6461da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
6471da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
64896d83359Spooka 	KASSERT(uuc != NULL);
64996d83359Spooka 	int ng;
65096d83359Spooka 
651a8a5c538Sriastradh 	ng = uimin(cred->cr_ngroups, NGROUPS);
65296d83359Spooka 	uuc->cr_uid = cred->cr_euid;
65396d83359Spooka 	uuc->cr_gid = cred->cr_egid;
65496d83359Spooka 	uuc->cr_ngroups = ng;
65504c196a6Sdsl 	kauth_cred_getgroups(cred, uuc->cr_groups, ng, UIO_SYSSPACE);
65696d83359Spooka }
65796d83359Spooka 
65896d83359Spooka /*
65933b0a10dSelad  * Compare kauth_cred_t and uucred credentials.
66033b0a10dSelad  * XXX: Modelled after crcmp() for NFS.
66133b0a10dSelad  */
66233b0a10dSelad int
kauth_cred_uucmp(kauth_cred_t cred,const struct uucred * uuc)66333b0a10dSelad kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc)
66433b0a10dSelad {
66533b0a10dSelad 	KASSERT(cred != NULL);
6661da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
6671da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
66833b0a10dSelad 	KASSERT(uuc != NULL);
66933b0a10dSelad 
67033b0a10dSelad 	if (cred->cr_euid == uuc->cr_uid &&
67133b0a10dSelad 	    cred->cr_egid == uuc->cr_gid &&
6722b2f4703Slukem 	    cred->cr_ngroups == (uint32_t)uuc->cr_ngroups) {
67333b0a10dSelad 		int i;
67433b0a10dSelad 
67533b0a10dSelad 		/* Check if all groups from uuc appear in cred. */
67633b0a10dSelad 		for (i = 0; i < uuc->cr_ngroups; i++) {
67733b0a10dSelad 			int ismember;
67833b0a10dSelad 
67933b0a10dSelad 			ismember = 0;
68033b0a10dSelad 			if (kauth_cred_ismember_gid(cred, uuc->cr_groups[i],
68133b0a10dSelad 			    &ismember) != 0 || !ismember)
68233b0a10dSelad 				return (1);
68333b0a10dSelad 		}
68433b0a10dSelad 
68533b0a10dSelad 		return (0);
68633b0a10dSelad 	}
68733b0a10dSelad 
688a53726f2Syamt 	return (1);
689a53726f2Syamt }
690a53726f2Syamt 
69133b0a10dSelad /*
6922af3d29eSad  * Make a struct ucred out of a kauth_cred_t.  For compatibility.
69333b0a10dSelad  */
69433b0a10dSelad void
kauth_cred_toucred(kauth_cred_t cred,struct ki_ucred * uc)6956209b5bbSdsl kauth_cred_toucred(kauth_cred_t cred, struct ki_ucred *uc)
69633b0a10dSelad {
69733b0a10dSelad 	KASSERT(cred != NULL);
6981da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
6991da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
70033b0a10dSelad 	KASSERT(uc != NULL);
70133b0a10dSelad 
7022af3d29eSad 	uc->cr_ref = cred->cr_refcnt;
70333b0a10dSelad 	uc->cr_uid = cred->cr_euid;
70433b0a10dSelad 	uc->cr_gid = cred->cr_egid;
705a8a5c538Sriastradh 	uc->cr_ngroups = uimin(cred->cr_ngroups, __arraycount(uc->cr_groups));
70633b0a10dSelad 	memcpy(uc->cr_groups, cred->cr_groups,
70733b0a10dSelad 	       uc->cr_ngroups * sizeof(uc->cr_groups[0]));
70833b0a10dSelad }
70933b0a10dSelad 
71033b0a10dSelad /*
7112af3d29eSad  * Make a struct pcred out of a kauth_cred_t.  For compatibility.
71233b0a10dSelad  */
71333b0a10dSelad void
kauth_cred_topcred(kauth_cred_t cred,struct ki_pcred * pc)7146209b5bbSdsl kauth_cred_topcred(kauth_cred_t cred, struct ki_pcred *pc)
71533b0a10dSelad {
71633b0a10dSelad 	KASSERT(cred != NULL);
7171da7d2a2Smlelstv 	KASSERT(cred != NOCRED);
7181da7d2a2Smlelstv 	KASSERT(cred != FSCRED);
71933b0a10dSelad 	KASSERT(pc != NULL);
72033b0a10dSelad 
7216209b5bbSdsl 	pc->p_pad = NULL;
72233b0a10dSelad 	pc->p_ruid = cred->cr_uid;
72333b0a10dSelad 	pc->p_svuid = cred->cr_svuid;
72433b0a10dSelad 	pc->p_rgid = cred->cr_gid;
72533b0a10dSelad 	pc->p_svgid = cred->cr_svgid;
72633b0a10dSelad 	pc->p_refcnt = cred->cr_refcnt;
72733b0a10dSelad }
72833b0a10dSelad 
72933b0a10dSelad /*
730f474dcebSad  * Return kauth_cred_t for the current LWP.
73133b0a10dSelad  */
73233b0a10dSelad kauth_cred_t
kauth_cred_get(void)73333b0a10dSelad kauth_cred_get(void)
73433b0a10dSelad {
735f474dcebSad 	return (curlwp->l_cred);
73633b0a10dSelad }
73733b0a10dSelad 
73833b0a10dSelad /*
73933b0a10dSelad  * Returns a scope matching the provided id.
74033b0a10dSelad  * Requires the scope list lock to be held by the caller.
74133b0a10dSelad  */
74233b0a10dSelad static kauth_scope_t
kauth_ifindscope(const char * id)743be46b8e4Syamt kauth_ifindscope(const char *id)
744be46b8e4Syamt {
74533b0a10dSelad 	kauth_scope_t scope;
74633b0a10dSelad 
747b07ec3fcSad 	KASSERT(rw_lock_held(&kauth_lock));
74833b0a10dSelad 
74933b0a10dSelad 	scope = NULL;
75033b0a10dSelad 	SIMPLEQ_FOREACH(scope, &scope_list, next_scope) {
75133b0a10dSelad 		if (strcmp(scope->id, id) == 0)
75233b0a10dSelad 			break;
75333b0a10dSelad 	}
75433b0a10dSelad 
75533b0a10dSelad 	return (scope);
75633b0a10dSelad }
75733b0a10dSelad 
75833b0a10dSelad /*
75933b0a10dSelad  * Register a new scope.
76033b0a10dSelad  *
76133b0a10dSelad  * id - identifier for the scope
76233b0a10dSelad  * callback - the scope's default listener
76333b0a10dSelad  * cookie - cookie to be passed to the listener(s)
76433b0a10dSelad  */
76533b0a10dSelad kauth_scope_t
kauth_register_scope(const char * id,kauth_scope_callback_t callback,void * cookie)76633b0a10dSelad kauth_register_scope(const char *id, kauth_scope_callback_t callback,
76733b0a10dSelad     void *cookie)
76833b0a10dSelad {
76933b0a10dSelad 	kauth_scope_t scope;
77097194de3Syamt 	kauth_listener_t listener = NULL; /* XXX gcc */
77133b0a10dSelad 
77233b0a10dSelad 	/* Sanitize input */
773c07e4988Schristos 	if (id == NULL)
77433b0a10dSelad 		return (NULL);
77533b0a10dSelad 
77633b0a10dSelad 	/* Allocate space for a new scope and listener. */
777d635d897Sad 	scope = kmem_alloc(sizeof(*scope), KM_SLEEP);
778d65a6a67Schs 	if (callback != NULL)
779d635d897Sad 		listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
78033b0a10dSelad 
781d635d897Sad 	/*
782d635d897Sad 	 * Acquire scope list lock.
783d635d897Sad 	 */
784b07ec3fcSad 	rw_enter(&kauth_lock, RW_WRITER);
78533b0a10dSelad 
78633b0a10dSelad 	/* Check we don't already have a scope with the same id */
78733b0a10dSelad 	if (kauth_ifindscope(id) != NULL) {
788b07ec3fcSad 		rw_exit(&kauth_lock);
78933b0a10dSelad 
790d635d897Sad 		kmem_free(scope, sizeof(*scope));
791d635d897Sad 		if (callback != NULL)
792d635d897Sad 			kmem_free(listener, sizeof(*listener));
79333b0a10dSelad 
79433b0a10dSelad 		return (NULL);
79533b0a10dSelad 	}
79633b0a10dSelad 
79733b0a10dSelad 	/* Initialize new scope with parameters */
79833b0a10dSelad 	scope->id = id;
79933b0a10dSelad 	scope->cookie = cookie;
80033b0a10dSelad 	scope->nlisteners = 1;
80133b0a10dSelad 
802c07e4988Schristos 	SIMPLEQ_INIT(&scope->listenq);
803c07e4988Schristos 
80433b0a10dSelad 	/* Add default listener */
805c07e4988Schristos 	if (callback != NULL) {
80633b0a10dSelad 		listener->func = callback;
80733b0a10dSelad 		listener->scope = scope;
80833b0a10dSelad 		listener->refcnt = 0;
80933b0a10dSelad 		SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next);
810c07e4988Schristos 	}
81133b0a10dSelad 
81233b0a10dSelad 	/* Insert scope to scopes list */
81333b0a10dSelad 	SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope);
81433b0a10dSelad 
815b07ec3fcSad 	rw_exit(&kauth_lock);
81633b0a10dSelad 
81733b0a10dSelad 	return (scope);
81833b0a10dSelad }
81933b0a10dSelad 
82033b0a10dSelad /*
82133b0a10dSelad  * Initialize the kernel authorization subsystem.
82233b0a10dSelad  *
82333b0a10dSelad  * Initialize the scopes list lock.
824c439bcfeSelad  * Create specificdata domain.
825c439bcfeSelad  * Register the credentials scope, used in kauth(9) internally.
826c439bcfeSelad  * Register built-in scopes: generic, system, process, network, machdep, device.
82733b0a10dSelad  */
82833b0a10dSelad void
kauth_init(void)82933b0a10dSelad kauth_init(void)
83033b0a10dSelad {
831b07ec3fcSad 	rw_init(&kauth_lock);
83233b0a10dSelad 
833d18c6ca4Sad 	kauth_cred_cache = pool_cache_init(sizeof(struct kauth_cred),
834feb4783fSad 	    coherency_unit, 0, 0, "kcredpl", NULL, IPL_NONE,
8357ec1c5e6Sad 	    NULL, NULL, NULL);
836d18c6ca4Sad 
837c439bcfeSelad 	/* Create specificdata domain. */
838c439bcfeSelad 	kauth_domain = specificdata_domain_create();
839c439bcfeSelad 
840c439bcfeSelad 	/* Register credentials scope. */
841c439bcfeSelad 	kauth_builtin_scope_cred =
842c439bcfeSelad 	    kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL);
843c439bcfeSelad 
84433b0a10dSelad 	/* Register generic scope. */
84533b0a10dSelad 	kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC,
8465f7169ccSelad 	    NULL, NULL);
8475f7169ccSelad 
8485f7169ccSelad 	/* Register system scope. */
8495f7169ccSelad 	kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM,
8505f7169ccSelad 	    NULL, NULL);
85133b0a10dSelad 
85233b0a10dSelad 	/* Register process scope. */
85333b0a10dSelad 	kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS,
8545f7169ccSelad 	    NULL, NULL);
8555f7169ccSelad 
8565f7169ccSelad 	/* Register network scope. */
8575f7169ccSelad 	kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK,
8585f7169ccSelad 	    NULL, NULL);
8595f7169ccSelad 
8605f7169ccSelad 	/* Register machdep scope. */
8615f7169ccSelad 	kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP,
8625f7169ccSelad 	    NULL, NULL);
863b8a33934Selad 
864b8a33934Selad 	/* Register device scope. */
865b8a33934Selad 	kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE,
866b8a33934Selad 	    NULL, NULL);
867a1621401Selad 
868a1621401Selad 	/* Register vnode scope. */
869a1621401Selad 	kauth_builtin_scope_vnode = kauth_register_scope(KAUTH_SCOPE_VNODE,
870a1621401Selad 	    NULL, NULL);
87133b0a10dSelad }
87233b0a10dSelad 
87333b0a10dSelad /*
87433b0a10dSelad  * Deregister a scope.
87533b0a10dSelad  * Requires scope list lock to be held by the caller.
87633b0a10dSelad  *
87733b0a10dSelad  * scope - the scope to deregister
87833b0a10dSelad  */
87933b0a10dSelad void
kauth_deregister_scope(kauth_scope_t scope)88033b0a10dSelad kauth_deregister_scope(kauth_scope_t scope)
88133b0a10dSelad {
88233b0a10dSelad 	if (scope != NULL) {
88333b0a10dSelad 		/* Remove scope from list */
88433b0a10dSelad 		SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope);
885c6e8423fSelad 		kmem_free(scope, sizeof(*scope));
88633b0a10dSelad 	}
88733b0a10dSelad }
88833b0a10dSelad 
88933b0a10dSelad /*
89033b0a10dSelad  * Register a listener.
89133b0a10dSelad  *
89233b0a10dSelad  * id - scope identifier.
89333b0a10dSelad  * callback - the callback routine for the listener.
89433b0a10dSelad  * cookie - cookie to pass unmoidfied to the callback.
89533b0a10dSelad  */
89633b0a10dSelad kauth_listener_t
kauth_listen_scope(const char * id,kauth_scope_callback_t callback,void * cookie)89733b0a10dSelad kauth_listen_scope(const char *id, kauth_scope_callback_t callback,
8981a7bc55dSyamt    void *cookie)
89933b0a10dSelad {
90033b0a10dSelad 	kauth_scope_t scope;
90133b0a10dSelad 	kauth_listener_t listener;
90233b0a10dSelad 
903d635d897Sad 	listener = kmem_alloc(sizeof(*listener), KM_SLEEP);
904b07ec3fcSad 	rw_enter(&kauth_lock, RW_WRITER);
905b07ec3fcSad 
906b07ec3fcSad 	/*
907b07ec3fcSad 	 * Find scope struct.
908b07ec3fcSad 	 */
909b07ec3fcSad 	scope = kauth_ifindscope(id);
910b07ec3fcSad 	if (scope == NULL) {
911b07ec3fcSad 		rw_exit(&kauth_lock);
912b07ec3fcSad 		kmem_free(listener, sizeof(*listener));
913b07ec3fcSad 		return (NULL);
914b07ec3fcSad 	}
915b07ec3fcSad 
916b07ec3fcSad 	/* Allocate listener */
917b07ec3fcSad 
91833b0a10dSelad 	/* Initialize listener with parameters */
91933b0a10dSelad 	listener->func = callback;
92033b0a10dSelad 	listener->refcnt = 0;
92133b0a10dSelad 
92233b0a10dSelad 	/* Add listener to scope */
92333b0a10dSelad 	SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next);
92433b0a10dSelad 
92533b0a10dSelad 	/* Raise number of listeners on scope. */
92633b0a10dSelad 	scope->nlisteners++;
92733b0a10dSelad 	listener->scope = scope;
92833b0a10dSelad 
929b07ec3fcSad 	rw_exit(&kauth_lock);
930b07ec3fcSad 
93133b0a10dSelad 	return (listener);
93233b0a10dSelad }
93333b0a10dSelad 
93433b0a10dSelad /*
93533b0a10dSelad  * Deregister a listener.
93633b0a10dSelad  *
93733b0a10dSelad  * listener - listener reference as returned from kauth_listen_scope().
93833b0a10dSelad  */
93933b0a10dSelad void
kauth_unlisten_scope(kauth_listener_t listener)94033b0a10dSelad kauth_unlisten_scope(kauth_listener_t listener)
94133b0a10dSelad {
942b07ec3fcSad 
94333b0a10dSelad 	if (listener != NULL) {
944b07ec3fcSad 		rw_enter(&kauth_lock, RW_WRITER);
945be46b8e4Syamt 		SIMPLEQ_REMOVE(&listener->scope->listenq, listener,
946be46b8e4Syamt 		    kauth_listener, listener_next);
94733b0a10dSelad 		listener->scope->nlisteners--;
948b07ec3fcSad 		rw_exit(&kauth_lock);
949c6e8423fSelad 		kmem_free(listener, sizeof(*listener));
95033b0a10dSelad 	}
95133b0a10dSelad }
95233b0a10dSelad 
95333b0a10dSelad /*
95433b0a10dSelad  * Authorize a request.
95533b0a10dSelad  *
95633b0a10dSelad  * scope - the scope of the request as defined by KAUTH_SCOPE_* or as
95733b0a10dSelad  *	   returned from kauth_register_scope().
95833b0a10dSelad  * credential - credentials of the user ("actor") making the request.
95933b0a10dSelad  * action - request identifier.
96033b0a10dSelad  * arg[0-3] - passed unmodified to listener(s).
961a1621401Selad  *
962a1621401Selad  * Returns the aggregated result:
963a1621401Selad  *     - KAUTH_RESULT_ALLOW if there is at least one KAUTH_RESULT_ALLOW and
964a1621401Selad  *       zero KAUTH_DESULT_DENY
965a1621401Selad  *     - KAUTH_RESULT_DENY if there is at least one KAUTH_RESULT_DENY
966a1621401Selad  *     - KAUTH_RESULT_DEFER if there is nothing but KAUTH_RESULT_DEFER
96733b0a10dSelad  */
968a1621401Selad 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)969a1621401Selad kauth_authorize_action_internal(kauth_scope_t scope, kauth_cred_t cred,
970a1621401Selad     kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3)
97133b0a10dSelad {
97233b0a10dSelad 	kauth_listener_t listener;
97333b0a10dSelad 	int error, allow, fail;
97433b0a10dSelad 
975f0c7040aSelad 	KASSERT(cred != NULL);
976f0c7040aSelad 	KASSERT(action != 0);
97733b0a10dSelad 
978cba8e50fSchristos 	/* Short-circuit requests coming from the kernel. */
979cba8e50fSchristos 	if (cred == NOCRED || cred == FSCRED)
98040dfd1a8Schristos 		return KAUTH_RESULT_ALLOW;
981cba8e50fSchristos 
982f0c7040aSelad 	KASSERT(scope != NULL);
983f0c7040aSelad 
98433b0a10dSelad 	fail = 0;
98533b0a10dSelad 	allow = 0;
986f3cc348dSelad 
987b07ec3fcSad 	/* rw_enter(&kauth_lock, RW_READER); XXX not yet */
98833b0a10dSelad 	SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) {
98933b0a10dSelad 		error = listener->func(cred, action, scope->cookie, arg0,
99033b0a10dSelad 		    arg1, arg2, arg3);
99133b0a10dSelad 
99233b0a10dSelad 		if (error == KAUTH_RESULT_ALLOW)
99333b0a10dSelad 			allow = 1;
99433b0a10dSelad 		else if (error == KAUTH_RESULT_DENY)
99533b0a10dSelad 			fail = 1;
99633b0a10dSelad 	}
997b07ec3fcSad 	/* rw_exit(&kauth_lock); */
99833b0a10dSelad 
999f3cc348dSelad 	if (fail)
1000a1621401Selad 		return (KAUTH_RESULT_DENY);
1001f3cc348dSelad 
1002f3cc348dSelad 	if (allow)
1003a1621401Selad 		return (KAUTH_RESULT_ALLOW);
1004a1621401Selad 
1005a1621401Selad 	return (KAUTH_RESULT_DEFER);
1006a1621401Selad };
1007a1621401Selad 
1008a1621401Selad int
kauth_authorize_action(kauth_scope_t scope,kauth_cred_t cred,kauth_action_t action,void * arg0,void * arg1,void * arg2,void * arg3)1009a1621401Selad kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred,
1010a1621401Selad     kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3)
1011a1621401Selad {
1012a1621401Selad 	int r;
1013a1621401Selad 
1014a1621401Selad 	r = kauth_authorize_action_internal(scope, cred, action, arg0, arg1,
1015a1621401Selad 	    arg2, arg3);
1016a1621401Selad 
1017a1621401Selad 	if (r == KAUTH_RESULT_DENY)
1018a1621401Selad 		return (EPERM);
1019a1621401Selad 
1020a1621401Selad 	if (r == KAUTH_RESULT_ALLOW)
1021f3cc348dSelad 		return (0);
1022f3cc348dSelad 
10236519e39dSjym 	if (secmodel_nsecmodels() == 0)
1024f3cc348dSelad 		return (0);
1025f3cc348dSelad 
1026f3cc348dSelad 	return (EPERM);
1027a1621401Selad }
102833b0a10dSelad 
102933b0a10dSelad /*
103033b0a10dSelad  * Generic scope authorization wrapper.
103133b0a10dSelad  */
103233b0a10dSelad int
kauth_authorize_generic(kauth_cred_t cred,kauth_action_t action,void * arg0)103333b0a10dSelad kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0)
103433b0a10dSelad {
103533b0a10dSelad 	return (kauth_authorize_action(kauth_builtin_scope_generic, cred,
103633b0a10dSelad 	    action, arg0, NULL, NULL, NULL));
103733b0a10dSelad }
103833b0a10dSelad 
103933b0a10dSelad /*
10405f7169ccSelad  * System scope authorization wrapper.
104133b0a10dSelad  */
104233b0a10dSelad int
kauth_authorize_system(kauth_cred_t cred,kauth_action_t action,enum kauth_system_req req,void * arg1,void * arg2,void * arg3)10435f7169ccSelad kauth_authorize_system(kauth_cred_t cred, kauth_action_t action,
10445f7169ccSelad     enum kauth_system_req req, void *arg1, void *arg2, void *arg3)
104533b0a10dSelad {
10465f7169ccSelad 	return (kauth_authorize_action(kauth_builtin_scope_system, cred,
10475f7169ccSelad 	    action, (void *)req, arg1, arg2, arg3));
104833b0a10dSelad }
104933b0a10dSelad 
105033b0a10dSelad /*
105133b0a10dSelad  * Process scope authorization wrapper.
105233b0a10dSelad  */
105333b0a10dSelad int
kauth_authorize_process(kauth_cred_t cred,kauth_action_t action,struct proc * p,void * arg1,void * arg2,void * arg3)105433b0a10dSelad kauth_authorize_process(kauth_cred_t cred, kauth_action_t action,
105533b0a10dSelad     struct proc *p, void *arg1, void *arg2, void *arg3)
105633b0a10dSelad {
105733b0a10dSelad 	return (kauth_authorize_action(kauth_builtin_scope_process, cred,
105833b0a10dSelad 	    action, p, arg1, arg2, arg3));
105933b0a10dSelad }
10605f7169ccSelad 
10615f7169ccSelad /*
10625f7169ccSelad  * Network scope authorization wrapper.
10635f7169ccSelad  */
10645f7169ccSelad int
kauth_authorize_network(kauth_cred_t cred,kauth_action_t action,enum kauth_network_req req,void * arg1,void * arg2,void * arg3)10655f7169ccSelad kauth_authorize_network(kauth_cred_t cred, kauth_action_t action,
106683a5239bSelad     enum kauth_network_req req, void *arg1, void *arg2, void *arg3)
10675f7169ccSelad {
10685f7169ccSelad 	return (kauth_authorize_action(kauth_builtin_scope_network, cred,
106983a5239bSelad 	    action, (void *)req, arg1, arg2, arg3));
10705f7169ccSelad }
10715f7169ccSelad 
10725f7169ccSelad int
kauth_authorize_machdep(kauth_cred_t cred,kauth_action_t action,void * arg0,void * arg1,void * arg2,void * arg3)10735f7169ccSelad kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action,
1074504c71d9Selad     void *arg0, void *arg1, void *arg2, void *arg3)
10755f7169ccSelad {
10765f7169ccSelad 	return (kauth_authorize_action(kauth_builtin_scope_machdep, cred,
1077504c71d9Selad 	    action, arg0, arg1, arg2, arg3));
10785f7169ccSelad }
1079b8a33934Selad 
1080b8a33934Selad int
kauth_authorize_device(kauth_cred_t cred,kauth_action_t action,void * arg0,void * arg1,void * arg2,void * arg3)10812db3a96bSelad kauth_authorize_device(kauth_cred_t cred, kauth_action_t action,
10822db3a96bSelad     void *arg0, void *arg1, void *arg2, void *arg3)
10832db3a96bSelad {
10842db3a96bSelad 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
10852db3a96bSelad 	    action, arg0, arg1, arg2, arg3));
10862db3a96bSelad }
10872db3a96bSelad 
10882db3a96bSelad int
kauth_authorize_device_tty(kauth_cred_t cred,kauth_action_t action,struct tty * tty)1089b8a33934Selad kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action,
1090b8a33934Selad     struct tty *tty)
1091b8a33934Selad {
1092b8a33934Selad 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1093b8a33934Selad 	    action, tty, NULL, NULL, NULL));
1094b8a33934Selad }
1095fe9e2303Selad 
1096fe9e2303Selad int
kauth_authorize_device_spec(kauth_cred_t cred,enum kauth_device_req req,struct vnode * vp)1097fe9e2303Selad kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req,
1098fe9e2303Selad     struct vnode *vp)
1099fe9e2303Selad {
1100fe9e2303Selad 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1101fe9e2303Selad 	    KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL));
1102fe9e2303Selad }
1103fe9e2303Selad 
1104fe9e2303Selad int
kauth_authorize_device_passthru(kauth_cred_t cred,dev_t dev,u_long bits,void * data)1105432c3099Selad kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits,
1106432c3099Selad     void *data)
1107fe9e2303Selad {
1108fe9e2303Selad 	return (kauth_authorize_action(kauth_builtin_scope_device, cred,
1109432c3099Selad 	    KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev,
1110432c3099Selad 	    data, NULL));
1111fe9e2303Selad }
1112f3cc348dSelad 
1113a1621401Selad kauth_action_t
kauth_accmode_to_action(accmode_t accmode)1114b9c1fd7fSchristos kauth_accmode_to_action(accmode_t accmode)
1115a1621401Selad {
1116a1621401Selad 	kauth_action_t action = 0;
1117a1621401Selad 
1118b9c1fd7fSchristos 	// XXX: Revisit we need to have a richer set of kauth primitives
1119b9c1fd7fSchristos 	// We also get only the Unix perms here sometimes
1120b9c1fd7fSchristos 	if (accmode & (VSTAT_PERMS|VREAD))
1121a1621401Selad 		action |= KAUTH_VNODE_READ_DATA;
1122b9c1fd7fSchristos 	if (accmode & (VMODIFY_PERMS|VADMIN_PERMS))
1123a1621401Selad 		action |= KAUTH_VNODE_WRITE_DATA;
1124b9c1fd7fSchristos 	if (accmode & VEXEC)
1125a1621401Selad 		action |= KAUTH_VNODE_EXECUTE;
1126b9c1fd7fSchristos 	return action == 0 ? KAUTH_VNODE_ACCESS : action;
1127a1621401Selad }
1128a1621401Selad 
1129bd26c726Selad kauth_action_t
kauth_extattr_action(mode_t access_mode)1130bd26c726Selad kauth_extattr_action(mode_t access_mode)
1131bd26c726Selad {
1132bd26c726Selad 	kauth_action_t action = 0;
1133bd26c726Selad 
1134bd26c726Selad 	if (access_mode & VREAD)
1135bd26c726Selad 		action |= KAUTH_VNODE_READ_EXTATTRIBUTES;
1136bd26c726Selad 	if (access_mode & VWRITE)
1137bd26c726Selad 		action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
1138bd26c726Selad 
1139bd26c726Selad 	return action;
1140bd26c726Selad }
1141bd26c726Selad 
1142a1621401Selad int
kauth_authorize_vnode(kauth_cred_t cred,kauth_action_t action,struct vnode * vp,struct vnode * dvp,int fs_decision)1143a1621401Selad kauth_authorize_vnode(kauth_cred_t cred, kauth_action_t action,
1144a1621401Selad     struct vnode *vp, struct vnode *dvp, int fs_decision)
1145a1621401Selad {
1146a1621401Selad 	int error;
1147a1621401Selad 
1148a1621401Selad 	error = kauth_authorize_action_internal(kauth_builtin_scope_vnode, cred,
1149a1621401Selad 	    action, vp, dvp, NULL, NULL);
1150a1621401Selad 
1151a1621401Selad 	if (error == KAUTH_RESULT_DENY)
1152a1621401Selad 		return (EACCES);
1153a1621401Selad 
1154a1621401Selad 	if (error == KAUTH_RESULT_ALLOW)
1155a1621401Selad 		return (0);
1156a1621401Selad 
1157a1621401Selad 	/*
1158a1621401Selad 	 * If the file-system does not support decision-before-action, we can
1159a1621401Selad 	 * only short-circuit the operation (deny). If we're here, it means no
1160a1621401Selad 	 * listener denied it, so our only alternative is to supposedly-allow
1161a1621401Selad 	 * it and let the file-system have the last word.
1162a1621401Selad 	 */
1163a1621401Selad 	if (fs_decision == KAUTH_VNODE_REMOTEFS)
1164a1621401Selad 		return (0);
1165a1621401Selad 
1166a1621401Selad 	return (fs_decision);
1167a1621401Selad }
1168a1621401Selad 
1169c439bcfeSelad static int
kauth_cred_hook(kauth_cred_t cred,kauth_action_t action,void * arg0,void * arg1)1170c439bcfeSelad kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0,
1171c439bcfeSelad     void *arg1)
1172c439bcfeSelad {
1173c439bcfeSelad 	int r;
1174c439bcfeSelad 
1175c439bcfeSelad 	r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action,
1176c439bcfeSelad 	    arg0, arg1, NULL, NULL);
1177c439bcfeSelad 
117885f51576Selad #ifdef DIAGNOSTIC
117985f51576Selad 	if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq))
1180c439bcfeSelad 		KASSERT(r == 0);
118185f51576Selad #endif /* DIAGNOSTIC */
1182c439bcfeSelad 
1183c439bcfeSelad 	return (r);
1184c439bcfeSelad }
1185