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