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