xref: /netbsd-src/external/cddl/osnet/sys/kern/policy.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /*	$NetBSD: policy.c,v 1.6 2012/10/19 22:19:15 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Doran.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*-
33  * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 /*
59  * CDDL HEADER START
60  *
61  * The contents of this file are subject to the terms of the
62  * Common Development and Distribution License (the "License").
63  * You may not use this file except in compliance with the License.
64  *
65  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
66  * or http://www.opensolaris.org/os/licensing.
67  * See the License for the specific language governing permissions
68  * and limitations under the License.
69  *
70  * When distributing Covered Code, include this CDDL HEADER in each
71  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
72  * If applicable, add the following below this CDDL HEADER, with the
73  * fields enclosed by brackets "[]" replaced with your own identifying
74  * information: Portions Copyright [yyyy] [name of copyright owner]
75  *
76  * CDDL HEADER END
77  */
78 /*
79  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
80  * Copyright 2012, Joyent, Inc. All rights reserved.
81  */
82 
83 #include <sys/param.h>
84 #include <sys/priv.h>
85 #include <sys/vnode.h>
86 #include <sys/mount.h>
87 #include <sys/stat.h>
88 #include <sys/policy.h>
89 
90 int
91 secpolicy_zfs(kauth_cred_t cred)
92 {
93 
94 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
95 }
96 
97 int
98 secpolicy_sys_config(kauth_cred_t cred, int checkonly __unused)
99 {
100 
101 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
102 }
103 
104 int
105 secpolicy_zinject(kauth_cred_t cred)
106 {
107 
108 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
109 }
110 
111 int
112 secpolicy_fs_mount(kauth_cred_t cred, struct vnode *mvp, struct mount *vfsp)
113 {
114 
115 	return kauth_authorize_system(cred, KAUTH_SYSTEM_MOUNT,
116 	    KAUTH_REQ_SYSTEM_MOUNT_NEW, vfsp, NULL, NULL);
117 }
118 
119 int
120 secpolicy_fs_unmount(kauth_cred_t cred, struct mount *vfsp)
121 {
122 
123 	return kauth_authorize_system(cred, KAUTH_SYSTEM_MOUNT,
124 	    KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT, vfsp, NULL, NULL);
125 }
126 
127 /*
128  * This check is done in kern_link(), so we could just return 0 here.
129  */
130 int
131 secpolicy_basic_link(kauth_cred_t cred)
132 {
133 
134 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
135 }
136 
137 int
138 secpolicy_vnode_stky_modify(kauth_cred_t cred)
139 {
140 
141 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
142 }
143 
144 int
145 secpolicy_vnode_remove(kauth_cred_t cred)
146 {
147 
148 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
149 }
150 
151 
152 int
153 secpolicy_vnode_owner(kauth_cred_t cred, uid_t owner)
154 {
155 
156 	if (owner == kauth_cred_getuid(cred))
157 		return (0);
158 
159 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
160 }
161 
162 int
163 secpolicy_vnode_access(kauth_cred_t cred, struct vnode *vp, uid_t owner,
164     int mode)
165 {
166 
167 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
168 }
169 
170 int
171 secpolicy_xvattr(xvattr_t *xvap, uid_t owner, kauth_cred_t cred, vtype_t vtype)
172 {
173 
174 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
175 }
176 
177 int
178 secpolicy_vnode_setid_retain(kauth_cred_t cred, boolean_t issuidroot __unused)
179 {
180 
181 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
182 }
183 
184 int
185 secpolicy_vnode_setids_setgids(kauth_cred_t cred, gid_t gid)
186 {
187 
188 	if (groupmember(gid, cred))
189 		return (0);
190 
191 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
192 }
193 
194 int
195 secpolicy_vnode_chown(kauth_cred_t cred, boolean_t check_self)
196 {
197 
198 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
199 }
200 
201 int
202 secpolicy_vnode_create_gid(kauth_cred_t cred)
203 {
204 
205 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
206 }
207 
208 int
209 secpolicy_vnode_utime_modify(kauth_cred_t cred)
210 {
211 
212 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
213 }
214 
215 int
216 secpolicy_vnode_setdac(kauth_cred_t cred, uid_t owner)
217 {
218 
219 	if (owner == kauth_cred_getuid(cred))
220 		return (0);
221 
222 	return kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL);
223 }
224 
225 int
226 secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap,
227     const struct vattr *ovap, kauth_cred_t cred)
228 {
229 	/*
230 	 * Privileged processes may set the sticky bit on non-directories,
231 	 * as well as set the setgid bit on a file with a group that the process
232 	 * is not a member of. Both of these are allowed in jail(8).
233 	 */
234 	if (vp->v_type != VDIR && (vap->va_mode & S_ISTXT)) {
235 		if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL))
236 			return (EFTYPE);
237 	}
238 	/*
239 	 * Check for privilege if attempting to set the
240 	 * group-id bit.
241 	 */
242 	if ((vap->va_mode & S_ISGID) != 0)
243 		return (secpolicy_vnode_setids_setgids(cred, ovap->va_gid));
244 
245 	return (0);
246 }
247 
248 /*
249  * XXX Copied from illumos.  Should not be here; should be under
250  * external/cddl/osnet/dist.  Not sure why it is even in illumos's
251  * policy.c rather than somewhere in vnode.c or something.
252  */
253 int
254 secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap,
255     const struct vattr *ovap, int flags,
256     int unlocked_access(void *, int, kauth_cred_t), void *node)
257 {
258 	int mask = vap->va_mask;
259 	int error = 0;
260 	boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
261 
262 	if (mask & AT_SIZE) {
263 		if (vp->v_type == VDIR) {
264 			error = EISDIR;
265 			goto out;
266 		}
267 
268 		/*
269 		 * If ATTR_NOACLCHECK is set in the flags, then we don't
270 		 * perform the secondary unlocked_access() call since the
271 		 * ACL (if any) is being checked there.
272 		 */
273 		if (skipaclchk == B_FALSE) {
274 			error = unlocked_access(node, VWRITE, cred);
275 			if (error)
276 				goto out;
277 		}
278 	}
279 	if (mask & AT_MODE) {
280 		/*
281 		 * If not the owner of the file then check privilege
282 		 * for two things: the privilege to set the mode at all
283 		 * and, if we're setting setuid, we also need permissions
284 		 * to add the set-uid bit, if we're not the owner.
285 		 * In the specific case of creating a set-uid root
286 		 * file, we need even more permissions.
287 		 */
288 		if ((error = secpolicy_vnode_setdac(cred, ovap->va_uid)) != 0)
289 			goto out;
290 
291 		if ((error = secpolicy_setid_setsticky_clear(vp, vap,
292 		    ovap, cred)) != 0)
293 			goto out;
294 	} else
295 		vap->va_mode = ovap->va_mode;
296 
297 	if (mask & (AT_UID|AT_GID)) {
298 		boolean_t checkpriv = B_FALSE;
299 
300 		/*
301 		 * Chowning files.
302 		 *
303 		 * If you are the file owner:
304 		 *	chown to other uid		FILE_CHOWN_SELF
305 		 *	chown to gid (non-member) 	FILE_CHOWN_SELF
306 		 *	chown to gid (member) 		<none>
307 		 *
308 		 * Instead of PRIV_FILE_CHOWN_SELF, FILE_CHOWN is also
309 		 * acceptable but the first one is reported when debugging.
310 		 *
311 		 * If you are not the file owner:
312 		 *	chown from root			PRIV_FILE_CHOWN + zone
313 		 *	chown from other to any		PRIV_FILE_CHOWN
314 		 *
315 		 */
316 		if (kauth_cred_getuid(cred) != ovap->va_uid) {
317 			checkpriv = B_TRUE;
318 		} else {
319 			if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
320 			    ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
321 			    !groupmember(vap->va_gid, cred))) {
322 				checkpriv = B_TRUE;
323 			}
324 		}
325 		/*
326 		 * If necessary, check privilege to see if update can be done.
327 		 */
328 		if (checkpriv &&
329 		    (error = secpolicy_vnode_chown(cred, ovap->va_uid)) != 0) {
330 			goto out;
331 		}
332 
333 		/*
334 		 * If the file has either the set UID or set GID bits
335 		 * set and the caller can set the bits, then leave them.
336 		 */
337 		secpolicy_setid_clear(vap, cred);
338 	}
339 	if (mask & (AT_ATIME|AT_MTIME)) {
340 		/*
341 		 * If not the file owner and not otherwise privileged,
342 		 * always return an error when setting the
343 		 * time other than the current (ATTR_UTIME flag set).
344 		 * If setting the current time (ATTR_UTIME not set) then
345 		 * unlocked_access will check permissions according to policy.
346 		 */
347 		if (kauth_cred_getuid(cred) != ovap->va_uid) {
348 			if (flags & ATTR_UTIME)
349 				error = secpolicy_vnode_utime_modify(cred);
350 			else if (skipaclchk == B_FALSE) {
351 				error = unlocked_access(node, VWRITE, cred);
352 				if (error == EACCES &&
353 				    secpolicy_vnode_utime_modify(cred) == 0)
354 					error = 0;
355 			}
356 			if (error)
357 				goto out;
358 		}
359 	}
360 
361 	/*
362 	 * Check for optional attributes here by checking the following:
363 	 */
364 	if (mask & AT_XVATTR)
365 		error = secpolicy_xvattr((xvattr_t *)vap, ovap->va_uid, cred,
366 		    vp->v_type);
367 out:
368 	return (error);
369 }
370 
371 void
372 secpolicy_setid_clear(struct vattr *vap, kauth_cred_t cred)
373 {
374 	if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) != 0)
375 		return;
376 
377 	if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) {
378 		vap->va_mask |= AT_MODE;
379 		vap->va_mode &= ~(S_ISUID|S_ISGID);
380 	}
381 
382 	return;
383 }
384 
385 #ifdef notyet
386 int
387 secpolicy_vnode_setdac(kauth_cred_t cred, uid_t owner)
388 {
389 
390 	if (owner == cred->cr_uid)
391 		return (0);
392 	return (priv_check_cred(cred, PRIV_VFS_ADMIN, 0));
393 }
394 
395 int
396 secpolicy_vnode_setattr(kauth_cred_t cred, struct vnode *vp, struct vattr *vap,
397     const struct vattr *ovap, int flags,
398     int unlocked_access(void *, int, kauth_cred_t), void *node)
399 {
400 	int mask = vap->va_mask;
401 	int error;
402 
403 	if (mask & AT_SIZE) {
404 		if (vp->v_type == VDIR)
405 			return (EISDIR);
406 		error = unlocked_access(node, VWRITE, cred);
407 		if (error)
408 			return (error);
409 	}
410 	if (mask & AT_MODE) {
411 		/*
412 		 * If not the owner of the file then check privilege
413 		 * for two things: the privilege to set the mode at all
414 		 * and, if we're setting setuid, we also need permissions
415 		 * to add the set-uid bit, if we're not the owner.
416 		 * In the specific case of creating a set-uid root
417 		 * file, we need even more permissions.
418 		 */
419 		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
420 		if (error)
421 			return (error);
422 		error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cred);
423 		if (error)
424 			return (error);
425 	} else {
426 		vap->va_mode = ovap->va_mode;
427 	}
428 	if (mask & (AT_UID | AT_GID)) {
429 		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
430 		if (error)
431 			return (error);
432 
433 		/*
434 		 * To change the owner of a file, or change the group of a file to a
435 		 * group of which we are not a member, the caller must have
436 		 * privilege.
437 		 */
438 		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
439 		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
440 		     !groupmember(vap->va_gid, cred))) {
441 			error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
442 			if (error)
443 				return (error);
444 		}
445 
446 		if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
447 		    ((mask & AT_GID) && vap->va_gid != ovap->va_gid)) {
448 			secpolicy_setid_clear(vap, cred);
449 		}
450 	}
451 	if (mask & (AT_ATIME | AT_MTIME)) {
452 		/*
453 		 * From utimes(2):
454 		 * If times is NULL, ... The caller must be the owner of
455 		 * the file, have permission to write the file, or be the
456 		 * super-user.
457 		 * If times is non-NULL, ... The caller must be the owner of
458 		 * the file or be the super-user.
459 		 */
460 		error = secpolicy_vnode_setdac(cred, ovap->va_uid);
461 		if (error && (vap->va_vaflags & VA_UTIMES_NULL))
462 			error = unlocked_access(node, VWRITE, cred);
463 		if (error)
464 			return (error);
465 	}
466 	return (0);
467 }
468 
469 int
470 secpolicy_vnode_create_gid(kauth_cred_t cred)
471 {
472 
473 	return (EPERM);
474 }
475 
476 int
477 secpolicy_vnode_setid_retain(kauth_cred_t cred, boolean_t issuidroot __unused)
478 {
479 
480 	return (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0));
481 }
482 
483 void
484 secpolicy_setid_clear(struct vattr *vap, kauth_cred_t cred)
485 {
486 
487 	if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL))
488 		return;
489 
490 	if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) {
491 		if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) {
492 			vap->va_mask |= AT_MODE;
493 			vap->va_mode &= ~(S_ISUID|S_ISGID);
494 		}
495 	}
496 }
497 #endif
498