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 70 * syscall code once the syscall has converted the path or file 71 * descriptor to a vnode (unlocked). The aclp pointer is assumed 72 * still to point to userland, so this should not be consumed within 73 * the kernel except by syscall code. Other code should directly 74 * invoke VOP_{SET,GET}ACL. 75 */ 76 77 /* 78 * Given a vnode, set its ACL. 79 */ 80 static int 81 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 82 struct acl *aclp) 83 { 84 struct acl inkernacl; 85 struct mount *mp; 86 int error; 87 88 error = copyin(aclp, &inkernacl, sizeof(struct acl)); 89 if (error) 90 return(error); 91 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 92 if (error != 0) 93 return (error); 94 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 95 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 96 #ifdef MAC 97 error = mac_check_vnode_setacl(td->td_ucred, vp, type, &inkernacl); 98 if (error != 0) 99 goto out; 100 #endif 101 error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); 102 #ifdef MAC 103 out: 104 #endif 105 VOP_UNLOCK(vp, 0, td); 106 vn_finished_write(mp); 107 return(error); 108 } 109 110 /* 111 * Given a vnode, get its ACL. 112 */ 113 static int 114 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 115 struct acl *aclp) 116 { 117 struct acl inkernelacl; 118 int error; 119 120 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 121 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 122 #ifdef MAC 123 error = mac_check_vnode_getacl(td->td_ucred, vp, type); 124 if (error != 0) 125 goto out; 126 #endif 127 error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); 128 #ifdef MAC 129 out: 130 #endif 131 VOP_UNLOCK(vp, 0, td); 132 if (error == 0) 133 error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 134 return (error); 135 } 136 137 /* 138 * Given a vnode, delete its ACL. 139 */ 140 static int 141 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 142 { 143 struct mount *mp; 144 int error; 145 146 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 147 if (error) 148 return (error); 149 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 150 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 151 #ifdef MAC 152 error = mac_check_vnode_deleteacl(td->td_ucred, vp, type); 153 if (error) 154 goto out; 155 #endif 156 error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 157 #ifdef MAC 158 out: 159 #endif 160 VOP_UNLOCK(vp, 0, td); 161 vn_finished_write(mp); 162 return (error); 163 } 164 165 /* 166 * Given a vnode, check whether an ACL is appropriate for it 167 */ 168 static int 169 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 170 struct acl *aclp) 171 { 172 struct acl inkernelacl; 173 int error; 174 175 error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 176 if (error) 177 return(error); 178 error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); 179 return (error); 180 } 181 182 /* 183 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. 184 * Don't need to lock, as the vacl_ code will get/release any locks 185 * required. 186 */ 187 188 /* 189 * Given a file path, get an ACL for it 190 * 191 * MPSAFE 192 */ 193 int 194 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 195 { 196 struct nameidata nd; 197 int vfslocked, error; 198 199 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 200 error = namei(&nd); 201 vfslocked = NDHASGIANT(&nd); 202 if (error == 0) { 203 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 204 NDFREE(&nd, 0); 205 } 206 VFS_UNLOCK_GIANT(vfslocked); 207 return (error); 208 } 209 210 /* 211 * Given a file path, get an ACL for it; don't follow links. 212 * 213 * MPSAFE 214 */ 215 int 216 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 217 { 218 struct nameidata nd; 219 int vfslocked, error; 220 221 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 222 error = namei(&nd); 223 vfslocked = NDHASGIANT(&nd); 224 if (error == 0) { 225 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 226 NDFREE(&nd, 0); 227 } 228 VFS_UNLOCK_GIANT(vfslocked); 229 return (error); 230 } 231 232 /* 233 * Given a file path, set an ACL for it 234 * 235 * MPSAFE 236 */ 237 int 238 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 239 { 240 struct nameidata nd; 241 int vfslocked, error; 242 243 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 244 error = namei(&nd); 245 vfslocked = NDHASGIANT(&nd); 246 if (error == 0) { 247 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 248 NDFREE(&nd, 0); 249 } 250 VFS_UNLOCK_GIANT(vfslocked); 251 return (error); 252 } 253 254 /* 255 * Given a file path, set an ACL for it; don't follow links. 256 * 257 * MPSAFE 258 */ 259 int 260 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 261 { 262 struct nameidata nd; 263 int vfslocked, error; 264 265 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 266 error = namei(&nd); 267 vfslocked = NDHASGIANT(&nd); 268 if (error == 0) { 269 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 270 NDFREE(&nd, 0); 271 } 272 VFS_UNLOCK_GIANT(vfslocked); 273 return (error); 274 } 275 276 /* 277 * Given a file descriptor, get an ACL for it 278 * 279 * MPSAFE 280 */ 281 int 282 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 283 { 284 struct file *fp; 285 int vfslocked, error; 286 287 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 288 if (error == 0) { 289 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 290 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 291 fdrop(fp, td); 292 VFS_UNLOCK_GIANT(vfslocked); 293 } 294 return (error); 295 } 296 297 /* 298 * Given a file descriptor, set an ACL for it 299 * 300 * MPSAFE 301 */ 302 int 303 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 304 { 305 struct file *fp; 306 int vfslocked, error; 307 308 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 309 if (error == 0) { 310 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 311 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 312 fdrop(fp, td); 313 VFS_UNLOCK_GIANT(vfslocked); 314 } 315 return (error); 316 } 317 318 /* 319 * Given a file path, delete an ACL from it. 320 * 321 * MPSAFE 322 */ 323 int 324 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 325 { 326 struct nameidata nd; 327 int vfslocked, error; 328 329 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 330 error = namei(&nd); 331 vfslocked = NDHASGIANT(&nd); 332 if (error == 0) { 333 error = vacl_delete(td, nd.ni_vp, uap->type); 334 NDFREE(&nd, 0); 335 } 336 VFS_UNLOCK_GIANT(vfslocked); 337 return (error); 338 } 339 340 /* 341 * Given a file path, delete an ACL from it; don't follow links. 342 * 343 * MPSAFE 344 */ 345 int 346 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 347 { 348 struct nameidata nd; 349 int vfslocked, error; 350 351 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 352 error = namei(&nd); 353 vfslocked = NDHASGIANT(&nd); 354 if (error == 0) { 355 error = vacl_delete(td, nd.ni_vp, uap->type); 356 NDFREE(&nd, 0); 357 } 358 VFS_UNLOCK_GIANT(vfslocked); 359 return (error); 360 } 361 362 /* 363 * Given a file path, delete an ACL from it. 364 * 365 * MPSAFE 366 */ 367 int 368 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 369 { 370 struct file *fp; 371 int vfslocked, error; 372 373 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 374 if (error == 0) { 375 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 376 error = vacl_delete(td, fp->f_vnode, uap->type); 377 fdrop(fp, td); 378 VFS_UNLOCK_GIANT(vfslocked); 379 } 380 return (error); 381 } 382 383 /* 384 * Given a file path, check an ACL for it 385 * 386 * MPSAFE 387 */ 388 int 389 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 390 { 391 struct nameidata nd; 392 int vfslocked, error; 393 394 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 395 error = namei(&nd); 396 vfslocked = NDHASGIANT(&nd); 397 if (error == 0) { 398 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 399 NDFREE(&nd, 0); 400 } 401 VFS_UNLOCK_GIANT(vfslocked); 402 return (error); 403 } 404 405 /* 406 * Given a file path, check an ACL for it; don't follow links. 407 * 408 * MPSAFE 409 */ 410 int 411 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 412 { 413 struct nameidata nd; 414 int vfslocked, error; 415 416 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 417 error = namei(&nd); 418 vfslocked = NDHASGIANT(&nd); 419 if (error == 0) { 420 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 421 NDFREE(&nd, 0); 422 } 423 VFS_UNLOCK_GIANT(vfslocked); 424 return (error); 425 } 426 427 /* 428 * Given a file descriptor, check an ACL for it 429 * 430 * MPSAFE 431 */ 432 int 433 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 434 { 435 struct file *fp; 436 int vfslocked, error; 437 438 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 439 if (error == 0) { 440 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 441 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 442 fdrop(fp, td); 443 VFS_UNLOCK_GIANT(vfslocked); 444 } 445 return (error); 446 } 447 448 /* ARGUSED */ 449 450 static void 451 aclinit(void *dummy __unused) 452 { 453 454 acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl), 455 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 456 } 457 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL) 458