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