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