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 <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/sysproto.h> 41 #include <sys/capability.h> 42 #include <sys/fcntl.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 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES); 59 60 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 61 62 static int vacl_set_acl(struct thread *td, struct vnode *vp, 63 acl_type_t type, struct acl *aclp); 64 static int vacl_get_acl(struct thread *td, struct vnode *vp, 65 acl_type_t type, struct acl *aclp); 66 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 67 acl_type_t type, struct acl *aclp); 68 69 int 70 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest) 71 { 72 int i; 73 74 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) 75 return (EINVAL); 76 77 bzero(dest, sizeof(*dest)); 78 79 dest->acl_cnt = source->acl_cnt; 80 dest->acl_maxcnt = ACL_MAX_ENTRIES; 81 82 for (i = 0; i < dest->acl_cnt; i++) { 83 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 84 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 85 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 86 } 87 88 return (0); 89 } 90 91 int 92 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest) 93 { 94 int i; 95 96 if (source->acl_cnt > OLDACL_MAX_ENTRIES) 97 return (EINVAL); 98 99 bzero(dest, sizeof(*dest)); 100 101 dest->acl_cnt = source->acl_cnt; 102 103 for (i = 0; i < dest->acl_cnt; i++) { 104 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 105 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 106 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 107 } 108 109 return (0); 110 } 111 112 /* 113 * At one time, "struct ACL" was extended in order to add support for NFSv4 114 * ACLs. Instead of creating compatibility versions of all the ACL-related 115 * syscalls, they were left intact. It's possible to find out what the code 116 * calling these syscalls (libc) expects basing on "type" argument - if it's 117 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were 118 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct 119 * oldacl". If it's something else, then it's the new "struct acl". In the 120 * latter case, the routines below just copyin/copyout the contents. In the 121 * former case, they copyin the "struct oldacl" and convert it to the new 122 * format. 123 */ 124 static int 125 acl_copyin(void *user_acl, struct acl *kernel_acl, acl_type_t type) 126 { 127 int error; 128 struct oldacl old; 129 130 switch (type) { 131 case ACL_TYPE_ACCESS_OLD: 132 case ACL_TYPE_DEFAULT_OLD: 133 error = copyin(user_acl, &old, sizeof(old)); 134 if (error != 0) 135 break; 136 acl_copy_oldacl_into_acl(&old, kernel_acl); 137 break; 138 139 default: 140 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl)); 141 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES) 142 return (EINVAL); 143 } 144 145 return (error); 146 } 147 148 static int 149 acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type) 150 { 151 int error; 152 struct oldacl old; 153 154 switch (type) { 155 case ACL_TYPE_ACCESS_OLD: 156 case ACL_TYPE_DEFAULT_OLD: 157 error = acl_copy_acl_into_oldacl(kernel_acl, &old); 158 if (error != 0) 159 break; 160 161 error = copyout(&old, user_acl, sizeof(old)); 162 break; 163 164 default: 165 if (fuword32((char *)user_acl + 166 offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES) 167 return (EINVAL); 168 169 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl)); 170 } 171 172 return (error); 173 } 174 175 /* 176 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" 177 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work 178 * with new kernel. Fixing 'type' for old binaries with new libc 179 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). 180 */ 181 static int 182 acl_type_unold(int type) 183 { 184 switch (type) { 185 case ACL_TYPE_ACCESS_OLD: 186 return (ACL_TYPE_ACCESS); 187 188 case ACL_TYPE_DEFAULT_OLD: 189 return (ACL_TYPE_DEFAULT); 190 191 default: 192 return (type); 193 } 194 } 195 196 /* 197 * These calls wrap the real vnode operations, and are called by the syscall 198 * code once the syscall has converted the path or file descriptor to a vnode 199 * (unlocked). The aclp pointer is assumed still to point to userland, so 200 * this should not be consumed within the kernel except by syscall code. 201 * Other code should directly invoke VOP_{SET,GET}ACL. 202 */ 203 204 /* 205 * Given a vnode, set its ACL. 206 */ 207 static int 208 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 209 struct acl *aclp) 210 { 211 struct acl *inkernelacl; 212 struct mount *mp; 213 int error; 214 215 inkernelacl = acl_alloc(M_WAITOK); 216 error = acl_copyin(aclp, inkernelacl, type); 217 if (error != 0) 218 goto out; 219 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 220 if (error != 0) 221 goto out; 222 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 223 #ifdef MAC 224 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); 225 if (error != 0) 226 goto out_unlock; 227 #endif 228 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, 229 td->td_ucred, td); 230 #ifdef MAC 231 out_unlock: 232 #endif 233 VOP_UNLOCK(vp, 0); 234 vn_finished_write(mp); 235 out: 236 acl_free(inkernelacl); 237 return (error); 238 } 239 240 /* 241 * Given a vnode, get its ACL. 242 */ 243 static int 244 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 245 struct acl *aclp) 246 { 247 struct acl *inkernelacl; 248 int error; 249 250 inkernelacl = acl_alloc(M_WAITOK); 251 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 252 #ifdef MAC 253 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 254 if (error != 0) 255 goto out; 256 #endif 257 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, 258 td->td_ucred, td); 259 260 #ifdef MAC 261 out: 262 #endif 263 VOP_UNLOCK(vp, 0); 264 if (error == 0) 265 error = acl_copyout(inkernelacl, aclp, type); 266 acl_free(inkernelacl); 267 return (error); 268 } 269 270 /* 271 * Given a vnode, delete its ACL. 272 */ 273 static int 274 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 275 { 276 struct mount *mp; 277 int error; 278 279 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 280 if (error != 0) 281 return (error); 282 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 283 #ifdef MAC 284 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 285 if (error != 0) 286 goto out; 287 #endif 288 error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); 289 #ifdef MAC 290 out: 291 #endif 292 VOP_UNLOCK(vp, 0); 293 vn_finished_write(mp); 294 return (error); 295 } 296 297 /* 298 * Given a vnode, check whether an ACL is appropriate for it 299 */ 300 static int 301 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 302 struct acl *aclp) 303 { 304 struct acl *inkernelacl; 305 int error; 306 307 inkernelacl = acl_alloc(M_WAITOK); 308 error = acl_copyin(aclp, inkernelacl, type); 309 if (error != 0) 310 goto out; 311 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl, 312 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 sys___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 sys___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 sys___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 sys___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 sys___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, CAP_ACL_GET, &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 sys___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, CAP_ACL_SET, &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 sys___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 sys___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 sys___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, CAP_ACL_DELETE, 491 &fp); 492 if (error == 0) { 493 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 494 error = vacl_delete(td, fp->f_vnode, uap->type); 495 fdrop(fp, td); 496 VFS_UNLOCK_GIANT(vfslocked); 497 } 498 return (error); 499 } 500 501 /* 502 * Given a file path, check an ACL for it. 503 */ 504 int 505 sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 506 { 507 struct nameidata nd; 508 int vfslocked, error; 509 510 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 511 error = namei(&nd); 512 vfslocked = NDHASGIANT(&nd); 513 if (error == 0) { 514 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 515 NDFREE(&nd, 0); 516 } 517 VFS_UNLOCK_GIANT(vfslocked); 518 return (error); 519 } 520 521 /* 522 * Given a file path, check an ACL for it; don't follow links. 523 */ 524 int 525 sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 526 { 527 struct nameidata nd; 528 int vfslocked, error; 529 530 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 531 error = namei(&nd); 532 vfslocked = NDHASGIANT(&nd); 533 if (error == 0) { 534 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 535 NDFREE(&nd, 0); 536 } 537 VFS_UNLOCK_GIANT(vfslocked); 538 return (error); 539 } 540 541 /* 542 * Given a file descriptor, check an ACL for it. 543 */ 544 int 545 sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 546 { 547 struct file *fp; 548 int vfslocked, error; 549 550 error = getvnode(td->td_proc->p_fd, uap->filedes, CAP_ACL_CHECK, 551 &fp); 552 if (error == 0) { 553 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 554 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 555 fdrop(fp, td); 556 VFS_UNLOCK_GIANT(vfslocked); 557 } 558 return (error); 559 } 560 561 struct acl * 562 acl_alloc(int flags) 563 { 564 struct acl *aclp; 565 566 aclp = malloc(sizeof(*aclp), M_ACL, flags); 567 aclp->acl_maxcnt = ACL_MAX_ENTRIES; 568 569 return (aclp); 570 } 571 572 void 573 acl_free(struct acl *aclp) 574 { 575 576 free(aclp, M_ACL); 577 } 578