1168404Spjd/*-
2168404Spjd * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3168404Spjd * All rights reserved.
4168404Spjd *
5168404Spjd * Redistribution and use in source and binary forms, with or without
6168404Spjd * modification, are permitted provided that the following conditions
7168404Spjd * are met:
8168404Spjd * 1. Redistributions of source code must retain the above copyright
9168404Spjd *    notice, this list of conditions and the following disclaimer.
10168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
11168404Spjd *    notice, this list of conditions and the following disclaimer in the
12168404Spjd *    documentation and/or other materials provided with the distribution.
13168404Spjd *
14168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17168404Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18168404Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24168404Spjd * SUCH DAMAGE.
25168404Spjd */
26168404Spjd
27168404Spjd#include <sys/cdefs.h>
28168404Spjd__FBSDID("$FreeBSD$");
29168404Spjd
30168404Spjd#include <sys/param.h>
31168404Spjd#include <sys/priv.h>
32168404Spjd#include <sys/vnode.h>
33185029Spjd#include <sys/mntent.h>
34168404Spjd#include <sys/mount.h>
35168404Spjd#include <sys/stat.h>
36185029Spjd#include <sys/jail.h>
37168404Spjd#include <sys/policy.h>
38185029Spjd#include <sys/zfs_vfsops.h>
39168404Spjd
40168404Spjdint
41219089Spjdsecpolicy_nfs(cred_t *cr)
42185029Spjd{
43185029Spjd
44219089Spjd	return (priv_check_cred(cr, PRIV_NFS_DAEMON, 0));
45185029Spjd}
46185029Spjd
47185029Spjdint
48219089Spjdsecpolicy_zfs(cred_t *cr)
49168404Spjd{
50168404Spjd
51219089Spjd	return (priv_check_cred(cr, PRIV_VFS_MOUNT, 0));
52168404Spjd}
53168404Spjd
54168404Spjdint
55219089Spjdsecpolicy_sys_config(cred_t *cr, int checkonly __unused)
56168404Spjd{
57168404Spjd
58219089Spjd	return (priv_check_cred(cr, PRIV_ZFS_POOL_CONFIG, 0));
59168404Spjd}
60168404Spjd
61168404Spjdint
62219089Spjdsecpolicy_zinject(cred_t *cr)
63168404Spjd{
64168404Spjd
65219089Spjd	return (priv_check_cred(cr, PRIV_ZFS_INJECT, 0));
66168404Spjd}
67168404Spjd
68168404Spjdint
69219089Spjdsecpolicy_fs_unmount(cred_t *cr, struct mount *vfsp __unused)
70168404Spjd{
71168404Spjd
72219089Spjd	return (priv_check_cred(cr, PRIV_VFS_UNMOUNT, 0));
73168404Spjd}
74168404Spjd
75185029Spjdint
76219089Spjdsecpolicy_fs_owner(struct mount *mp, cred_t *cr)
77185029Spjd{
78185029Spjd
79185029Spjd	if (zfs_super_owner) {
80219089Spjd		if (cr->cr_uid == mp->mnt_cred->cr_uid &&
81219089Spjd		    cr->cr_prison == mp->mnt_cred->cr_prison) {
82185029Spjd			return (0);
83185029Spjd		}
84185029Spjd	}
85197860Spjd	return (EPERM);
86185029Spjd}
87185029Spjd
88168404Spjd/*
89168404Spjd * This check is done in kern_link(), so we could just return 0 here.
90168404Spjd */
91168404Spjdextern int hardlink_check_uid;
92168404Spjdint
93219089Spjdsecpolicy_basic_link(vnode_t *vp, cred_t *cr)
94168404Spjd{
95168404Spjd
96168404Spjd	if (!hardlink_check_uid)
97168404Spjd		return (0);
98219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
99185029Spjd		return (0);
100219089Spjd	return (priv_check_cred(cr, PRIV_VFS_LINK, 0));
101168404Spjd}
102168404Spjd
103168404Spjdint
104219089Spjdsecpolicy_vnode_stky_modify(cred_t *cr)
105168404Spjd{
106168404Spjd
107168404Spjd	return (EPERM);
108168404Spjd}
109168404Spjd
110168404Spjdint
111219089Spjdsecpolicy_vnode_remove(vnode_t *vp, cred_t *cr)
112168404Spjd{
113168404Spjd
114219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
115185029Spjd		return (0);
116219089Spjd	return (priv_check_cred(cr, PRIV_VFS_ADMIN, 0));
117168404Spjd}
118168404Spjd
119168404Spjdint
120219089Spjdsecpolicy_vnode_access(cred_t *cr, vnode_t *vp, uid_t owner, accmode_t accmode)
121168404Spjd{
122168404Spjd
123219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
124185029Spjd		return (0);
125185029Spjd
126219089Spjd	if ((accmode & VREAD) && priv_check_cred(cr, PRIV_VFS_READ, 0) != 0)
127168404Spjd		return (EACCES);
128184413Strasz	if ((accmode & VWRITE) &&
129219089Spjd	    priv_check_cred(cr, PRIV_VFS_WRITE, 0) != 0) {
130168404Spjd		return (EACCES);
131168404Spjd	}
132184413Strasz	if (accmode & VEXEC) {
133168404Spjd		if (vp->v_type == VDIR) {
134219089Spjd			if (priv_check_cred(cr, PRIV_VFS_LOOKUP, 0) != 0)
135168404Spjd				return (EACCES);
136168404Spjd		} else {
137219089Spjd			if (priv_check_cred(cr, PRIV_VFS_EXEC, 0) != 0)
138168404Spjd				return (EACCES);
139168404Spjd		}
140168404Spjd	}
141168404Spjd	return (0);
142168404Spjd}
143168404Spjd
144219089Spjd/*
145219089Spjd * Like secpolicy_vnode_access() but we get the actual wanted mode and the
146219089Spjd * current mode of the file, not the missing bits.
147219089Spjd */
148168404Spjdint
149219089Spjdsecpolicy_vnode_access2(cred_t *cr, vnode_t *vp, uid_t owner,
150219089Spjd    accmode_t curmode, accmode_t wantmode)
151168404Spjd{
152219089Spjd	accmode_t mode;
153168404Spjd
154219089Spjd	mode = ~curmode & wantmode;
155219089Spjd
156219089Spjd	if (mode == 0)
157168404Spjd		return (0);
158219089Spjd
159219089Spjd	return (secpolicy_vnode_access(cr, vp, owner, mode));
160219089Spjd}
161219089Spjd
162219089Spjdint
163219089Spjdsecpolicy_vnode_any_access(cred_t *cr, vnode_t *vp, uid_t owner)
164219089Spjd{
165219089Spjd	static int privs[] = {
166219089Spjd	    PRIV_VFS_ADMIN,
167219089Spjd	    PRIV_VFS_READ,
168219089Spjd	    PRIV_VFS_WRITE,
169219089Spjd	    PRIV_VFS_EXEC,
170219089Spjd	    PRIV_VFS_LOOKUP
171219089Spjd	};
172219089Spjd	int i;
173219089Spjd
174219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
175185029Spjd		return (0);
176219089Spjd
177219089Spjd	/* Same as secpolicy_vnode_setdac */
178219089Spjd	if (owner == cr->cr_uid)
179219089Spjd		return (0);
180219089Spjd
181219089Spjd	for (i = 0; i < sizeof (privs)/sizeof (int); i++) {
182219089Spjd		boolean_t allzone = B_FALSE;
183219089Spjd		int priv;
184219089Spjd
185219089Spjd		switch (priv = privs[i]) {
186219089Spjd		case PRIV_VFS_EXEC:
187219089Spjd			if (vp->v_type == VDIR)
188219089Spjd				continue;
189219089Spjd			break;
190219089Spjd		case PRIV_VFS_LOOKUP:
191219089Spjd			if (vp->v_type != VDIR)
192219089Spjd				continue;
193219089Spjd			break;
194219089Spjd		}
195219089Spjd		if (priv_check_cred(cr, priv, 0) == 0)
196219089Spjd			return (0);
197219089Spjd	}
198219089Spjd	return (EPERM);
199168404Spjd}
200168404Spjd
201168404Spjdint
202219089Spjdsecpolicy_vnode_setdac(vnode_t *vp, cred_t *cr, uid_t owner)
203219089Spjd{
204219089Spjd
205219089Spjd	if (owner == cr->cr_uid)
206219089Spjd		return (0);
207219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
208219089Spjd		return (0);
209219089Spjd	return (priv_check_cred(cr, PRIV_VFS_ADMIN, 0));
210219089Spjd}
211219089Spjd
212219089Spjdint
213219089Spjdsecpolicy_vnode_setattr(cred_t *cr, vnode_t *vp, struct vattr *vap,
214168404Spjd    const struct vattr *ovap, int flags,
215219089Spjd    int unlocked_access(void *, int, cred_t *), void *node)
216168404Spjd{
217168404Spjd	int mask = vap->va_mask;
218168404Spjd	int error;
219168404Spjd
220168404Spjd	if (mask & AT_SIZE) {
221168404Spjd		if (vp->v_type == VDIR)
222168404Spjd			return (EISDIR);
223219089Spjd		error = unlocked_access(node, VWRITE, cr);
224168404Spjd		if (error)
225168404Spjd			return (error);
226168404Spjd	}
227168404Spjd	if (mask & AT_MODE) {
228168404Spjd		/*
229168404Spjd		 * If not the owner of the file then check privilege
230168404Spjd		 * for two things: the privilege to set the mode at all
231168404Spjd		 * and, if we're setting setuid, we also need permissions
232168404Spjd		 * to add the set-uid bit, if we're not the owner.
233168404Spjd		 * In the specific case of creating a set-uid root
234168404Spjd		 * file, we need even more permissions.
235168404Spjd		 */
236219089Spjd		error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid);
237168404Spjd		if (error)
238168404Spjd			return (error);
239219089Spjd		error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cr);
240168404Spjd		if (error)
241168404Spjd			return (error);
242168404Spjd	} else {
243168404Spjd		vap->va_mode = ovap->va_mode;
244168404Spjd	}
245168404Spjd	if (mask & (AT_UID | AT_GID)) {
246219089Spjd		error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid);
247168404Spjd		if (error)
248168404Spjd			return (error);
249168404Spjd
250168404Spjd		/*
251168404Spjd		 * To change the owner of a file, or change the group of a file to a
252168404Spjd		 * group of which we are not a member, the caller must have
253168404Spjd		 * privilege.
254168404Spjd		 */
255168404Spjd		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
256168404Spjd		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
257219089Spjd		     !groupmember(vap->va_gid, cr))) {
258219089Spjd			if (secpolicy_fs_owner(vp->v_mount, cr) != 0) {
259219089Spjd				error = priv_check_cred(cr, PRIV_VFS_CHOWN, 0);
260185029Spjd				if (error)
261185029Spjd					return (error);
262185029Spjd			}
263168404Spjd		}
264168404Spjd
265168404Spjd		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
266168404Spjd		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid)) {
267219089Spjd			secpolicy_setid_clear(vap, vp, cr);
268168404Spjd		}
269168404Spjd	}
270168404Spjd	if (mask & (AT_ATIME | AT_MTIME)) {
271168404Spjd		/*
272168404Spjd		 * From utimes(2):
273168404Spjd		 * If times is NULL, ... The caller must be the owner of
274168404Spjd		 * the file, have permission to write the file, or be the
275168404Spjd		 * super-user.
276168404Spjd		 * If times is non-NULL, ... The caller must be the owner of
277168404Spjd		 * the file or be the super-user.
278168404Spjd		 */
279219089Spjd		error = secpolicy_vnode_setdac(vp, cr, ovap->va_uid);
280168404Spjd		if (error && (vap->va_vaflags & VA_UTIMES_NULL))
281219089Spjd			error = unlocked_access(node, VWRITE, cr);
282168404Spjd		if (error)
283168404Spjd			return (error);
284168404Spjd	}
285168404Spjd	return (0);
286168404Spjd}
287168404Spjd
288168404Spjdint
289219089Spjdsecpolicy_vnode_create_gid(cred_t *cr)
290168404Spjd{
291168404Spjd
292168404Spjd	return (EPERM);
293168404Spjd}
294168404Spjd
295168404Spjdint
296219089Spjdsecpolicy_vnode_setids_setgids(vnode_t *vp, cred_t *cr, gid_t gid)
297168404Spjd{
298219089Spjd
299219089Spjd	if (groupmember(gid, cr))
300185029Spjd		return (0);
301219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
302185029Spjd		return (0);
303219089Spjd	return (priv_check_cred(cr, PRIV_VFS_SETGID, 0));
304168404Spjd}
305168404Spjd
306168404Spjdint
307219089Spjdsecpolicy_vnode_setid_retain(vnode_t *vp, cred_t *cr,
308185029Spjd    boolean_t issuidroot __unused)
309168404Spjd{
310168404Spjd
311219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
312185029Spjd		return (0);
313219089Spjd	return (priv_check_cred(cr, PRIV_VFS_RETAINSUGID, 0));
314168404Spjd}
315168404Spjd
316168404Spjdvoid
317219089Spjdsecpolicy_setid_clear(struct vattr *vap, vnode_t *vp, cred_t *cr)
318168404Spjd{
319168404Spjd
320219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
321185029Spjd		return;
322185029Spjd
323168404Spjd	if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) {
324219089Spjd		if (priv_check_cred(cr, PRIV_VFS_RETAINSUGID, 0)) {
325168404Spjd			vap->va_mask |= AT_MODE;
326168404Spjd			vap->va_mode &= ~(S_ISUID|S_ISGID);
327168404Spjd		}
328168404Spjd	}
329168404Spjd}
330168404Spjd
331168404Spjdint
332219089Spjdsecpolicy_setid_setsticky_clear(vnode_t *vp, struct vattr *vap,
333219089Spjd    const struct vattr *ovap, cred_t *cr)
334168404Spjd{
335168404Spjd        int error;
336168404Spjd
337219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
338185029Spjd		return (0);
339185029Spjd
340168404Spjd	/*
341168404Spjd	 * Privileged processes may set the sticky bit on non-directories,
342168404Spjd	 * as well as set the setgid bit on a file with a group that the process
343168404Spjd	 * is not a member of. Both of these are allowed in jail(8).
344168404Spjd	 */
345168404Spjd	if (vp->v_type != VDIR && (vap->va_mode & S_ISTXT)) {
346219089Spjd		if (priv_check_cred(cr, PRIV_VFS_STICKYFILE, 0))
347168404Spjd			return (EFTYPE);
348168404Spjd	}
349168404Spjd	/*
350168404Spjd	 * Check for privilege if attempting to set the
351168404Spjd	 * group-id bit.
352168404Spjd	 */
353168404Spjd	if ((vap->va_mode & S_ISGID) != 0) {
354219089Spjd		error = secpolicy_vnode_setids_setgids(vp, cr, ovap->va_gid);
355168404Spjd		if (error)
356168404Spjd			return (error);
357168404Spjd	}
358192694Strasz	/*
359192694Strasz	 * Deny setting setuid if we are not the file owner.
360192694Strasz	 */
361219089Spjd	if ((vap->va_mode & S_ISUID) && ovap->va_uid != cr->cr_uid) {
362219089Spjd		error = priv_check_cred(cr, PRIV_VFS_ADMIN, 0);
363192694Strasz		if (error)
364192694Strasz			return (error);
365192694Strasz	}
366168404Spjd	return (0);
367168404Spjd}
368185029Spjd
369185029Spjdint
370185029Spjdsecpolicy_fs_mount(cred_t *cr, vnode_t *mvp, struct mount *vfsp)
371185029Spjd{
372185029Spjd
373185029Spjd	return (priv_check_cred(cr, PRIV_VFS_MOUNT, 0));
374185029Spjd}
375185029Spjd
376185029Spjdint
377219089Spjdsecpolicy_vnode_owner(vnode_t *vp, cred_t *cr, uid_t owner)
378185029Spjd{
379185029Spjd
380219089Spjd	if (owner == cr->cr_uid)
381185029Spjd		return (0);
382219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
383185029Spjd		return (0);
384185029Spjd
385185029Spjd	/* XXX: vfs_suser()? */
386219089Spjd	return (priv_check_cred(cr, PRIV_VFS_MOUNT_OWNER, 0));
387185029Spjd}
388185029Spjd
389185029Spjdint
390219089Spjdsecpolicy_vnode_chown(vnode_t *vp, cred_t *cr, uid_t owner)
391185029Spjd{
392185029Spjd
393219089Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
394185029Spjd		return (0);
395219089Spjd	return (priv_check_cred(cr, PRIV_VFS_CHOWN, 0));
396185029Spjd}
397185029Spjd
398185029Spjdvoid
399185029Spjdsecpolicy_fs_mount_clearopts(cred_t *cr, struct mount *vfsp)
400185029Spjd{
401185029Spjd
402185029Spjd	if (priv_check_cred(cr, PRIV_VFS_MOUNT_NONUSER, 0) != 0) {
403185029Spjd		MNT_ILOCK(vfsp);
404185029Spjd		vfsp->vfs_flag |= VFS_NOSETUID | MNT_USER;
405185029Spjd		vfs_clearmntopt(vfsp, MNTOPT_SETUID);
406185029Spjd		vfs_setmntopt(vfsp, MNTOPT_NOSETUID, NULL, 0);
407185029Spjd		MNT_IUNLOCK(vfsp);
408185029Spjd	}
409185029Spjd}
410185029Spjd
411185029Spjd/*
412185029Spjd * Check privileges for setting xvattr attributes
413185029Spjd */
414185029Spjdint
415219089Spjdsecpolicy_xvattr(vnode_t *vp, xvattr_t *xvap, uid_t owner, cred_t *cr,
416197861Spjd    vtype_t vtype)
417185029Spjd{
418185029Spjd
419197861Spjd	if (secpolicy_fs_owner(vp->v_mount, cr) == 0)
420197861Spjd		return (0);
421185029Spjd	return (priv_check_cred(cr, PRIV_VFS_SYSFLAGS, 0));
422185029Spjd}
423209962Smm
424209962Smmint
425209962Smmsecpolicy_smb(cred_t *cr)
426209962Smm{
427209962Smm
428209962Smm	return (priv_check_cred(cr, PRIV_NETSMB, 0));
429209962Smm}
430