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