1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 1999-2006, 2016-2017 Robert N. M. Watson 5 * All rights reserved. 6 * 7 * This software was developed by Robert Watson for the TrustedBSD Project. 8 * 9 * Portions of this software were developed by BAE Systems, the University of 10 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL 11 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent 12 * Computing (TC) research program. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 /* 36 * Developed by the TrustedBSD Project. 37 * 38 * ACL system calls and other functions common across different ACL types. 39 * Type-specific routines go into subr_acl_<type>.c. 40 */ 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/sysproto.h> 48 #include <sys/capsicum.h> 49 #include <sys/fcntl.h> 50 #include <sys/kernel.h> 51 #include <sys/malloc.h> 52 #include <sys/mount.h> 53 #include <sys/vnode.h> 54 #include <sys/lock.h> 55 #include <sys/mutex.h> 56 #include <sys/namei.h> 57 #include <sys/file.h> 58 #include <sys/filedesc.h> 59 #include <sys/proc.h> 60 #include <sys/sysent.h> 61 #include <sys/acl.h> 62 63 #include <security/audit/audit.h> 64 #include <security/mac/mac_framework.h> 65 66 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES); 67 68 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 69 70 71 static int kern___acl_aclcheck_path(struct thread *td, const char *path, 72 acl_type_t type, struct acl *aclp, int follow); 73 static int kern___acl_delete_path(struct thread *td, const char *path, 74 acl_type_t type, int follow); 75 static int kern___acl_get_path(struct thread *td, const char *path, 76 acl_type_t type, struct acl *aclp, int follow); 77 static int kern___acl_set_path(struct thread *td, const char *path, 78 acl_type_t type, const struct acl *aclp, int follow); 79 static int vacl_set_acl(struct thread *td, struct vnode *vp, 80 acl_type_t type, const struct acl *aclp); 81 static int vacl_get_acl(struct thread *td, struct vnode *vp, 82 acl_type_t type, struct acl *aclp); 83 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 84 acl_type_t type, const struct acl *aclp); 85 86 int 87 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest) 88 { 89 int i; 90 91 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) 92 return (EINVAL); 93 94 bzero(dest, sizeof(*dest)); 95 96 dest->acl_cnt = source->acl_cnt; 97 dest->acl_maxcnt = ACL_MAX_ENTRIES; 98 99 for (i = 0; i < dest->acl_cnt; i++) { 100 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 101 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 102 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 103 } 104 105 return (0); 106 } 107 108 int 109 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest) 110 { 111 int i; 112 113 if (source->acl_cnt > OLDACL_MAX_ENTRIES) 114 return (EINVAL); 115 116 bzero(dest, sizeof(*dest)); 117 118 dest->acl_cnt = source->acl_cnt; 119 120 for (i = 0; i < dest->acl_cnt; i++) { 121 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 122 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 123 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 124 } 125 126 return (0); 127 } 128 129 /* 130 * At one time, "struct ACL" was extended in order to add support for NFSv4 131 * ACLs. Instead of creating compatibility versions of all the ACL-related 132 * syscalls, they were left intact. It's possible to find out what the code 133 * calling these syscalls (libc) expects basing on "type" argument - if it's 134 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were 135 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct 136 * oldacl". If it's something else, then it's the new "struct acl". In the 137 * latter case, the routines below just copyin/copyout the contents. In the 138 * former case, they copyin the "struct oldacl" and convert it to the new 139 * format. 140 */ 141 static int 142 acl_copyin(const void *user_acl, struct acl *kernel_acl, acl_type_t type) 143 { 144 int error; 145 struct oldacl old; 146 147 switch (type) { 148 case ACL_TYPE_ACCESS_OLD: 149 case ACL_TYPE_DEFAULT_OLD: 150 error = copyin(user_acl, &old, sizeof(old)); 151 if (error != 0) 152 break; 153 acl_copy_oldacl_into_acl(&old, kernel_acl); 154 break; 155 156 default: 157 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl)); 158 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES) 159 return (EINVAL); 160 } 161 162 return (error); 163 } 164 165 static int 166 acl_copyout(const struct acl *kernel_acl, void *user_acl, acl_type_t type) 167 { 168 uint32_t am; 169 int error; 170 struct oldacl old; 171 172 switch (type) { 173 case ACL_TYPE_ACCESS_OLD: 174 case ACL_TYPE_DEFAULT_OLD: 175 error = acl_copy_acl_into_oldacl(kernel_acl, &old); 176 if (error != 0) 177 break; 178 179 error = copyout(&old, user_acl, sizeof(old)); 180 break; 181 182 default: 183 error = fueword32((char *)user_acl + 184 offsetof(struct acl, acl_maxcnt), &am); 185 if (error == -1) 186 return (EFAULT); 187 if (am != ACL_MAX_ENTRIES) 188 return (EINVAL); 189 190 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl)); 191 } 192 193 return (error); 194 } 195 196 /* 197 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" 198 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work 199 * with new kernel. Fixing 'type' for old binaries with new libc 200 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). 201 */ 202 static int 203 acl_type_unold(int type) 204 { 205 switch (type) { 206 case ACL_TYPE_ACCESS_OLD: 207 return (ACL_TYPE_ACCESS); 208 209 case ACL_TYPE_DEFAULT_OLD: 210 return (ACL_TYPE_DEFAULT); 211 212 default: 213 return (type); 214 } 215 } 216 217 /* 218 * These calls wrap the real vnode operations, and are called by the syscall 219 * code once the syscall has converted the path or file descriptor to a vnode 220 * (unlocked). The aclp pointer is assumed still to point to userland, so 221 * this should not be consumed within the kernel except by syscall code. 222 * Other code should directly invoke VOP_{SET,GET}ACL. 223 */ 224 225 /* 226 * Given a vnode, set its ACL. 227 */ 228 static int 229 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 230 const struct acl *aclp) 231 { 232 struct acl *inkernelacl; 233 struct mount *mp; 234 int error; 235 236 AUDIT_ARG_VALUE(type); 237 inkernelacl = acl_alloc(M_WAITOK); 238 error = acl_copyin(aclp, inkernelacl, type); 239 if (error != 0) 240 goto out; 241 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 242 if (error != 0) 243 goto out; 244 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 245 AUDIT_ARG_VNODE1(vp); 246 #ifdef MAC 247 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); 248 if (error != 0) 249 goto out_unlock; 250 #endif 251 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, 252 td->td_ucred, td); 253 #ifdef MAC 254 out_unlock: 255 #endif 256 VOP_UNLOCK(vp, 0); 257 vn_finished_write(mp); 258 out: 259 acl_free(inkernelacl); 260 return (error); 261 } 262 263 /* 264 * Given a vnode, get its ACL. 265 */ 266 static int 267 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 268 struct acl *aclp) 269 { 270 struct acl *inkernelacl; 271 int error; 272 273 AUDIT_ARG_VALUE(type); 274 inkernelacl = acl_alloc(M_WAITOK | M_ZERO); 275 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 276 AUDIT_ARG_VNODE1(vp); 277 #ifdef MAC 278 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 279 if (error != 0) 280 goto out; 281 #endif 282 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, 283 td->td_ucred, td); 284 285 #ifdef MAC 286 out: 287 #endif 288 VOP_UNLOCK(vp, 0); 289 if (error == 0) 290 error = acl_copyout(inkernelacl, aclp, type); 291 acl_free(inkernelacl); 292 return (error); 293 } 294 295 /* 296 * Given a vnode, delete its ACL. 297 */ 298 static int 299 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 300 { 301 struct mount *mp; 302 int error; 303 304 AUDIT_ARG_VALUE(type); 305 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 306 if (error != 0) 307 return (error); 308 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 309 AUDIT_ARG_VNODE1(vp); 310 #ifdef MAC 311 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 312 if (error != 0) 313 goto out; 314 #endif 315 error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); 316 #ifdef MAC 317 out: 318 #endif 319 VOP_UNLOCK(vp, 0); 320 vn_finished_write(mp); 321 return (error); 322 } 323 324 /* 325 * Given a vnode, check whether an ACL is appropriate for it 326 * 327 * XXXRW: No vnode lock held so can't audit vnode state...? 328 */ 329 static int 330 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 331 const struct acl *aclp) 332 { 333 struct acl *inkernelacl; 334 int error; 335 336 inkernelacl = acl_alloc(M_WAITOK); 337 error = acl_copyin(aclp, inkernelacl, type); 338 if (error != 0) 339 goto out; 340 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl, 341 td->td_ucred, td); 342 out: 343 acl_free(inkernelacl); 344 return (error); 345 } 346 347 /* 348 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 349 * need to lock, as the vacl_ code will get/release any locks required. 350 */ 351 352 /* 353 * Given a file path, get an ACL for it 354 */ 355 int 356 sys___acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 357 { 358 359 return (kern___acl_get_path(td, uap->path, uap->type, uap->aclp, 360 FOLLOW)); 361 } 362 363 /* 364 * Given a file path, get an ACL for it; don't follow links. 365 */ 366 int 367 sys___acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 368 { 369 370 return(kern___acl_get_path(td, uap->path, uap->type, uap->aclp, 371 NOFOLLOW)); 372 } 373 374 static int 375 kern___acl_get_path(struct thread *td, const char *path, acl_type_t type, 376 struct acl *aclp, int follow) 377 { 378 struct nameidata nd; 379 int error; 380 381 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td); 382 error = namei(&nd); 383 if (error == 0) { 384 error = vacl_get_acl(td, nd.ni_vp, type, aclp); 385 NDFREE(&nd, 0); 386 } 387 return (error); 388 } 389 390 /* 391 * Given a file path, set an ACL for it. 392 */ 393 int 394 sys___acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 395 { 396 397 return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp, 398 FOLLOW)); 399 } 400 401 /* 402 * Given a file path, set an ACL for it; don't follow links. 403 */ 404 int 405 sys___acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 406 { 407 408 return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp, 409 NOFOLLOW)); 410 } 411 412 static int 413 kern___acl_set_path(struct thread *td, const char *path, 414 acl_type_t type, const struct acl *aclp, int follow) 415 { 416 struct nameidata nd; 417 int error; 418 419 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td); 420 error = namei(&nd); 421 if (error == 0) { 422 error = vacl_set_acl(td, nd.ni_vp, type, aclp); 423 NDFREE(&nd, 0); 424 } 425 return (error); 426 } 427 428 /* 429 * Given a file descriptor, get an ACL for it. 430 */ 431 int 432 sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 433 { 434 struct file *fp; 435 cap_rights_t rights; 436 int error; 437 438 AUDIT_ARG_FD(uap->filedes); 439 error = getvnode(td, uap->filedes, 440 cap_rights_init(&rights, CAP_ACL_GET), &fp); 441 if (error == 0) { 442 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 443 fdrop(fp, td); 444 } 445 return (error); 446 } 447 448 /* 449 * Given a file descriptor, set an ACL for it. 450 */ 451 int 452 sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 453 { 454 struct file *fp; 455 cap_rights_t rights; 456 int error; 457 458 AUDIT_ARG_FD(uap->filedes); 459 error = getvnode(td, uap->filedes, 460 cap_rights_init(&rights, CAP_ACL_SET), &fp); 461 if (error == 0) { 462 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 463 fdrop(fp, td); 464 } 465 return (error); 466 } 467 468 /* 469 * Given a file path, delete an ACL from it. 470 */ 471 int 472 sys___acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 473 { 474 475 return (kern___acl_delete_path(td, uap->path, uap->type, FOLLOW)); 476 } 477 478 /* 479 * Given a file path, delete an ACL from it; don't follow links. 480 */ 481 int 482 sys___acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 483 { 484 485 return (kern___acl_delete_path(td, uap->path, uap->type, NOFOLLOW)); 486 } 487 488 static int 489 kern___acl_delete_path(struct thread *td, const char *path, 490 acl_type_t type, int follow) 491 { 492 struct nameidata nd; 493 int error; 494 495 NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td); 496 error = namei(&nd); 497 if (error == 0) { 498 error = vacl_delete(td, nd.ni_vp, type); 499 NDFREE(&nd, 0); 500 } 501 return (error); 502 } 503 504 /* 505 * Given a file path, delete an ACL from it. 506 */ 507 int 508 sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 509 { 510 struct file *fp; 511 cap_rights_t rights; 512 int error; 513 514 AUDIT_ARG_FD(uap->filedes); 515 error = getvnode(td, uap->filedes, 516 cap_rights_init(&rights, CAP_ACL_DELETE), &fp); 517 if (error == 0) { 518 error = vacl_delete(td, fp->f_vnode, uap->type); 519 fdrop(fp, td); 520 } 521 return (error); 522 } 523 524 /* 525 * Given a file path, check an ACL for it. 526 */ 527 int 528 sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 529 { 530 531 return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp, 532 FOLLOW)); 533 } 534 535 /* 536 * Given a file path, check an ACL for it; don't follow links. 537 */ 538 int 539 sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 540 { 541 return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp, 542 NOFOLLOW)); 543 } 544 545 static int 546 kern___acl_aclcheck_path(struct thread *td, const char *path, acl_type_t type, 547 struct acl *aclp, int follow) 548 { 549 struct nameidata nd; 550 int error; 551 552 NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td); 553 error = namei(&nd); 554 if (error == 0) { 555 error = vacl_aclcheck(td, nd.ni_vp, type, aclp); 556 NDFREE(&nd, 0); 557 } 558 return (error); 559 } 560 561 /* 562 * Given a file descriptor, check an ACL for it. 563 */ 564 int 565 sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 566 { 567 struct file *fp; 568 cap_rights_t rights; 569 int error; 570 571 AUDIT_ARG_FD(uap->filedes); 572 error = getvnode(td, uap->filedes, 573 cap_rights_init(&rights, CAP_ACL_CHECK), &fp); 574 if (error == 0) { 575 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 576 fdrop(fp, td); 577 } 578 return (error); 579 } 580 581 struct acl * 582 acl_alloc(int flags) 583 { 584 struct acl *aclp; 585 586 aclp = malloc(sizeof(*aclp), M_ACL, flags); 587 if (aclp == NULL) 588 return (NULL); 589 590 aclp->acl_maxcnt = ACL_MAX_ENTRIES; 591 592 return (aclp); 593 } 594 595 void 596 acl_free(struct acl *aclp) 597 { 598 599 free(aclp, M_ACL); 600 } 601