Home | History | Annotate | Line # | Download | only in kern
      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