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
acl_copy_oldacl_into_acl(const struct oldacl * source,struct acl * dest)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
acl_copy_acl_into_oldacl(const struct acl * source,struct oldacl * dest)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
acl_copyin(const void * user_acl,struct acl * kernel_acl,acl_type_t type)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
acl_copyout(const struct acl * kernel_acl,void * user_acl,acl_type_t type)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
acl_type_unold(int type)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
vacl_set_acl(struct lwp * l,struct vnode * vp,acl_type_t type,const struct acl * aclp)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
vacl_get_acl(struct lwp * l,struct vnode * vp,acl_type_t type,struct acl * aclp)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
vacl_delete(struct lwp * l,struct vnode * vp,acl_type_t type)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
vacl_aclcheck(struct lwp * l,struct vnode * vp,acl_type_t type,const struct acl * aclp)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
sys___acl_get_file(struct lwp * l,const struct sys___acl_get_file_args * uap,register_t * retval)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
sys___acl_get_link(struct lwp * l,const struct sys___acl_get_link_args * uap,register_t * retval)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
kern___acl_get_path(struct lwp * l,const char * path,acl_type_t type,struct acl * aclp,namei_simple_flags_t flags)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
sys___acl_set_file(struct lwp * l,const struct sys___acl_set_file_args * uap,register_t * retval)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
sys___acl_set_link(struct lwp * l,const struct sys___acl_set_link_args * uap,register_t * retval)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
kern___acl_set_path(struct lwp * l,const char * path,acl_type_t type,const struct acl * aclp,namei_simple_flags_t flags)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
sys___acl_get_fd(struct lwp * l,const struct sys___acl_get_fd_args * uap,register_t * retval)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
sys___acl_set_fd(struct lwp * l,const struct sys___acl_set_fd_args * uap,register_t * retval)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
sys___acl_delete_file(struct lwp * l,const struct sys___acl_delete_file_args * uap,register_t * retval)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
sys___acl_delete_link(struct lwp * l,const struct sys___acl_delete_link_args * uap,register_t * retval)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
kern___acl_delete_path(struct lwp * l,const char * path,acl_type_t type,namei_simple_flags_t flags)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
sys___acl_delete_fd(struct lwp * l,const struct sys___acl_delete_fd_args * uap,register_t * retval)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
sys___acl_aclcheck_file(struct lwp * l,const struct sys___acl_aclcheck_file_args * uap,register_t * retval)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
sys___acl_aclcheck_link(struct lwp * l,const struct sys___acl_aclcheck_link_args * uap,register_t * retval)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
kern___acl_aclcheck_path(struct lwp * l,const char * path,acl_type_t type,struct acl * aclp,namei_simple_flags_t flags)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
sys___acl_aclcheck_fd(struct lwp * l,const struct sys___acl_aclcheck_fd_args * uap,register_t * retval)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 *
acl_alloc(int flags)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
acl_free(struct acl * aclp)540 acl_free(struct acl *aclp)
541 {
542 
543           kmem_free(aclp, sizeof(*aclp));
544 }
545