1 /*- 2 * Copyright (c) 1999-2006 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by Robert Watson for the TrustedBSD Project. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 /* 29 * Developed by the TrustedBSD Project. 30 * 31 * ACL system calls and other functions common across different ACL types. 32 * Type-specific routines go into subr_acl_<type>.c. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "opt_mac.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/sysproto.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/mount.h> 46 #include <sys/vnode.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/namei.h> 50 #include <sys/file.h> 51 #include <sys/filedesc.h> 52 #include <sys/proc.h> 53 #include <sys/sysent.h> 54 #include <sys/acl.h> 55 56 #include <security/mac/mac_framework.h> 57 58 #include <vm/uma.h> 59 60 uma_zone_t acl_zone; 61 static int vacl_set_acl(struct thread *td, struct vnode *vp, 62 acl_type_t type, struct acl *aclp); 63 static int vacl_get_acl(struct thread *td, struct vnode *vp, 64 acl_type_t type, struct acl *aclp); 65 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 66 acl_type_t type, struct acl *aclp); 67 68 /* 69 * These calls wrap the real vnode operations, and are called by the syscall 70 * code once the syscall has converted the path or file descriptor to a vnode 71 * (unlocked). The aclp pointer is assumed still to point to userland, so 72 * this should not be consumed within the kernel except by syscall code. 73 * Other code should directly invoke VOP_{SET,GET}ACL. 74 */ 75 76 /* 77 * Given a vnode, set its ACL. 78 */ 79 static int 80 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 81 struct acl *aclp) 82 { 83 struct acl inkernacl; 84 struct mount *mp; 85 int error; 86 87 error = copyin(aclp, &inkernacl, sizeof(struct acl)); 88 if (error) 89 return(error); 90 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 91 if (error != 0) 92 return (error); 93 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 94 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 95 #ifdef MAC 96 error = mac_check_vnode_setacl(td->td_ucred, vp, type, &inkernacl); 97 if (error != 0) 98 goto out; 99 #endif 100 error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); 101 #ifdef MAC 102 out: 103 #endif 104 VOP_UNLOCK(vp, 0, td); 105 vn_finished_write(mp); 106 return(error); 107 } 108 109 /* 110 * Given a vnode, get its ACL. 111 */ 112 static int 113 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 114 struct acl *aclp) 115 { 116 struct acl inkernelacl; 117 int error; 118 119 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 120 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 121 #ifdef MAC 122 error = mac_check_vnode_getacl(td->td_ucred, vp, type); 123 if (error != 0) 124 goto out; 125 #endif 126 error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); 127 #ifdef MAC 128 out: 129 #endif 130 VOP_UNLOCK(vp, 0, td); 131 if (error == 0) 132 error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 133 return (error); 134 } 135 136 /* 137 * Given a vnode, delete its ACL. 138 */ 139 static int 140 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 141 { 142 struct mount *mp; 143 int error; 144 145 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 146 if (error) 147 return (error); 148 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 149 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 150 #ifdef MAC 151 error = mac_check_vnode_deleteacl(td->td_ucred, vp, type); 152 if (error) 153 goto out; 154 #endif 155 error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 156 #ifdef MAC 157 out: 158 #endif 159 VOP_UNLOCK(vp, 0, td); 160 vn_finished_write(mp); 161 return (error); 162 } 163 164 /* 165 * Given a vnode, check whether an ACL is appropriate for it 166 */ 167 static int 168 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 169 struct acl *aclp) 170 { 171 struct acl inkernelacl; 172 int error; 173 174 error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 175 if (error) 176 return(error); 177 error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); 178 return (error); 179 } 180 181 /* 182 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 183 * need to lock, as the vacl_ code will get/release any locks required. 184 */ 185 186 /* 187 * Given a file path, get an ACL for it 188 * 189 * MPSAFE 190 */ 191 int 192 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 193 { 194 struct nameidata nd; 195 int vfslocked, error; 196 197 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 198 error = namei(&nd); 199 vfslocked = NDHASGIANT(&nd); 200 if (error == 0) { 201 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 202 NDFREE(&nd, 0); 203 } 204 VFS_UNLOCK_GIANT(vfslocked); 205 return (error); 206 } 207 208 /* 209 * Given a file path, get an ACL for it; don't follow links. 210 * 211 * MPSAFE 212 */ 213 int 214 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 215 { 216 struct nameidata nd; 217 int vfslocked, error; 218 219 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 220 error = namei(&nd); 221 vfslocked = NDHASGIANT(&nd); 222 if (error == 0) { 223 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 224 NDFREE(&nd, 0); 225 } 226 VFS_UNLOCK_GIANT(vfslocked); 227 return (error); 228 } 229 230 /* 231 * Given a file path, set an ACL for it 232 * 233 * MPSAFE 234 */ 235 int 236 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 237 { 238 struct nameidata nd; 239 int vfslocked, error; 240 241 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 242 error = namei(&nd); 243 vfslocked = NDHASGIANT(&nd); 244 if (error == 0) { 245 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 246 NDFREE(&nd, 0); 247 } 248 VFS_UNLOCK_GIANT(vfslocked); 249 return (error); 250 } 251 252 /* 253 * Given a file path, set an ACL for it; don't follow links. 254 * 255 * MPSAFE 256 */ 257 int 258 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 259 { 260 struct nameidata nd; 261 int vfslocked, error; 262 263 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 264 error = namei(&nd); 265 vfslocked = NDHASGIANT(&nd); 266 if (error == 0) { 267 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 268 NDFREE(&nd, 0); 269 } 270 VFS_UNLOCK_GIANT(vfslocked); 271 return (error); 272 } 273 274 /* 275 * Given a file descriptor, get an ACL for it 276 * 277 * MPSAFE 278 */ 279 int 280 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 281 { 282 struct file *fp; 283 int vfslocked, error; 284 285 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 286 if (error == 0) { 287 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 288 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 289 fdrop(fp, td); 290 VFS_UNLOCK_GIANT(vfslocked); 291 } 292 return (error); 293 } 294 295 /* 296 * Given a file descriptor, set an ACL for it 297 * 298 * MPSAFE 299 */ 300 int 301 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 302 { 303 struct file *fp; 304 int vfslocked, error; 305 306 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 307 if (error == 0) { 308 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 309 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 310 fdrop(fp, td); 311 VFS_UNLOCK_GIANT(vfslocked); 312 } 313 return (error); 314 } 315 316 /* 317 * Given a file path, delete an ACL from it. 318 * 319 * MPSAFE 320 */ 321 int 322 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 323 { 324 struct nameidata nd; 325 int vfslocked, error; 326 327 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 328 error = namei(&nd); 329 vfslocked = NDHASGIANT(&nd); 330 if (error == 0) { 331 error = vacl_delete(td, nd.ni_vp, uap->type); 332 NDFREE(&nd, 0); 333 } 334 VFS_UNLOCK_GIANT(vfslocked); 335 return (error); 336 } 337 338 /* 339 * Given a file path, delete an ACL from it; don't follow links. 340 * 341 * MPSAFE 342 */ 343 int 344 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 345 { 346 struct nameidata nd; 347 int vfslocked, error; 348 349 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 350 error = namei(&nd); 351 vfslocked = NDHASGIANT(&nd); 352 if (error == 0) { 353 error = vacl_delete(td, nd.ni_vp, uap->type); 354 NDFREE(&nd, 0); 355 } 356 VFS_UNLOCK_GIANT(vfslocked); 357 return (error); 358 } 359 360 /* 361 * Given a file path, delete an ACL from it. 362 * 363 * MPSAFE 364 */ 365 int 366 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 367 { 368 struct file *fp; 369 int vfslocked, error; 370 371 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 372 if (error == 0) { 373 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 374 error = vacl_delete(td, fp->f_vnode, uap->type); 375 fdrop(fp, td); 376 VFS_UNLOCK_GIANT(vfslocked); 377 } 378 return (error); 379 } 380 381 /* 382 * Given a file path, check an ACL for it 383 * 384 * MPSAFE 385 */ 386 int 387 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 388 { 389 struct nameidata nd; 390 int vfslocked, error; 391 392 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 393 error = namei(&nd); 394 vfslocked = NDHASGIANT(&nd); 395 if (error == 0) { 396 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 397 NDFREE(&nd, 0); 398 } 399 VFS_UNLOCK_GIANT(vfslocked); 400 return (error); 401 } 402 403 /* 404 * Given a file path, check an ACL for it; don't follow links. 405 * 406 * MPSAFE 407 */ 408 int 409 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 410 { 411 struct nameidata nd; 412 int vfslocked, error; 413 414 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 415 error = namei(&nd); 416 vfslocked = NDHASGIANT(&nd); 417 if (error == 0) { 418 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 419 NDFREE(&nd, 0); 420 } 421 VFS_UNLOCK_GIANT(vfslocked); 422 return (error); 423 } 424 425 /* 426 * Given a file descriptor, check an ACL for it 427 * 428 * MPSAFE 429 */ 430 int 431 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 432 { 433 struct file *fp; 434 int vfslocked, error; 435 436 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 437 if (error == 0) { 438 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 439 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 440 fdrop(fp, td); 441 VFS_UNLOCK_GIANT(vfslocked); 442 } 443 return (error); 444 } 445 446 /* ARGUSED */ 447 448 static void 449 aclinit(void *dummy __unused) 450 { 451 452 acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl), 453 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 454 } 455 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL) 456