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