xref: /freebsd-src/sys/security/mac_bsdextended/mac_bsdextended.c (revision db33c6f3ae9d1231087710068ee4ea5398aacca7)
1d8a7b7a3SRobert Watson /*-
2a1b9471aSRobert Watson  * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson
3a203d978STom Rhodes  * Copyright (c) 2001-2005 Networks Associates Technology, Inc.
4458f818fSRobert Watson  * Copyright (c) 2005 Tom Rhodes
530d239bcSRobert Watson  * Copyright (c) 2006 SPARTA, Inc.
6d8a7b7a3SRobert Watson  * All rights reserved.
7d8a7b7a3SRobert Watson  *
8d8a7b7a3SRobert Watson  * This software was developed by Robert Watson for the TrustedBSD Project.
9a203d978STom Rhodes  * It was later enhanced by Tom Rhodes for the TrustedBSD Project.
10d8a7b7a3SRobert Watson  *
11dc858fcaSRobert Watson  * This software was developed for the FreeBSD Project in part by Network
12dc858fcaSRobert Watson  * Associates Laboratories, the Security Research Division of Network
13dc858fcaSRobert Watson  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
14dc858fcaSRobert Watson  * as part of the DARPA CHATS research program.
15d8a7b7a3SRobert Watson  *
1630d239bcSRobert Watson  * This software was enhanced by SPARTA ISSO under SPAWAR contract
1730d239bcSRobert Watson  * N66001-04-C-6019 ("SEFOS").
1830d239bcSRobert Watson  *
19d8a7b7a3SRobert Watson  * Redistribution and use in source and binary forms, with or without
20d8a7b7a3SRobert Watson  * modification, are permitted provided that the following conditions
21d8a7b7a3SRobert Watson  * are met:
22d8a7b7a3SRobert Watson  * 1. Redistributions of source code must retain the above copyright
23d8a7b7a3SRobert Watson  *    notice, this list of conditions and the following disclaimer.
24d8a7b7a3SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
25d8a7b7a3SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
26d8a7b7a3SRobert Watson  *    documentation and/or other materials provided with the distribution.
27d8a7b7a3SRobert Watson  *
28d8a7b7a3SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29d8a7b7a3SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30d8a7b7a3SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31d8a7b7a3SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32d8a7b7a3SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33d8a7b7a3SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34d8a7b7a3SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35d8a7b7a3SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36d8a7b7a3SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37d8a7b7a3SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38d8a7b7a3SRobert Watson  * SUCH DAMAGE.
39d8a7b7a3SRobert Watson  */
4046e23372SRobert Watson 
41d8a7b7a3SRobert Watson /*
42d8a7b7a3SRobert Watson  * Developed by the TrustedBSD Project.
43458f818fSRobert Watson  *
44458f818fSRobert Watson  * "BSD Extended" MAC policy, allowing the administrator to impose mandatory
45458f818fSRobert Watson  * firewall-like rules regarding users and file system objects.
46d8a7b7a3SRobert Watson  */
47d8a7b7a3SRobert Watson 
48d8a7b7a3SRobert Watson #include <sys/param.h>
49d8a7b7a3SRobert Watson #include <sys/acl.h>
50d8a7b7a3SRobert Watson #include <sys/kernel.h>
5189ddbd45SDavid Malone #include <sys/jail.h>
52a203d978STom Rhodes #include <sys/lock.h>
53d8a7b7a3SRobert Watson #include <sys/malloc.h>
547405fcc3SRobert Watson #include <sys/module.h>
55d8a7b7a3SRobert Watson #include <sys/mount.h>
56a203d978STom Rhodes #include <sys/mutex.h>
57c2259ba4SRobert Watson #include <sys/priv.h>
58413628a7SBjoern A. Zeeb #include <sys/proc.h>
59d8a7b7a3SRobert Watson #include <sys/systm.h>
60d8a7b7a3SRobert Watson #include <sys/vnode.h>
61d8a7b7a3SRobert Watson #include <sys/sysctl.h>
6260673f35STom Rhodes #include <sys/syslog.h>
63dfa7fd1dSEdward Tomasz Napierala #include <sys/stat.h>
64d8a7b7a3SRobert Watson 
650efd6615SRobert Watson #include <security/mac/mac_policy.h>
66d8a7b7a3SRobert Watson #include <security/mac_bsdextended/mac_bsdextended.h>
6734f6230eSRobert Watson #include <security/mac_bsdextended/ugidfw_internal.h>
68d8a7b7a3SRobert Watson 
693f1a7a90SRobert Watson static struct mtx ugidfw_mtx;
70a203d978STom Rhodes 
717029da5cSPawel Biernacki static SYSCTL_NODE(_security_mac, OID_AUTO, bsdextended,
727029da5cSPawel Biernacki     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
73d8a7b7a3SRobert Watson     "TrustedBSD extended BSD MAC policy controls");
74d8a7b7a3SRobert Watson 
753f1a7a90SRobert Watson static int	ugidfw_enabled = 1;
76af3b2549SHans Petter Selasky SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, enabled, CTLFLAG_RWTUN,
773f1a7a90SRobert Watson     &ugidfw_enabled, 0, "Enforce extended BSD policy");
78d8a7b7a3SRobert Watson 
79d745c852SEd Schouten static MALLOC_DEFINE(M_MACBSDEXTENDED, "mac_bsdextended",
80d745c852SEd Schouten     "BSD Extended MAC rule");
81d8a7b7a3SRobert Watson 
82d8a7b7a3SRobert Watson #define	MAC_BSDEXTENDED_MAXRULES	250
83d8a7b7a3SRobert Watson static struct mac_bsdextended_rule *rules[MAC_BSDEXTENDED_MAXRULES];
84d8a7b7a3SRobert Watson static int rule_count = 0;
85d8a7b7a3SRobert Watson static int rule_slots = 0;
8689ddbd45SDavid Malone static int rule_version = MB_VERSION;
87d8a7b7a3SRobert Watson 
88d8a7b7a3SRobert Watson SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_count, CTLFLAG_RD,
89*6b62e00dSEd Maste     &rule_count, 0, "Number of defined rules");
90d8a7b7a3SRobert Watson SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_slots, CTLFLAG_RD,
91*6b62e00dSEd Maste     &rule_slots, 0, "Number of used rule slots");
9289ddbd45SDavid Malone SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_version, CTLFLAG_RD,
93*6b62e00dSEd Maste     &rule_version, 0, "Version number for API");
94d8a7b7a3SRobert Watson 
9560673f35STom Rhodes /*
96458f818fSRobert Watson  * This is just used for logging purposes, eventually we would like to log
97458f818fSRobert Watson  * much more then failed requests.
9860673f35STom Rhodes  */
993f1a7a90SRobert Watson static int ugidfw_logging;
10060673f35STom Rhodes SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, logging, CTLFLAG_RW,
1013f1a7a90SRobert Watson     &ugidfw_logging, 0, "Log failed authorization requests");
10260673f35STom Rhodes 
10360673f35STom Rhodes /*
104458f818fSRobert Watson  * This tunable is here for compatibility.  It will allow the user to switch
105458f818fSRobert Watson  * between the new mode (first rule matches) and the old functionality (all
106458f818fSRobert Watson  * rules match).
107fa31f180STom Rhodes  */
1083f1a7a90SRobert Watson static int ugidfw_firstmatch_enabled;
109fa31f180STom Rhodes SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, firstmatch_enabled,
1103f1a7a90SRobert Watson     CTLFLAG_RW, &ugidfw_firstmatch_enabled, 1,
111fa31f180STom Rhodes     "Disable/enable match first rule functionality");
112fa31f180STom Rhodes 
113d8a7b7a3SRobert Watson static int
1143f1a7a90SRobert Watson ugidfw_rule_valid(struct mac_bsdextended_rule *rule)
115d8a7b7a3SRobert Watson {
116d8a7b7a3SRobert Watson 
11789ddbd45SDavid Malone 	if ((rule->mbr_subject.mbs_flags | MBS_ALL_FLAGS) != MBS_ALL_FLAGS)
118d8a7b7a3SRobert Watson 		return (EINVAL);
11989ddbd45SDavid Malone 	if ((rule->mbr_subject.mbs_neg | MBS_ALL_FLAGS) != MBS_ALL_FLAGS)
12089ddbd45SDavid Malone 		return (EINVAL);
12189ddbd45SDavid Malone 	if ((rule->mbr_object.mbo_flags | MBO_ALL_FLAGS) != MBO_ALL_FLAGS)
12289ddbd45SDavid Malone 		return (EINVAL);
12389ddbd45SDavid Malone 	if ((rule->mbr_object.mbo_neg | MBO_ALL_FLAGS) != MBO_ALL_FLAGS)
12489ddbd45SDavid Malone 		return (EINVAL);
125993114ddSEd Maste 	if (((rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) != 0) &&
12689ddbd45SDavid Malone 	    (rule->mbr_object.mbo_type | MBO_ALL_TYPE) != MBO_ALL_TYPE)
127d8a7b7a3SRobert Watson 		return (EINVAL);
1282e74bca1SRobert Watson 	if ((rule->mbr_mode | MBI_ALLPERM) != MBI_ALLPERM)
129d8a7b7a3SRobert Watson 		return (EINVAL);
130d8a7b7a3SRobert Watson 	return (0);
131d8a7b7a3SRobert Watson }
132d8a7b7a3SRobert Watson 
133d8a7b7a3SRobert Watson static int
134d8a7b7a3SRobert Watson sysctl_rule(SYSCTL_HANDLER_ARGS)
135d8a7b7a3SRobert Watson {
136d8a7b7a3SRobert Watson 	struct mac_bsdextended_rule temprule, *ruleptr;
137d8a7b7a3SRobert Watson 	u_int namelen;
138d8a7b7a3SRobert Watson 	int error, index, *name;
139d8a7b7a3SRobert Watson 
140a203d978STom Rhodes 	error = 0;
141d8a7b7a3SRobert Watson 	name = (int *)arg1;
142d8a7b7a3SRobert Watson 	namelen = arg2;
143d8a7b7a3SRobert Watson 	if (namelen != 1)
144d8a7b7a3SRobert Watson 		return (EINVAL);
145d8a7b7a3SRobert Watson 	index = name[0];
146e1216740SChristian S.J. Peron         if (index >= MAC_BSDEXTENDED_MAXRULES)
147d8a7b7a3SRobert Watson 		return (ENOENT);
148d8a7b7a3SRobert Watson 
149a203d978STom Rhodes 	ruleptr = NULL;
150a203d978STom Rhodes 	if (req->newptr && req->newlen != 0) {
151d8a7b7a3SRobert Watson 		error = SYSCTL_IN(req, &temprule, sizeof(temprule));
152d8a7b7a3SRobert Watson 		if (error)
153d8a7b7a3SRobert Watson 			return (error);
154e11e3f18SDag-Erling Smørgrav 		ruleptr = malloc(sizeof(*ruleptr), M_MACBSDEXTENDED,
155e11e3f18SDag-Erling Smørgrav 		    M_WAITOK | M_ZERO);
156a203d978STom Rhodes 	}
157d8a7b7a3SRobert Watson 
1583f1a7a90SRobert Watson 	mtx_lock(&ugidfw_mtx);
159a203d978STom Rhodes 	if (req->oldptr) {
160a203d978STom Rhodes 		if (index < 0 || index > rule_slots + 1) {
161a203d978STom Rhodes 			error = ENOENT;
162a203d978STom Rhodes 			goto out;
163a203d978STom Rhodes 		}
164a203d978STom Rhodes 		if (rules[index] == NULL) {
165a203d978STom Rhodes 			error = ENOENT;
166a203d978STom Rhodes 			goto out;
167a203d978STom Rhodes 		}
168a203d978STom Rhodes 		temprule = *rules[index];
169a203d978STom Rhodes 	}
170a203d978STom Rhodes 	if (req->newptr && req->newlen == 0) {
171a203d978STom Rhodes 		KASSERT(ruleptr == NULL, ("sysctl_rule: ruleptr != NULL"));
172a203d978STom Rhodes 		ruleptr = rules[index];
173a203d978STom Rhodes 		if (ruleptr == NULL) {
174a203d978STom Rhodes 			error = ENOENT;
175a203d978STom Rhodes 			goto out;
176a203d978STom Rhodes 		}
177a203d978STom Rhodes 		rule_count--;
178a203d978STom Rhodes 		rules[index] = NULL;
179a203d978STom Rhodes 	} else if (req->newptr) {
1803f1a7a90SRobert Watson 		error = ugidfw_rule_valid(&temprule);
181d8a7b7a3SRobert Watson 		if (error)
182a203d978STom Rhodes 			goto out;
183d8a7b7a3SRobert Watson 		if (rules[index] == NULL) {
184d8a7b7a3SRobert Watson 			*ruleptr = temprule;
185d8a7b7a3SRobert Watson 			rules[index] = ruleptr;
186a203d978STom Rhodes 			ruleptr = NULL;
187d8a7b7a3SRobert Watson 			if (index + 1 > rule_slots)
188d8a7b7a3SRobert Watson 				rule_slots = index + 1;
189d8a7b7a3SRobert Watson 			rule_count++;
190458f818fSRobert Watson 		} else
191d8a7b7a3SRobert Watson 			*rules[index] = temprule;
192d8a7b7a3SRobert Watson 	}
193a203d978STom Rhodes out:
1943f1a7a90SRobert Watson 	mtx_unlock(&ugidfw_mtx);
195a203d978STom Rhodes 	if (ruleptr != NULL)
1961ede983cSDag-Erling Smørgrav 		free(ruleptr, M_MACBSDEXTENDED);
197096dd406STom Rhodes 	if (req->oldptr && error == 0)
198a203d978STom Rhodes 		error = SYSCTL_OUT(req, &temprule, sizeof(temprule));
199096dd406STom Rhodes 	return (error);
200d8a7b7a3SRobert Watson }
201d8a7b7a3SRobert Watson 
2026472ac3dSEd Schouten static SYSCTL_NODE(_security_mac_bsdextended, OID_AUTO, rules,
203095b4d26SChristian S.J. Peron     CTLFLAG_MPSAFE | CTLFLAG_RW, sysctl_rule, "BSD extended MAC rules");
204d8a7b7a3SRobert Watson 
205d8a7b7a3SRobert Watson static void
2063f1a7a90SRobert Watson ugidfw_init(struct mac_policy_conf *mpc)
207d8a7b7a3SRobert Watson {
208d8a7b7a3SRobert Watson 
2093f1a7a90SRobert Watson 	mtx_init(&ugidfw_mtx, "mac_bsdextended lock", NULL, MTX_DEF);
210d8a7b7a3SRobert Watson }
211d8a7b7a3SRobert Watson 
212d8a7b7a3SRobert Watson static void
2133f1a7a90SRobert Watson ugidfw_destroy(struct mac_policy_conf *mpc)
214d8a7b7a3SRobert Watson {
215168a6ae7SRobert Watson 	int i;
216d8a7b7a3SRobert Watson 
217168a6ae7SRobert Watson 	for (i = 0; i < MAC_BSDEXTENDED_MAXRULES; i++) {
218168a6ae7SRobert Watson 		if (rules[i] != NULL)
219168a6ae7SRobert Watson 			free(rules[i], M_MACBSDEXTENDED);
220168a6ae7SRobert Watson 	}
2213f1a7a90SRobert Watson 	mtx_destroy(&ugidfw_mtx);
222d8a7b7a3SRobert Watson }
223d8a7b7a3SRobert Watson 
224d8a7b7a3SRobert Watson static int
2253f1a7a90SRobert Watson ugidfw_rulecheck(struct mac_bsdextended_rule *rule,
22689ddbd45SDavid Malone     struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode)
227d8a7b7a3SRobert Watson {
228cecd8edbSAttilio Rao 	int mac_granted, match, priv_granted;
22989ddbd45SDavid Malone 	int i;
230d8a7b7a3SRobert Watson 
231d8a7b7a3SRobert Watson 	/*
232d8a7b7a3SRobert Watson 	 * Is there a subject match?
233d8a7b7a3SRobert Watson 	 */
2343f1a7a90SRobert Watson 	mtx_assert(&ugidfw_mtx, MA_OWNED);
23589ddbd45SDavid Malone 	if (rule->mbr_subject.mbs_flags & MBS_UID_DEFINED) {
23689ddbd45SDavid Malone 		match =  ((cred->cr_uid <= rule->mbr_subject.mbs_uid_max &&
23789ddbd45SDavid Malone 		    cred->cr_uid >= rule->mbr_subject.mbs_uid_min) ||
23889ddbd45SDavid Malone 		    (cred->cr_ruid <= rule->mbr_subject.mbs_uid_max &&
23989ddbd45SDavid Malone 		    cred->cr_ruid >= rule->mbr_subject.mbs_uid_min) ||
24089ddbd45SDavid Malone 		    (cred->cr_svuid <= rule->mbr_subject.mbs_uid_max &&
24189ddbd45SDavid Malone 		    cred->cr_svuid >= rule->mbr_subject.mbs_uid_min));
24289ddbd45SDavid Malone 		if (rule->mbr_subject.mbs_neg & MBS_UID_DEFINED)
243d8a7b7a3SRobert Watson 			match = !match;
244d8a7b7a3SRobert Watson 		if (!match)
245d8a7b7a3SRobert Watson 			return (0);
246d8a7b7a3SRobert Watson 	}
247d8a7b7a3SRobert Watson 
24889ddbd45SDavid Malone 	if (rule->mbr_subject.mbs_flags & MBS_GID_DEFINED) {
24989ddbd45SDavid Malone 		match = ((cred->cr_rgid <= rule->mbr_subject.mbs_gid_max &&
25089ddbd45SDavid Malone 		    cred->cr_rgid >= rule->mbr_subject.mbs_gid_min) ||
25189ddbd45SDavid Malone 		    (cred->cr_svgid <= rule->mbr_subject.mbs_gid_max &&
25289ddbd45SDavid Malone 		    cred->cr_svgid >= rule->mbr_subject.mbs_gid_min));
25389ddbd45SDavid Malone 		if (!match) {
254458f818fSRobert Watson 			for (i = 0; i < cred->cr_ngroups; i++) {
25589ddbd45SDavid Malone 				if (cred->cr_groups[i]
25689ddbd45SDavid Malone 				    <= rule->mbr_subject.mbs_gid_max &&
25789ddbd45SDavid Malone 				    cred->cr_groups[i]
25889ddbd45SDavid Malone 				    >= rule->mbr_subject.mbs_gid_min) {
25989ddbd45SDavid Malone 					match = 1;
26089ddbd45SDavid Malone 					break;
26189ddbd45SDavid Malone 				}
26289ddbd45SDavid Malone 			}
263458f818fSRobert Watson 		}
26489ddbd45SDavid Malone 		if (rule->mbr_subject.mbs_neg & MBS_GID_DEFINED)
26589ddbd45SDavid Malone 			match = !match;
26689ddbd45SDavid Malone 		if (!match)
26789ddbd45SDavid Malone 			return (0);
26889ddbd45SDavid Malone 	}
26989ddbd45SDavid Malone 
27089ddbd45SDavid Malone 	if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
2710304c731SJamie Gritton 		match =
2720304c731SJamie Gritton 		    (cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison);
27389ddbd45SDavid Malone 		if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)
274d8a7b7a3SRobert Watson 			match = !match;
275d8a7b7a3SRobert Watson 		if (!match)
276d8a7b7a3SRobert Watson 			return (0);
277d8a7b7a3SRobert Watson 	}
278d8a7b7a3SRobert Watson 
279d8a7b7a3SRobert Watson 	/*
280d8a7b7a3SRobert Watson 	 * Is there an object match?
281d8a7b7a3SRobert Watson 	 */
28289ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
28389ddbd45SDavid Malone 		match = (vap->va_uid <= rule->mbr_object.mbo_uid_max &&
28489ddbd45SDavid Malone 		    vap->va_uid >= rule->mbr_object.mbo_uid_min);
28589ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)
286d8a7b7a3SRobert Watson 			match = !match;
287d8a7b7a3SRobert Watson 		if (!match)
288d8a7b7a3SRobert Watson 			return (0);
289d8a7b7a3SRobert Watson 	}
290d8a7b7a3SRobert Watson 
29189ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
29289ddbd45SDavid Malone 		match = (vap->va_gid <= rule->mbr_object.mbo_gid_max &&
29389ddbd45SDavid Malone 		    vap->va_gid >= rule->mbr_object.mbo_gid_min);
29489ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)
295d8a7b7a3SRobert Watson 			match = !match;
296d8a7b7a3SRobert Watson 		if (!match)
297d8a7b7a3SRobert Watson 			return (0);
298d8a7b7a3SRobert Watson 	}
299d8a7b7a3SRobert Watson 
30089ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
301245bfd34SRyan Moeller 		match = (fsidcmp(&vp->v_mount->mnt_stat.f_fsid,
302245bfd34SRyan Moeller 		    &rule->mbr_object.mbo_fsid) == 0);
30389ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)
30489ddbd45SDavid Malone 			match = !match;
30589ddbd45SDavid Malone 		if (!match)
306458f818fSRobert Watson 			return (0);
30789ddbd45SDavid Malone 	}
30889ddbd45SDavid Malone 
30989ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_SUID) {
310dfa7fd1dSEdward Tomasz Napierala 		match = (vap->va_mode & S_ISUID);
31189ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_SUID)
31289ddbd45SDavid Malone 			match = !match;
31389ddbd45SDavid Malone 		if (!match)
314458f818fSRobert Watson 			return (0);
31589ddbd45SDavid Malone 	}
31689ddbd45SDavid Malone 
31789ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_SGID) {
318dfa7fd1dSEdward Tomasz Napierala 		match = (vap->va_mode & S_ISGID);
31989ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_SGID)
32089ddbd45SDavid Malone 			match = !match;
32189ddbd45SDavid Malone 		if (!match)
322458f818fSRobert Watson 			return (0);
32389ddbd45SDavid Malone 	}
32489ddbd45SDavid Malone 
32589ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
32689ddbd45SDavid Malone 		match = (vap->va_uid == cred->cr_uid ||
32789ddbd45SDavid Malone 		    vap->va_uid == cred->cr_ruid ||
32889ddbd45SDavid Malone 		    vap->va_uid == cred->cr_svuid);
32989ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)
33089ddbd45SDavid Malone 			match = !match;
33189ddbd45SDavid Malone 		if (!match)
332458f818fSRobert Watson 			return (0);
33389ddbd45SDavid Malone 	}
33489ddbd45SDavid Malone 
33589ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
33689ddbd45SDavid Malone 		match = (groupmember(vap->va_gid, cred) ||
33789ddbd45SDavid Malone 		    vap->va_gid == cred->cr_rgid ||
33889ddbd45SDavid Malone 		    vap->va_gid == cred->cr_svgid);
33989ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)
34089ddbd45SDavid Malone 			match = !match;
34189ddbd45SDavid Malone 		if (!match)
342458f818fSRobert Watson 			return (0);
34389ddbd45SDavid Malone 	}
34489ddbd45SDavid Malone 
34589ddbd45SDavid Malone 	if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
34689ddbd45SDavid Malone 		switch (vap->va_type) {
34789ddbd45SDavid Malone 		case VREG:
34889ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_REG);
34989ddbd45SDavid Malone 			break;
35089ddbd45SDavid Malone 		case VDIR:
35189ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_DIR);
35289ddbd45SDavid Malone 			break;
35389ddbd45SDavid Malone 		case VBLK:
35489ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_BLK);
35589ddbd45SDavid Malone 			break;
35689ddbd45SDavid Malone 		case VCHR:
35789ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_CHR);
35889ddbd45SDavid Malone 			break;
35989ddbd45SDavid Malone 		case VLNK:
36089ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_LNK);
36189ddbd45SDavid Malone 			break;
36289ddbd45SDavid Malone 		case VSOCK:
36389ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_SOCK);
36489ddbd45SDavid Malone 			break;
36589ddbd45SDavid Malone 		case VFIFO:
36689ddbd45SDavid Malone 			match = (rule->mbr_object.mbo_type & MBO_TYPE_FIFO);
36789ddbd45SDavid Malone 			break;
36889ddbd45SDavid Malone 		default:
36989ddbd45SDavid Malone 			match = 0;
37089ddbd45SDavid Malone 		}
37189ddbd45SDavid Malone 		if (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)
37289ddbd45SDavid Malone 			match = !match;
37389ddbd45SDavid Malone 		if (!match)
374458f818fSRobert Watson 			return (0);
37589ddbd45SDavid Malone 	}
37689ddbd45SDavid Malone 
377d8a7b7a3SRobert Watson 	/*
378cecd8edbSAttilio Rao 	 * MBI_APPEND should not be here as it should get converted to
379cecd8edbSAttilio Rao 	 * MBI_WRITE.
380cecd8edbSAttilio Rao 	 */
381cecd8edbSAttilio Rao 	priv_granted = 0;
382cecd8edbSAttilio Rao 	mac_granted = rule->mbr_mode;
383cecd8edbSAttilio Rao 	if ((acc_mode & MBI_ADMIN) && (mac_granted & MBI_ADMIN) == 0 &&
384cc426dd3SMateusz Guzik 	    priv_check_cred(cred, PRIV_VFS_ADMIN) == 0)
385cecd8edbSAttilio Rao 		priv_granted |= MBI_ADMIN;
386cecd8edbSAttilio Rao 	if ((acc_mode & MBI_EXEC) && (mac_granted & MBI_EXEC) == 0 &&
387cc426dd3SMateusz Guzik 	    priv_check_cred(cred, (vap->va_type == VDIR) ? PRIV_VFS_LOOKUP : PRIV_VFS_EXEC) == 0)
388cecd8edbSAttilio Rao 		priv_granted |= MBI_EXEC;
389cecd8edbSAttilio Rao 	if ((acc_mode & MBI_READ) && (mac_granted & MBI_READ) == 0 &&
390cc426dd3SMateusz Guzik 	    priv_check_cred(cred, PRIV_VFS_READ) == 0)
391cecd8edbSAttilio Rao 		priv_granted |= MBI_READ;
392cecd8edbSAttilio Rao 	if ((acc_mode & MBI_STAT) && (mac_granted & MBI_STAT) == 0 &&
393cc426dd3SMateusz Guzik 	    priv_check_cred(cred, PRIV_VFS_STAT) == 0)
394cecd8edbSAttilio Rao 		priv_granted |= MBI_STAT;
395cecd8edbSAttilio Rao 	if ((acc_mode & MBI_WRITE) && (mac_granted & MBI_WRITE) == 0 &&
396cc426dd3SMateusz Guzik 	    priv_check_cred(cred, PRIV_VFS_WRITE) == 0)
397cecd8edbSAttilio Rao 		priv_granted |= MBI_WRITE;
398cecd8edbSAttilio Rao 	/*
399d8a7b7a3SRobert Watson 	 * Is the access permitted?
400d8a7b7a3SRobert Watson 	 */
401cecd8edbSAttilio Rao 	if (((mac_granted | priv_granted) & acc_mode) != acc_mode) {
4023f1a7a90SRobert Watson 		if (ugidfw_logging)
40360673f35STom Rhodes 			log(LOG_AUTHPRIV, "mac_bsdextended: %d:%d request %d"
40460673f35STom Rhodes 			    " on %d:%d failed. \n", cred->cr_ruid,
405458f818fSRobert Watson 			    cred->cr_rgid, acc_mode, vap->va_uid,
406458f818fSRobert Watson 			    vap->va_gid);
407458f818fSRobert Watson 		return (EACCES);
408d8a7b7a3SRobert Watson 	}
409a203d978STom Rhodes 
410fa31f180STom Rhodes 	/*
411a203d978STom Rhodes 	 * If the rule matched, permits access, and first match is enabled,
412a203d978STom Rhodes 	 * return success.
413fa31f180STom Rhodes 	 */
4143f1a7a90SRobert Watson 	if (ugidfw_firstmatch_enabled)
415fa31f180STom Rhodes 		return (EJUSTRETURN);
416fa31f180STom Rhodes 	else
417d8a7b7a3SRobert Watson 		return (0);
418d8a7b7a3SRobert Watson }
419d8a7b7a3SRobert Watson 
42034f6230eSRobert Watson int
4213f1a7a90SRobert Watson ugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap,
422b914de36SRobert Watson     int acc_mode)
423d8a7b7a3SRobert Watson {
424d8a7b7a3SRobert Watson 	int error, i;
425d8a7b7a3SRobert Watson 
426acd3428bSRobert Watson 	/*
427d24c76d1SRobert Watson 	 * Since we do not separately handle append, map append to write.
4285bf93d25SRobert Watson 	 */
4292e74bca1SRobert Watson 	if (acc_mode & MBI_APPEND) {
4302e74bca1SRobert Watson 		acc_mode &= ~MBI_APPEND;
4312e74bca1SRobert Watson 		acc_mode |= MBI_WRITE;
4325bf93d25SRobert Watson 	}
4333f1a7a90SRobert Watson 	mtx_lock(&ugidfw_mtx);
434d24c76d1SRobert Watson 	for (i = 0; i < rule_slots; i++) {
435d24c76d1SRobert Watson 		if (rules[i] == NULL)
436d24c76d1SRobert Watson 			continue;
4373f1a7a90SRobert Watson 		error = ugidfw_rulecheck(rules[i], cred,
43889ddbd45SDavid Malone 		    vp, vap, acc_mode);
439fa31f180STom Rhodes 		if (error == EJUSTRETURN)
440fa31f180STom Rhodes 			break;
441a203d978STom Rhodes 		if (error) {
4423f1a7a90SRobert Watson 			mtx_unlock(&ugidfw_mtx);
443d8a7b7a3SRobert Watson 			return (error);
444d8a7b7a3SRobert Watson 		}
445a203d978STom Rhodes 	}
4463f1a7a90SRobert Watson 	mtx_unlock(&ugidfw_mtx);
447d8a7b7a3SRobert Watson 	return (0);
448d8a7b7a3SRobert Watson }
449d8a7b7a3SRobert Watson 
45034f6230eSRobert Watson int
4513f1a7a90SRobert Watson ugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode)
45296c33a0cSRobert Watson {
45396c33a0cSRobert Watson 	int error;
45442ae38e9SDavid Malone 	struct vattr vap;
45596c33a0cSRobert Watson 
4563f1a7a90SRobert Watson 	if (!ugidfw_enabled)
45796c33a0cSRobert Watson 		return (0);
4580359a12eSAttilio Rao 	error = VOP_GETATTR(vp, &vap, cred);
45996c33a0cSRobert Watson 	if (error)
46096c33a0cSRobert Watson 		return (error);
4613f1a7a90SRobert Watson 	return (ugidfw_check(cred, vp, &vap, acc_mode));
46242ae38e9SDavid Malone }
46342ae38e9SDavid Malone 
464a1b9471aSRobert Watson int
465a1b9471aSRobert Watson ugidfw_accmode2mbi(accmode_t accmode)
466a1b9471aSRobert Watson {
467a1b9471aSRobert Watson 	int mbi;
468a1b9471aSRobert Watson 
469a1b9471aSRobert Watson 	mbi = 0;
470a1b9471aSRobert Watson 	if (accmode & VEXEC)
471a1b9471aSRobert Watson 		mbi |= MBI_EXEC;
472a1b9471aSRobert Watson 	if (accmode & VWRITE)
473a1b9471aSRobert Watson 		mbi |= MBI_WRITE;
474a1b9471aSRobert Watson 	if (accmode & VREAD)
475a1b9471aSRobert Watson 		mbi |= MBI_READ;
4766180d318SEdward Tomasz Napierala 	if (accmode & VADMIN_PERMS)
477a1b9471aSRobert Watson 		mbi |= MBI_ADMIN;
4786180d318SEdward Tomasz Napierala 	if (accmode & VSTAT_PERMS)
479a1b9471aSRobert Watson 		mbi |= MBI_STAT;
480a1b9471aSRobert Watson 	if (accmode & VAPPEND)
481a1b9471aSRobert Watson 		mbi |= MBI_APPEND;
482a1b9471aSRobert Watson 	return (mbi);
483a1b9471aSRobert Watson }
484a1b9471aSRobert Watson 
4853f1a7a90SRobert Watson static struct mac_policy_ops ugidfw_ops =
486d8a7b7a3SRobert Watson {
4873f1a7a90SRobert Watson 	.mpo_destroy = ugidfw_destroy,
4883f1a7a90SRobert Watson 	.mpo_init = ugidfw_init,
4893f1a7a90SRobert Watson 	.mpo_system_check_acct = ugidfw_system_check_acct,
4903f1a7a90SRobert Watson 	.mpo_system_check_auditctl = ugidfw_system_check_auditctl,
4913f1a7a90SRobert Watson 	.mpo_system_check_swapon = ugidfw_system_check_swapon,
4923f1a7a90SRobert Watson 	.mpo_vnode_check_access = ugidfw_vnode_check_access,
4933f1a7a90SRobert Watson 	.mpo_vnode_check_chdir = ugidfw_vnode_check_chdir,
4943f1a7a90SRobert Watson 	.mpo_vnode_check_chroot = ugidfw_vnode_check_chroot,
4953f1a7a90SRobert Watson 	.mpo_vnode_check_create = ugidfw_check_create_vnode,
4963f1a7a90SRobert Watson 	.mpo_vnode_check_deleteacl = ugidfw_vnode_check_deleteacl,
4973f1a7a90SRobert Watson 	.mpo_vnode_check_deleteextattr = ugidfw_vnode_check_deleteextattr,
4983f1a7a90SRobert Watson 	.mpo_vnode_check_exec = ugidfw_vnode_check_exec,
4993f1a7a90SRobert Watson 	.mpo_vnode_check_getacl = ugidfw_vnode_check_getacl,
5003f1a7a90SRobert Watson 	.mpo_vnode_check_getextattr = ugidfw_vnode_check_getextattr,
5013f1a7a90SRobert Watson 	.mpo_vnode_check_link = ugidfw_vnode_check_link,
5023f1a7a90SRobert Watson 	.mpo_vnode_check_listextattr = ugidfw_vnode_check_listextattr,
5033f1a7a90SRobert Watson 	.mpo_vnode_check_lookup = ugidfw_vnode_check_lookup,
5043f1a7a90SRobert Watson 	.mpo_vnode_check_open = ugidfw_vnode_check_open,
5053f1a7a90SRobert Watson 	.mpo_vnode_check_readdir = ugidfw_vnode_check_readdir,
5063f1a7a90SRobert Watson 	.mpo_vnode_check_readlink = ugidfw_vnode_check_readdlink,
5073f1a7a90SRobert Watson 	.mpo_vnode_check_rename_from = ugidfw_vnode_check_rename_from,
5083f1a7a90SRobert Watson 	.mpo_vnode_check_rename_to = ugidfw_vnode_check_rename_to,
5093f1a7a90SRobert Watson 	.mpo_vnode_check_revoke = ugidfw_vnode_check_revoke,
5103f1a7a90SRobert Watson 	.mpo_vnode_check_setacl = ugidfw_check_setacl_vnode,
5113f1a7a90SRobert Watson 	.mpo_vnode_check_setextattr = ugidfw_vnode_check_setextattr,
5123f1a7a90SRobert Watson 	.mpo_vnode_check_setflags = ugidfw_vnode_check_setflags,
5133f1a7a90SRobert Watson 	.mpo_vnode_check_setmode = ugidfw_vnode_check_setmode,
5143f1a7a90SRobert Watson 	.mpo_vnode_check_setowner = ugidfw_vnode_check_setowner,
5153f1a7a90SRobert Watson 	.mpo_vnode_check_setutimes = ugidfw_vnode_check_setutimes,
5163f1a7a90SRobert Watson 	.mpo_vnode_check_stat = ugidfw_vnode_check_stat,
5173f1a7a90SRobert Watson 	.mpo_vnode_check_unlink = ugidfw_vnode_check_unlink,
518d8a7b7a3SRobert Watson };
519d8a7b7a3SRobert Watson 
5203f1a7a90SRobert Watson MAC_POLICY_SET(&ugidfw_ops, mac_bsdextended, "TrustedBSD MAC/BSD Extended",
5219162f64bSRobert Watson     MPC_LOADTIME_FLAG_UNLOADOK, NULL);
522