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