1101099Srwatson/*-
2184467Srwatson * Copyright (c) 1999-2002, 2007-2008 Robert N. M. Watson
3145412Strhodes * Copyright (c) 2001-2005 Networks Associates Technology, Inc.
4171253Srwatson * Copyright (c) 2005 Tom Rhodes
5172930Srwatson * Copyright (c) 2006 SPARTA, Inc.
6101099Srwatson * All rights reserved.
7101099Srwatson *
8101099Srwatson * This software was developed by Robert Watson for the TrustedBSD Project.
9145412Strhodes * It was later enhanced by Tom Rhodes for the TrustedBSD Project.
10101099Srwatson *
11106393Srwatson * This software was developed for the FreeBSD Project in part by Network
12106393Srwatson * Associates Laboratories, the Security Research Division of Network
13106393Srwatson * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
14106393Srwatson * as part of the DARPA CHATS research program.
15101099Srwatson *
16172930Srwatson * This software was enhanced by SPARTA ISSO under SPAWAR contract
17172930Srwatson * N66001-04-C-6019 ("SEFOS").
18172930Srwatson *
19101099Srwatson * Redistribution and use in source and binary forms, with or without
20101099Srwatson * modification, are permitted provided that the following conditions
21101099Srwatson * are met:
22101099Srwatson * 1. Redistributions of source code must retain the above copyright
23101099Srwatson *    notice, this list of conditions and the following disclaimer.
24101099Srwatson * 2. Redistributions in binary form must reproduce the above copyright
25101099Srwatson *    notice, this list of conditions and the following disclaimer in the
26101099Srwatson *    documentation and/or other materials provided with the distribution.
27101099Srwatson *
28101099Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29101099Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30101099Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31101099Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32101099Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33101099Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34101099Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35101099Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36101099Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37101099Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38101099Srwatson * SUCH DAMAGE.
39101099Srwatson *
40101099Srwatson * $FreeBSD: stable/11/sys/security/mac_bsdextended/mac_bsdextended.c 321055 2017-07-16 19:24:09Z emaste $
41101099Srwatson */
42136774Srwatson
43101099Srwatson/*
44101099Srwatson * Developed by the TrustedBSD Project.
45171253Srwatson *
46171253Srwatson * "BSD Extended" MAC policy, allowing the administrator to impose mandatory
47171253Srwatson * firewall-like rules regarding users and file system objects.
48101099Srwatson */
49101099Srwatson
50101099Srwatson#include <sys/param.h>
51101099Srwatson#include <sys/acl.h>
52101099Srwatson#include <sys/kernel.h>
53157986Sdwmalone#include <sys/jail.h>
54145412Strhodes#include <sys/lock.h>
55101099Srwatson#include <sys/malloc.h>
56166905Srwatson#include <sys/module.h>
57101099Srwatson#include <sys/mount.h>
58145412Strhodes#include <sys/mutex.h>
59170689Srwatson#include <sys/priv.h>
60185435Sbz#include <sys/proc.h>
61101099Srwatson#include <sys/systm.h>
62101099Srwatson#include <sys/vnode.h>
63101099Srwatson#include <sys/sysctl.h>
64134132Strhodes#include <sys/syslog.h>
65182905Strasz#include <sys/stat.h>
66101099Srwatson
67165469Srwatson#include <security/mac/mac_policy.h>
68101099Srwatson#include <security/mac_bsdextended/mac_bsdextended.h>
69184331Srwatson#include <security/mac_bsdextended/ugidfw_internal.h>
70101099Srwatson
71172955Srwatsonstatic struct mtx ugidfw_mtx;
72145412Strhodes
73101099SrwatsonSYSCTL_DECL(_security_mac);
74101099Srwatson
75227309Sedstatic SYSCTL_NODE(_security_mac, OID_AUTO, bsdextended, CTLFLAG_RW, 0,
76101099Srwatson    "TrustedBSD extended BSD MAC policy controls");
77101099Srwatson
78172955Srwatsonstatic int	ugidfw_enabled = 1;
79267992ShselaskySYSCTL_INT(_security_mac_bsdextended, OID_AUTO, enabled, CTLFLAG_RWTUN,
80172955Srwatson    &ugidfw_enabled, 0, "Enforce extended BSD policy");
81101099Srwatson
82227293Sedstatic MALLOC_DEFINE(M_MACBSDEXTENDED, "mac_bsdextended",
83227293Sed    "BSD Extended MAC rule");
84101099Srwatson
85101099Srwatson#define	MAC_BSDEXTENDED_MAXRULES	250
86101099Srwatsonstatic struct mac_bsdextended_rule *rules[MAC_BSDEXTENDED_MAXRULES];
87101099Srwatsonstatic int rule_count = 0;
88101099Srwatsonstatic int rule_slots = 0;
89157986Sdwmalonestatic int rule_version = MB_VERSION;
90101099Srwatson
91101099SrwatsonSYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_count, CTLFLAG_RD,
92101099Srwatson    &rule_count, 0, "Number of defined rules\n");
93101099SrwatsonSYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_slots, CTLFLAG_RD,
94101099Srwatson    &rule_slots, 0, "Number of used rule slots\n");
95157986SdwmaloneSYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_version, CTLFLAG_RD,
96157986Sdwmalone    &rule_version, 0, "Version number for API\n");
97101099Srwatson
98134132Strhodes/*
99171253Srwatson * This is just used for logging purposes, eventually we would like to log
100171253Srwatson * much more then failed requests.
101134132Strhodes */
102172955Srwatsonstatic int ugidfw_logging;
103134132StrhodesSYSCTL_INT(_security_mac_bsdextended, OID_AUTO, logging, CTLFLAG_RW,
104172955Srwatson    &ugidfw_logging, 0, "Log failed authorization requests");
105134132Strhodes
106134132Strhodes/*
107171253Srwatson * This tunable is here for compatibility.  It will allow the user to switch
108171253Srwatson * between the new mode (first rule matches) and the old functionality (all
109171253Srwatson * rules match).
110134131Strhodes */
111172955Srwatsonstatic int ugidfw_firstmatch_enabled;
112134131StrhodesSYSCTL_INT(_security_mac_bsdextended, OID_AUTO, firstmatch_enabled,
113172955Srwatson    CTLFLAG_RW, &ugidfw_firstmatch_enabled, 1,
114171253Srwatson    "Disable/enable match first rule functionality");
115134131Strhodes
116134131Strhodesstatic int
117172955Srwatsonugidfw_rule_valid(struct mac_bsdextended_rule *rule)
118101099Srwatson{
119101099Srwatson
120157986Sdwmalone	if ((rule->mbr_subject.mbs_flags | MBS_ALL_FLAGS) != MBS_ALL_FLAGS)
121101099Srwatson		return (EINVAL);
122157986Sdwmalone	if ((rule->mbr_subject.mbs_neg | MBS_ALL_FLAGS) != MBS_ALL_FLAGS)
123101099Srwatson		return (EINVAL);
124157986Sdwmalone	if ((rule->mbr_object.mbo_flags | MBO_ALL_FLAGS) != MBO_ALL_FLAGS)
125157986Sdwmalone		return (EINVAL);
126157986Sdwmalone	if ((rule->mbr_object.mbo_neg | MBO_ALL_FLAGS) != MBO_ALL_FLAGS)
127157986Sdwmalone		return (EINVAL);
128321055Semaste	if (((rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) != 0) &&
129157986Sdwmalone	    (rule->mbr_object.mbo_type | MBO_ALL_TYPE) != MBO_ALL_TYPE)
130157986Sdwmalone		return (EINVAL);
131136739Srwatson	if ((rule->mbr_mode | MBI_ALLPERM) != MBI_ALLPERM)
132101099Srwatson		return (EINVAL);
133101099Srwatson	return (0);
134101099Srwatson}
135101099Srwatson
136101099Srwatsonstatic int
137101099Srwatsonsysctl_rule(SYSCTL_HANDLER_ARGS)
138101099Srwatson{
139101099Srwatson	struct mac_bsdextended_rule temprule, *ruleptr;
140101099Srwatson	u_int namelen;
141101099Srwatson	int error, index, *name;
142101099Srwatson
143145412Strhodes	error = 0;
144101099Srwatson	name = (int *)arg1;
145101099Srwatson	namelen = arg2;
146101099Srwatson	if (namelen != 1)
147101099Srwatson		return (EINVAL);
148101099Srwatson	index = name[0];
149154386Scsjp        if (index >= MAC_BSDEXTENDED_MAXRULES)
150101099Srwatson		return (ENOENT);
151101099Srwatson
152145412Strhodes	ruleptr = NULL;
153145412Strhodes	if (req->newptr && req->newlen != 0) {
154145412Strhodes		error = SYSCTL_IN(req, &temprule, sizeof(temprule));
155101099Srwatson		if (error)
156101099Srwatson			return (error);
157184214Sdes		ruleptr = malloc(sizeof(*ruleptr), M_MACBSDEXTENDED,
158184214Sdes		    M_WAITOK | M_ZERO);
159101099Srwatson	}
160101099Srwatson
161172955Srwatson	mtx_lock(&ugidfw_mtx);
162145412Strhodes	if (req->oldptr) {
163145412Strhodes		if (index < 0 || index > rule_slots + 1) {
164145412Strhodes			error = ENOENT;
165145412Strhodes			goto out;
166101099Srwatson		}
167145412Strhodes		if (rules[index] == NULL) {
168145412Strhodes			error = ENOENT;
169145412Strhodes			goto out;
170145412Strhodes		}
171145412Strhodes		temprule = *rules[index];
172145412Strhodes	}
173145412Strhodes	if (req->newptr && req->newlen == 0) {
174145412Strhodes		KASSERT(ruleptr == NULL, ("sysctl_rule: ruleptr != NULL"));
175145412Strhodes		ruleptr = rules[index];
176145412Strhodes		if (ruleptr == NULL) {
177145412Strhodes			error = ENOENT;
178145412Strhodes			goto out;
179145412Strhodes		}
180145412Strhodes		rule_count--;
181145412Strhodes		rules[index] = NULL;
182145412Strhodes	} else if (req->newptr) {
183172955Srwatson		error = ugidfw_rule_valid(&temprule);
184101099Srwatson		if (error)
185145412Strhodes			goto out;
186101099Srwatson		if (rules[index] == NULL) {
187101099Srwatson			*ruleptr = temprule;
188101099Srwatson			rules[index] = ruleptr;
189145412Strhodes			ruleptr = NULL;
190145412Strhodes			if (index + 1 > rule_slots)
191145412Strhodes				rule_slots = index + 1;
192101099Srwatson			rule_count++;
193171253Srwatson		} else
194101099Srwatson			*rules[index] = temprule;
195101099Srwatson	}
196145412Strhodesout:
197172955Srwatson	mtx_unlock(&ugidfw_mtx);
198145412Strhodes	if (ruleptr != NULL)
199184205Sdes		free(ruleptr, M_MACBSDEXTENDED);
200148482Strhodes	if (req->oldptr && error == 0)
201145412Strhodes		error = SYSCTL_OUT(req, &temprule, sizeof(temprule));
202148482Strhodes	return (error);
203101099Srwatson}
204101099Srwatson
205227309Sedstatic SYSCTL_NODE(_security_mac_bsdextended, OID_AUTO, rules,
206189590Scsjp    CTLFLAG_MPSAFE | CTLFLAG_RW, sysctl_rule, "BSD extended MAC rules");
207101099Srwatson
208101099Srwatsonstatic void
209172955Srwatsonugidfw_init(struct mac_policy_conf *mpc)
210101099Srwatson{
211101099Srwatson
212172955Srwatson	mtx_init(&ugidfw_mtx, "mac_bsdextended lock", NULL, MTX_DEF);
213101099Srwatson}
214101099Srwatson
215101099Srwatsonstatic void
216172955Srwatsonugidfw_destroy(struct mac_policy_conf *mpc)
217101099Srwatson{
218184367Srwatson	int i;
219101099Srwatson
220184367Srwatson	for (i = 0; i < MAC_BSDEXTENDED_MAXRULES; i++) {
221184367Srwatson		if (rules[i] != NULL)
222184367Srwatson			free(rules[i], M_MACBSDEXTENDED);
223184367Srwatson	}
224172955Srwatson	mtx_destroy(&ugidfw_mtx);
225101099Srwatson}
226101099Srwatson
227101099Srwatsonstatic int
228172955Srwatsonugidfw_rulecheck(struct mac_bsdextended_rule *rule,
229157986Sdwmalone    struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode)
230101099Srwatson{
231183113Sattilio	int mac_granted, match, priv_granted;
232157986Sdwmalone	int i;
233101099Srwatson
234101099Srwatson	/*
235101099Srwatson	 * Is there a subject match?
236101099Srwatson	 */
237172955Srwatson	mtx_assert(&ugidfw_mtx, MA_OWNED);
238157986Sdwmalone	if (rule->mbr_subject.mbs_flags & MBS_UID_DEFINED) {
239157986Sdwmalone		match =  ((cred->cr_uid <= rule->mbr_subject.mbs_uid_max &&
240157986Sdwmalone		    cred->cr_uid >= rule->mbr_subject.mbs_uid_min) ||
241157986Sdwmalone		    (cred->cr_ruid <= rule->mbr_subject.mbs_uid_max &&
242157986Sdwmalone		    cred->cr_ruid >= rule->mbr_subject.mbs_uid_min) ||
243157986Sdwmalone		    (cred->cr_svuid <= rule->mbr_subject.mbs_uid_max &&
244157986Sdwmalone		    cred->cr_svuid >= rule->mbr_subject.mbs_uid_min));
245157986Sdwmalone		if (rule->mbr_subject.mbs_neg & MBS_UID_DEFINED)
246101099Srwatson			match = !match;
247101099Srwatson		if (!match)
248101099Srwatson			return (0);
249101099Srwatson	}
250101099Srwatson
251157986Sdwmalone	if (rule->mbr_subject.mbs_flags & MBS_GID_DEFINED) {
252157986Sdwmalone		match = ((cred->cr_rgid <= rule->mbr_subject.mbs_gid_max &&
253157986Sdwmalone		    cred->cr_rgid >= rule->mbr_subject.mbs_gid_min) ||
254157986Sdwmalone		    (cred->cr_svgid <= rule->mbr_subject.mbs_gid_max &&
255157986Sdwmalone		    cred->cr_svgid >= rule->mbr_subject.mbs_gid_min));
256157986Sdwmalone		if (!match) {
257171253Srwatson			for (i = 0; i < cred->cr_ngroups; i++) {
258157986Sdwmalone				if (cred->cr_groups[i]
259157986Sdwmalone				    <= rule->mbr_subject.mbs_gid_max &&
260157986Sdwmalone				    cred->cr_groups[i]
261157986Sdwmalone				    >= rule->mbr_subject.mbs_gid_min) {
262157986Sdwmalone					match = 1;
263157986Sdwmalone					break;
264157986Sdwmalone				}
265171253Srwatson			}
266157986Sdwmalone		}
267157986Sdwmalone		if (rule->mbr_subject.mbs_neg & MBS_GID_DEFINED)
268101099Srwatson			match = !match;
269101099Srwatson		if (!match)
270101099Srwatson			return (0);
271101099Srwatson	}
272101099Srwatson
273157986Sdwmalone	if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
274192895Sjamie		match =
275192895Sjamie		    (cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison);
276157986Sdwmalone		if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)
277157986Sdwmalone			match = !match;
278157986Sdwmalone		if (!match)
279157986Sdwmalone			return (0);
280157986Sdwmalone	}
281157986Sdwmalone
282101099Srwatson	/*
283101099Srwatson	 * Is there an object match?
284101099Srwatson	 */
285157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
286157986Sdwmalone		match = (vap->va_uid <= rule->mbr_object.mbo_uid_max &&
287157986Sdwmalone		    vap->va_uid >= rule->mbr_object.mbo_uid_min);
288157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)
289101099Srwatson			match = !match;
290101099Srwatson		if (!match)
291101099Srwatson			return (0);
292101099Srwatson	}
293101099Srwatson
294157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
295157986Sdwmalone		match = (vap->va_gid <= rule->mbr_object.mbo_gid_max &&
296157986Sdwmalone		    vap->va_gid >= rule->mbr_object.mbo_gid_min);
297157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)
298101099Srwatson			match = !match;
299101099Srwatson		if (!match)
300101099Srwatson			return (0);
301101099Srwatson	}
302101099Srwatson
303157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
304157986Sdwmalone		match = (bcmp(&(vp->v_mount->mnt_stat.f_fsid),
305157986Sdwmalone		    &(rule->mbr_object.mbo_fsid),
306157986Sdwmalone		    sizeof(rule->mbr_object.mbo_fsid)) == 0);
307157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)
308157986Sdwmalone			match = !match;
309157986Sdwmalone		if (!match)
310171253Srwatson			return (0);
311157986Sdwmalone	}
312157986Sdwmalone
313157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_SUID) {
314182905Strasz		match = (vap->va_mode & S_ISUID);
315157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_SUID)
316157986Sdwmalone			match = !match;
317157986Sdwmalone		if (!match)
318171253Srwatson			return (0);
319157986Sdwmalone	}
320157986Sdwmalone
321157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_SGID) {
322182905Strasz		match = (vap->va_mode & S_ISGID);
323157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_SGID)
324157986Sdwmalone			match = !match;
325157986Sdwmalone		if (!match)
326171253Srwatson			return (0);
327157986Sdwmalone	}
328157986Sdwmalone
329157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
330157986Sdwmalone		match = (vap->va_uid == cred->cr_uid ||
331157986Sdwmalone		    vap->va_uid == cred->cr_ruid ||
332157986Sdwmalone		    vap->va_uid == cred->cr_svuid);
333157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)
334157986Sdwmalone			match = !match;
335157986Sdwmalone		if (!match)
336171253Srwatson			return (0);
337157986Sdwmalone	}
338157986Sdwmalone
339157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
340157986Sdwmalone		match = (groupmember(vap->va_gid, cred) ||
341157986Sdwmalone		    vap->va_gid == cred->cr_rgid ||
342157986Sdwmalone		    vap->va_gid == cred->cr_svgid);
343157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)
344157986Sdwmalone			match = !match;
345157986Sdwmalone		if (!match)
346171253Srwatson			return (0);
347157986Sdwmalone	}
348157986Sdwmalone
349157986Sdwmalone	if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
350157986Sdwmalone		switch (vap->va_type) {
351157986Sdwmalone		case VREG:
352157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_REG);
353157986Sdwmalone			break;
354157986Sdwmalone		case VDIR:
355157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_DIR);
356157986Sdwmalone			break;
357157986Sdwmalone		case VBLK:
358157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_BLK);
359157986Sdwmalone			break;
360157986Sdwmalone		case VCHR:
361157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_CHR);
362157986Sdwmalone			break;
363157986Sdwmalone		case VLNK:
364157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_LNK);
365157986Sdwmalone			break;
366157986Sdwmalone		case VSOCK:
367157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_SOCK);
368157986Sdwmalone			break;
369157986Sdwmalone		case VFIFO:
370157986Sdwmalone			match = (rule->mbr_object.mbo_type & MBO_TYPE_FIFO);
371157986Sdwmalone			break;
372157986Sdwmalone		default:
373157986Sdwmalone			match = 0;
374157986Sdwmalone		}
375157986Sdwmalone		if (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)
376157986Sdwmalone			match = !match;
377157986Sdwmalone		if (!match)
378171253Srwatson			return (0);
379157986Sdwmalone	}
380157986Sdwmalone
381101099Srwatson	/*
382183113Sattilio	 * MBI_APPEND should not be here as it should get converted to
383183113Sattilio	 * MBI_WRITE.
384183113Sattilio	 */
385183113Sattilio	priv_granted = 0;
386183113Sattilio	mac_granted = rule->mbr_mode;
387183113Sattilio	if ((acc_mode & MBI_ADMIN) && (mac_granted & MBI_ADMIN) == 0 &&
388183113Sattilio	    priv_check_cred(cred, PRIV_VFS_ADMIN, 0) == 0)
389183113Sattilio		priv_granted |= MBI_ADMIN;
390183113Sattilio	if ((acc_mode & MBI_EXEC) && (mac_granted & MBI_EXEC) == 0 &&
391183113Sattilio	    priv_check_cred(cred, (vap->va_type == VDIR) ? PRIV_VFS_LOOKUP :
392183113Sattilio	    PRIV_VFS_EXEC, 0) == 0)
393183113Sattilio		priv_granted |= MBI_EXEC;
394183113Sattilio	if ((acc_mode & MBI_READ) && (mac_granted & MBI_READ) == 0 &&
395183113Sattilio	    priv_check_cred(cred, PRIV_VFS_READ, 0) == 0)
396183113Sattilio		priv_granted |= MBI_READ;
397183113Sattilio	if ((acc_mode & MBI_STAT) && (mac_granted & MBI_STAT) == 0 &&
398183113Sattilio	    priv_check_cred(cred, PRIV_VFS_STAT, 0) == 0)
399183113Sattilio		priv_granted |= MBI_STAT;
400183113Sattilio	if ((acc_mode & MBI_WRITE) && (mac_granted & MBI_WRITE) == 0 &&
401183113Sattilio	    priv_check_cred(cred, PRIV_VFS_WRITE, 0) == 0)
402183113Sattilio		priv_granted |= MBI_WRITE;
403183113Sattilio	/*
404101099Srwatson	 * Is the access permitted?
405101099Srwatson	 */
406183113Sattilio	if (((mac_granted | priv_granted) & acc_mode) != acc_mode) {
407172955Srwatson		if (ugidfw_logging)
408134132Strhodes			log(LOG_AUTHPRIV, "mac_bsdextended: %d:%d request %d"
409134132Strhodes			    " on %d:%d failed. \n", cred->cr_ruid,
410171253Srwatson			    cred->cr_rgid, acc_mode, vap->va_uid,
411171253Srwatson			    vap->va_gid);
412171253Srwatson		return (EACCES);
413101099Srwatson	}
414145412Strhodes
415134131Strhodes	/*
416145412Strhodes	 * If the rule matched, permits access, and first match is enabled,
417145412Strhodes	 * return success.
418134131Strhodes	 */
419172955Srwatson	if (ugidfw_firstmatch_enabled)
420134131Strhodes		return (EJUSTRETURN);
421134131Strhodes	else
422171253Srwatson		return (0);
423101099Srwatson}
424101099Srwatson
425184331Srwatsonint
426172955Srwatsonugidfw_check(struct ucred *cred, struct vnode *vp, struct vattr *vap,
427106212Srwatson    int acc_mode)
428101099Srwatson{
429101099Srwatson	int error, i;
430101099Srwatson
431164033Srwatson	/*
432166843Srwatson	 * Since we do not separately handle append, map append to write.
433166843Srwatson	 */
434166843Srwatson	if (acc_mode & MBI_APPEND) {
435166843Srwatson		acc_mode &= ~MBI_APPEND;
436166843Srwatson		acc_mode |= MBI_WRITE;
437166843Srwatson	}
438172955Srwatson	mtx_lock(&ugidfw_mtx);
439101099Srwatson	for (i = 0; i < rule_slots; i++) {
440101099Srwatson		if (rules[i] == NULL)
441101099Srwatson			continue;
442172955Srwatson		error = ugidfw_rulecheck(rules[i], cred,
443157986Sdwmalone		    vp, vap, acc_mode);
444134131Strhodes		if (error == EJUSTRETURN)
445134131Strhodes			break;
446145412Strhodes		if (error) {
447172955Srwatson			mtx_unlock(&ugidfw_mtx);
448101099Srwatson			return (error);
449145412Strhodes		}
450101099Srwatson	}
451172955Srwatson	mtx_unlock(&ugidfw_mtx);
452101099Srwatson	return (0);
453101099Srwatson}
454101099Srwatson
455184331Srwatsonint
456172955Srwatsonugidfw_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode)
457112575Srwatson{
458156300Sdwmalone	int error;
459112575Srwatson	struct vattr vap;
460112575Srwatson
461172955Srwatson	if (!ugidfw_enabled)
462112575Srwatson		return (0);
463182371Sattilio	error = VOP_GETATTR(vp, &vap, cred);
464112575Srwatson	if (error)
465112575Srwatson		return (error);
466172955Srwatson	return (ugidfw_check(cred, vp, &vap, acc_mode));
467112575Srwatson}
468112575Srwatson
469184467Srwatsonint
470184467Srwatsonugidfw_accmode2mbi(accmode_t accmode)
471184467Srwatson{
472184467Srwatson	int mbi;
473184467Srwatson
474184467Srwatson	mbi = 0;
475184467Srwatson	if (accmode & VEXEC)
476184467Srwatson		mbi |= MBI_EXEC;
477184467Srwatson	if (accmode & VWRITE)
478184467Srwatson		mbi |= MBI_WRITE;
479184467Srwatson	if (accmode & VREAD)
480184467Srwatson		mbi |= MBI_READ;
481190524Strasz	if (accmode & VADMIN_PERMS)
482184467Srwatson		mbi |= MBI_ADMIN;
483190524Strasz	if (accmode & VSTAT_PERMS)
484184467Srwatson		mbi |= MBI_STAT;
485184467Srwatson	if (accmode & VAPPEND)
486184467Srwatson		mbi |= MBI_APPEND;
487184467Srwatson	return (mbi);
488184467Srwatson}
489184467Srwatson
490172955Srwatsonstatic struct mac_policy_ops ugidfw_ops =
491101099Srwatson{
492172955Srwatson	.mpo_destroy = ugidfw_destroy,
493172955Srwatson	.mpo_init = ugidfw_init,
494172955Srwatson	.mpo_system_check_acct = ugidfw_system_check_acct,
495172955Srwatson	.mpo_system_check_auditctl = ugidfw_system_check_auditctl,
496172955Srwatson	.mpo_system_check_swapon = ugidfw_system_check_swapon,
497172955Srwatson	.mpo_vnode_check_access = ugidfw_vnode_check_access,
498172955Srwatson	.mpo_vnode_check_chdir = ugidfw_vnode_check_chdir,
499172955Srwatson	.mpo_vnode_check_chroot = ugidfw_vnode_check_chroot,
500172955Srwatson	.mpo_vnode_check_create = ugidfw_check_create_vnode,
501172955Srwatson	.mpo_vnode_check_deleteacl = ugidfw_vnode_check_deleteacl,
502172955Srwatson	.mpo_vnode_check_deleteextattr = ugidfw_vnode_check_deleteextattr,
503172955Srwatson	.mpo_vnode_check_exec = ugidfw_vnode_check_exec,
504172955Srwatson	.mpo_vnode_check_getacl = ugidfw_vnode_check_getacl,
505172955Srwatson	.mpo_vnode_check_getextattr = ugidfw_vnode_check_getextattr,
506172955Srwatson	.mpo_vnode_check_link = ugidfw_vnode_check_link,
507172955Srwatson	.mpo_vnode_check_listextattr = ugidfw_vnode_check_listextattr,
508172955Srwatson	.mpo_vnode_check_lookup = ugidfw_vnode_check_lookup,
509172955Srwatson	.mpo_vnode_check_open = ugidfw_vnode_check_open,
510172955Srwatson	.mpo_vnode_check_readdir = ugidfw_vnode_check_readdir,
511172955Srwatson	.mpo_vnode_check_readlink = ugidfw_vnode_check_readdlink,
512172955Srwatson	.mpo_vnode_check_rename_from = ugidfw_vnode_check_rename_from,
513172955Srwatson	.mpo_vnode_check_rename_to = ugidfw_vnode_check_rename_to,
514172955Srwatson	.mpo_vnode_check_revoke = ugidfw_vnode_check_revoke,
515172955Srwatson	.mpo_vnode_check_setacl = ugidfw_check_setacl_vnode,
516172955Srwatson	.mpo_vnode_check_setextattr = ugidfw_vnode_check_setextattr,
517172955Srwatson	.mpo_vnode_check_setflags = ugidfw_vnode_check_setflags,
518172955Srwatson	.mpo_vnode_check_setmode = ugidfw_vnode_check_setmode,
519172955Srwatson	.mpo_vnode_check_setowner = ugidfw_vnode_check_setowner,
520172955Srwatson	.mpo_vnode_check_setutimes = ugidfw_vnode_check_setutimes,
521172955Srwatson	.mpo_vnode_check_stat = ugidfw_vnode_check_stat,
522172955Srwatson	.mpo_vnode_check_unlink = ugidfw_vnode_check_unlink,
523101099Srwatson};
524101099Srwatson
525172955SrwatsonMAC_POLICY_SET(&ugidfw_ops, mac_bsdextended, "TrustedBSD MAC/BSD Extended",
526187016Srwatson    MPC_LOADTIME_FLAG_UNLOADOK, NULL);
527