xref: /netbsd-src/sys/kern/vfs_acl.c (revision e94a5d02693120d4ad9d909e488894e9fcf0eb76)
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