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 66CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES); 67 68MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 69 70 71static int kern___acl_aclcheck_path(struct thread *td, const char *path, 72 acl_type_t type, struct acl *aclp, int follow); 73static int kern___acl_delete_path(struct thread *td, const char *path, 74 acl_type_t type, int follow); 75static int kern___acl_get_path(struct thread *td, const char *path, 76 acl_type_t type, struct acl *aclp, int follow); 77static int kern___acl_set_path(struct thread *td, const char *path, 78 acl_type_t type, const struct acl *aclp, int follow); 79static int vacl_set_acl(struct thread *td, struct vnode *vp, 80 acl_type_t type, const struct acl *aclp); 81static int vacl_get_acl(struct thread *td, struct vnode *vp, 82 acl_type_t type, struct acl *aclp); 83static int vacl_aclcheck(struct thread *td, struct vnode *vp, 84 acl_type_t type, const struct acl *aclp); 85 86int 87acl_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 108int 109acl_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 */ 141static int 142acl_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 165static int 166acl_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 */ 202static int 203acl_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 */ 228static int 229vacl_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 254out_unlock: 255#endif 256 VOP_UNLOCK(vp, 0); 257 vn_finished_write(mp); 258out: 259 acl_free(inkernelacl); 260 return (error); 261} 262 263/* 264 * Given a vnode, get its ACL. 265 */ 266static int 267vacl_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 286out: 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 */ 298static int 299vacl_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 317out: 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 */ 329static int 330vacl_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); 342out: 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 */ 355int 356sys___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 */ 366int 367sys___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 374static int 375kern___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 */ 393int 394sys___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 */ 404int 405sys___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 412static int 413kern___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 */ 431int 432sys___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 */ 451int 452sys___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 */ 471int 472sys___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 */ 481int 482sys___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 488static int 489kern___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 */ 507int 508sys___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 */ 527int 528sys___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 */ 538int 539sys___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 545static int 546kern___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 */ 564int 565sys___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 581struct acl * 582acl_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 595void 596acl_free(struct acl *aclp) 597{ 598 599 free(aclp, M_ACL); 600} 601