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/fcntl.h> 44 #include <sys/kernel.h> 45 #include <sys/malloc.h> 46 #include <sys/mount.h> 47 #include <sys/vnode.h> 48 #include <sys/lock.h> 49 #include <sys/mutex.h> 50 #include <sys/namei.h> 51 #include <sys/file.h> 52 #include <sys/filedesc.h> 53 #include <sys/proc.h> 54 #include <sys/sysent.h> 55 #include <sys/acl.h> 56 57 #include <security/mac/mac_framework.h> 58 59 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES); 60 61 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 62 63 static int vacl_set_acl(struct thread *td, struct vnode *vp, 64 acl_type_t type, struct acl *aclp); 65 static int vacl_get_acl(struct thread *td, struct vnode *vp, 66 acl_type_t type, struct acl *aclp); 67 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 68 acl_type_t type, struct acl *aclp); 69 70 int 71 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest) 72 { 73 int i; 74 75 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) 76 return (EINVAL); 77 78 bzero(dest, sizeof(*dest)); 79 80 dest->acl_cnt = source->acl_cnt; 81 dest->acl_maxcnt = ACL_MAX_ENTRIES; 82 83 for (i = 0; i < dest->acl_cnt; i++) { 84 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 85 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 86 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 87 } 88 89 return (0); 90 } 91 92 int 93 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest) 94 { 95 int i; 96 97 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) 98 return (EINVAL); 99 100 bzero(dest, sizeof(*dest)); 101 102 dest->acl_cnt = source->acl_cnt; 103 104 for (i = 0; i < dest->acl_cnt; i++) { 105 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 106 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 107 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 108 } 109 110 return (0); 111 } 112 113 /* 114 * At one time, "struct ACL" was extended in order to add support for NFSv4 115 * ACLs. Instead of creating compatibility versions of all the ACL-related 116 * syscalls, they were left intact. It's possible to find out what the code 117 * calling these syscalls (libc) expects basing on "type" argument - if it's 118 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were 119 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct 120 * oldacl". If it's something else, then it's the new "struct acl". In the 121 * latter case, the routines below just copyin/copyout the contents. In the 122 * former case, they copyin the "struct oldacl" and convert it to the new 123 * format. 124 */ 125 static int 126 acl_copyin(void *user_acl, struct acl *kernel_acl, acl_type_t type) 127 { 128 int error; 129 struct oldacl old; 130 131 switch (type) { 132 case ACL_TYPE_ACCESS_OLD: 133 case ACL_TYPE_DEFAULT_OLD: 134 error = copyin(user_acl, &old, sizeof(old)); 135 if (error != 0) 136 break; 137 acl_copy_oldacl_into_acl(&old, kernel_acl); 138 break; 139 140 default: 141 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl)); 142 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES) 143 return (EINVAL); 144 } 145 146 return (error); 147 } 148 149 static int 150 acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type) 151 { 152 int error; 153 struct oldacl old; 154 155 switch (type) { 156 case ACL_TYPE_ACCESS_OLD: 157 case ACL_TYPE_DEFAULT_OLD: 158 error = acl_copy_acl_into_oldacl(kernel_acl, &old); 159 if (error != 0) 160 break; 161 162 error = copyout(&old, user_acl, sizeof(old)); 163 break; 164 165 default: 166 if (fuword((char *)user_acl + 167 offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES) 168 return (EINVAL); 169 170 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl)); 171 } 172 173 return (error); 174 } 175 176 /* 177 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" 178 * counterpart. It's required for old (pre-NFS4 ACLs) libc to work 179 * with new kernel. Fixing 'type' for old binaries with new libc 180 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). 181 */ 182 static int 183 acl_type_unold(int type) 184 { 185 switch (type) { 186 case ACL_TYPE_ACCESS_OLD: 187 return (ACL_TYPE_ACCESS); 188 189 case ACL_TYPE_DEFAULT_OLD: 190 return (ACL_TYPE_DEFAULT); 191 192 default: 193 return (type); 194 } 195 } 196 197 /* 198 * These calls wrap the real vnode operations, and are called by the syscall 199 * code once the syscall has converted the path or file descriptor to a vnode 200 * (unlocked). The aclp pointer is assumed still to point to userland, so 201 * this should not be consumed within the kernel except by syscall code. 202 * Other code should directly invoke VOP_{SET,GET}ACL. 203 */ 204 205 /* 206 * Given a vnode, set its ACL. 207 */ 208 static int 209 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 210 struct acl *aclp) 211 { 212 struct acl *inkernelacl; 213 struct mount *mp; 214 int error; 215 216 inkernelacl = acl_alloc(M_WAITOK); 217 error = acl_copyin(aclp, inkernelacl, type); 218 if (error) 219 goto out; 220 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 221 if (error != 0) 222 goto out; 223 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 224 #ifdef MAC 225 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); 226 if (error != 0) 227 goto out_unlock; 228 #endif 229 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, 230 td->td_ucred, td); 231 #ifdef MAC 232 out_unlock: 233 #endif 234 VOP_UNLOCK(vp, 0); 235 vn_finished_write(mp); 236 out: 237 acl_free(inkernelacl); 238 return(error); 239 } 240 241 /* 242 * Given a vnode, get its ACL. 243 */ 244 static int 245 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 246 struct acl *aclp) 247 { 248 struct acl *inkernelacl; 249 int error; 250 251 inkernelacl = acl_alloc(M_WAITOK); 252 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 253 #ifdef MAC 254 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 255 if (error != 0) 256 goto out; 257 #endif 258 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, 259 td->td_ucred, td); 260 261 #ifdef MAC 262 out: 263 #endif 264 VOP_UNLOCK(vp, 0); 265 if (error == 0) 266 error = acl_copyout(inkernelacl, aclp, type); 267 acl_free(inkernelacl); 268 return (error); 269 } 270 271 /* 272 * Given a vnode, delete its ACL. 273 */ 274 static int 275 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 276 { 277 struct mount *mp; 278 int error; 279 280 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 281 if (error) 282 return (error); 283 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 284 #ifdef MAC 285 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 286 if (error) 287 goto out; 288 #endif 289 error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); 290 #ifdef MAC 291 out: 292 #endif 293 VOP_UNLOCK(vp, 0); 294 vn_finished_write(mp); 295 return (error); 296 } 297 298 /* 299 * Given a vnode, check whether an ACL is appropriate for it 300 */ 301 static int 302 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 303 struct acl *aclp) 304 { 305 struct acl *inkernelacl; 306 int error; 307 308 inkernelacl = acl_alloc(M_WAITOK); 309 error = acl_copyin(aclp, inkernelacl, type); 310 if (error) 311 goto out; 312 error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td); 313 out: 314 acl_free(inkernelacl); 315 return (error); 316 } 317 318 /* 319 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 320 * need to lock, as the vacl_ code will get/release any locks required. 321 */ 322 323 /* 324 * Given a file path, get an ACL for it 325 */ 326 int 327 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 328 { 329 struct nameidata nd; 330 int vfslocked, error; 331 332 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 333 error = namei(&nd); 334 vfslocked = NDHASGIANT(&nd); 335 if (error == 0) { 336 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 337 NDFREE(&nd, 0); 338 } 339 VFS_UNLOCK_GIANT(vfslocked); 340 return (error); 341 } 342 343 /* 344 * Given a file path, get an ACL for it; don't follow links. 345 */ 346 int 347 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 348 { 349 struct nameidata nd; 350 int vfslocked, error; 351 352 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 353 error = namei(&nd); 354 vfslocked = NDHASGIANT(&nd); 355 if (error == 0) { 356 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 357 NDFREE(&nd, 0); 358 } 359 VFS_UNLOCK_GIANT(vfslocked); 360 return (error); 361 } 362 363 /* 364 * Given a file path, set an ACL for it. 365 */ 366 int 367 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 368 { 369 struct nameidata nd; 370 int vfslocked, error; 371 372 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 373 error = namei(&nd); 374 vfslocked = NDHASGIANT(&nd); 375 if (error == 0) { 376 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 377 NDFREE(&nd, 0); 378 } 379 VFS_UNLOCK_GIANT(vfslocked); 380 return (error); 381 } 382 383 /* 384 * Given a file path, set an ACL for it; don't follow links. 385 */ 386 int 387 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 388 { 389 struct nameidata nd; 390 int vfslocked, error; 391 392 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 393 error = namei(&nd); 394 vfslocked = NDHASGIANT(&nd); 395 if (error == 0) { 396 error = vacl_set_acl(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 descriptor, get an ACL for it. 405 */ 406 int 407 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 408 { 409 struct file *fp; 410 int vfslocked, error; 411 412 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 413 if (error == 0) { 414 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 415 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 416 fdrop(fp, td); 417 VFS_UNLOCK_GIANT(vfslocked); 418 } 419 return (error); 420 } 421 422 /* 423 * Given a file descriptor, set an ACL for it. 424 */ 425 int 426 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 427 { 428 struct file *fp; 429 int vfslocked, error; 430 431 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 432 if (error == 0) { 433 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 434 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 435 fdrop(fp, td); 436 VFS_UNLOCK_GIANT(vfslocked); 437 } 438 return (error); 439 } 440 441 /* 442 * Given a file path, delete an ACL from it. 443 */ 444 int 445 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 446 { 447 struct nameidata nd; 448 int vfslocked, error; 449 450 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 451 error = namei(&nd); 452 vfslocked = NDHASGIANT(&nd); 453 if (error == 0) { 454 error = vacl_delete(td, nd.ni_vp, uap->type); 455 NDFREE(&nd, 0); 456 } 457 VFS_UNLOCK_GIANT(vfslocked); 458 return (error); 459 } 460 461 /* 462 * Given a file path, delete an ACL from it; don't follow links. 463 */ 464 int 465 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 466 { 467 struct nameidata nd; 468 int vfslocked, error; 469 470 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 471 error = namei(&nd); 472 vfslocked = NDHASGIANT(&nd); 473 if (error == 0) { 474 error = vacl_delete(td, nd.ni_vp, uap->type); 475 NDFREE(&nd, 0); 476 } 477 VFS_UNLOCK_GIANT(vfslocked); 478 return (error); 479 } 480 481 /* 482 * Given a file path, delete an ACL from it. 483 */ 484 int 485 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 486 { 487 struct file *fp; 488 int vfslocked, error; 489 490 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 491 if (error == 0) { 492 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 493 error = vacl_delete(td, fp->f_vnode, uap->type); 494 fdrop(fp, td); 495 VFS_UNLOCK_GIANT(vfslocked); 496 } 497 return (error); 498 } 499 500 /* 501 * Given a file path, check an ACL for it. 502 */ 503 int 504 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 505 { 506 struct nameidata nd; 507 int vfslocked, error; 508 509 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 510 error = namei(&nd); 511 vfslocked = NDHASGIANT(&nd); 512 if (error == 0) { 513 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 514 NDFREE(&nd, 0); 515 } 516 VFS_UNLOCK_GIANT(vfslocked); 517 return (error); 518 } 519 520 /* 521 * Given a file path, check an ACL for it; don't follow links. 522 */ 523 int 524 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 525 { 526 struct nameidata nd; 527 int vfslocked, error; 528 529 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 530 error = namei(&nd); 531 vfslocked = NDHASGIANT(&nd); 532 if (error == 0) { 533 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 534 NDFREE(&nd, 0); 535 } 536 VFS_UNLOCK_GIANT(vfslocked); 537 return (error); 538 } 539 540 /* 541 * Given a file descriptor, check an ACL for it. 542 */ 543 int 544 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 545 { 546 struct file *fp; 547 int vfslocked, error; 548 549 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 550 if (error == 0) { 551 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 552 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 553 fdrop(fp, td); 554 VFS_UNLOCK_GIANT(vfslocked); 555 } 556 return (error); 557 } 558 559 struct acl * 560 acl_alloc(int flags) 561 { 562 struct acl *aclp; 563 564 aclp = malloc(sizeof(*aclp), M_ACL, flags); 565 aclp->acl_maxcnt = ACL_MAX_ENTRIES; 566 567 return (aclp); 568 } 569 570 void 571 acl_free(struct acl *aclp) 572 { 573 574 free(aclp, M_ACL); 575 } 576