vfs_acl.c revision 1.2 1 /* $NetBSD: vfs_acl.c,v 1.2 2024/12/07 02:11:42 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.2 2024/12/07 02:11:42 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/syscallargs.h>
64 #include <sys/systm.h>
65 #include <sys/vnode.h>
66
67 __CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
68
69 int
70 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest)
71 {
72 int i;
73
74 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
75 return EINVAL;
76
77 memset(dest, 0, sizeof(*dest));
78
79 dest->acl_cnt = source->acl_cnt;
80 dest->acl_maxcnt = ACL_MAX_ENTRIES;
81
82 for (i = 0; i < dest->acl_cnt; i++) {
83 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
84 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
85 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
86 }
87
88 return 0;
89 }
90
91 int
92 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
93 {
94 int i;
95
96 if (source->acl_cnt > OLDACL_MAX_ENTRIES)
97 return EINVAL;
98
99 memset(dest, 0, sizeof(*dest));
100
101 dest->acl_cnt = source->acl_cnt;
102
103 for (i = 0; i < dest->acl_cnt; i++) {
104 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
105 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
106 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
107 }
108
109 return 0;
110 }
111
112 /*
113 * At one time, "struct ACL" was extended in order to add support for NFSv4
114 * ACLs. Instead of creating compatibility versions of all the ACL-related
115 * syscalls, they were left intact. It's possible to find out what the code
116 * calling these syscalls (libc) expects basing on "type" argument - if it's
117 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were
118 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct
119 * oldacl". If it's something else, then it's the new "struct acl". In the
120 * latter case, the routines below just copyin/copyout the contents. In the
121 * former case, they copyin the "struct oldacl" and convert it to the new
122 * format.
123 */
124 static int
125 acl_copyin(const void *user_acl, struct acl *kernel_acl, acl_type_t type)
126 {
127 int error;
128 struct oldacl old;
129
130 switch (type) {
131 case ACL_TYPE_ACCESS_OLD:
132 case ACL_TYPE_DEFAULT_OLD:
133 error = copyin(user_acl, &old, sizeof(old));
134 if (error != 0)
135 break;
136 acl_copy_oldacl_into_acl(&old, kernel_acl);
137 break;
138
139 default:
140 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl));
141 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES)
142 return EINVAL;
143 }
144
145 return error;
146 }
147
148 static int
149 acl_copyout(const struct acl *kernel_acl, void *user_acl, acl_type_t type)
150 {
151 uint32_t am;
152 int error;
153 struct oldacl old;
154
155 switch (type) {
156 case ACL_TYPE_ACCESS_OLD:
157 case ACL_TYPE_DEFAULT_OLD:
158 error = acl_copy_acl_into_oldacl(kernel_acl, &old);
159 if (error != 0)
160 break;
161
162 error = copyout(&old, user_acl, sizeof(old));
163 break;
164
165 default:
166 error = ufetch_32((const uint32_t *)
167 (const void *)((const char *)user_acl +
168 offsetof(struct acl, acl_maxcnt)), &am);
169 if (error)
170 return error;
171 if (am != ACL_MAX_ENTRIES)
172 return EINVAL;
173
174 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
175 }
176
177 return error;
178 }
179
180 /*
181 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
182 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work
183 * with new kernel. Fixing 'type' for old binaries with new libc
184 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
185 */
186 static int
187 acl_type_unold(int type)
188 {
189 switch (type) {
190 case ACL_TYPE_ACCESS_OLD:
191 return ACL_TYPE_ACCESS;
192
193 case ACL_TYPE_DEFAULT_OLD:
194 return ACL_TYPE_DEFAULT;
195
196 default:
197 return type;
198 }
199 }
200
201 /*
202 * These calls wrap the real vnode operations, and are called by the syscall
203 * code once the syscall has converted the path or file descriptor to a vnode
204 * (unlocked). The aclp pointer is assumed still to point to userland, so
205 * this should not be consumed within the kernel except by syscall code.
206 * Other code should directly invoke VOP_{SET,GET}ACL.
207 */
208
209 /*
210 * Given a vnode, set its ACL.
211 */
212 int
213 vacl_set_acl(struct lwp *l, struct vnode *vp, acl_type_t type,
214 const struct acl *aclp)
215 {
216 struct acl *inkernelacl;
217 int error;
218
219 inkernelacl = acl_alloc(KM_SLEEP);
220 error = acl_copyin(aclp, inkernelacl, type);
221 if (error != 0)
222 goto out;
223 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
224 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, l->l_cred);
225 VOP_UNLOCK(vp);
226 out:
227 acl_free(inkernelacl);
228 return error;
229 }
230
231 /*
232 * Given a vnode, get its ACL.
233 */
234 int
235 vacl_get_acl(struct lwp *l, struct vnode *vp, acl_type_t type,
236 struct acl *aclp)
237 {
238 struct acl *inkernelacl;
239 int error;
240
241 inkernelacl = acl_alloc(KM_SLEEP);
242 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
243 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, l->l_cred);
244
245 VOP_UNLOCK(vp);
246 if (error == 0)
247 error = acl_copyout(inkernelacl, aclp, type);
248 acl_free(inkernelacl);
249 return error;
250 }
251
252 /*
253 * Given a vnode, delete its ACL.
254 */
255 int
256 vacl_delete(struct lwp *l, struct vnode *vp, acl_type_t type)
257 {
258 int error;
259
260 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
261 error = VOP_SETACL(vp, acl_type_unold(type), 0, l->l_cred);
262 VOP_UNLOCK(vp);
263 return error;
264 }
265
266 /*
267 * Given a vnode, check whether an ACL is appropriate for it
268 *
269 * XXXRW: No vnode lock held so can't audit vnode state...?
270 */
271 int
272 vacl_aclcheck(struct lwp *l, struct vnode *vp, acl_type_t type,
273 const struct acl *aclp)
274 {
275 struct acl *inkernelacl;
276 int error;
277
278 inkernelacl = acl_alloc(KM_SLEEP);
279 error = acl_copyin(aclp, inkernelacl, type);
280 if (error != 0)
281 goto out;
282 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl,
283 l->l_cred);
284 out:
285 acl_free(inkernelacl);
286 return error;
287 }
288
289 /*
290 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't
291 * need to lock, as the vacl_ code will get/release any locks required.
292 */
293
294 /*
295 * Given a file path, get an ACL for it
296 */
297 int
298 sys___acl_get_file(struct lwp *l,
299 const struct sys___acl_get_file_args *uap, register_t *retval)
300 {
301
302 return kern___acl_get_path(l, SCARG(uap, path), SCARG(uap, type),
303 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
304 }
305
306 /*
307 * Given a file path, get an ACL for it; don't follow links.
308 */
309 int
310 sys___acl_get_link(struct lwp *l,
311 const struct sys___acl_get_link_args *uap, register_t *retval)
312 {
313
314 return kern___acl_get_path(l, SCARG(uap, path), SCARG(uap, type),
315 SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
316 }
317
318 int
319 kern___acl_get_path(struct lwp *l, const char *path, acl_type_t type,
320 struct acl *aclp, namei_simple_flags_t flags)
321 {
322 struct vnode *path_vp;
323 int error;
324
325 error = namei_simple_user(path, flags, &path_vp);
326 if (error == 0) {
327 error = vacl_get_acl(l, path_vp, type, aclp);
328 vrele(path_vp);
329 }
330 return error;
331 }
332
333 /*
334 * Given a file path, set an ACL for it.
335 */
336 int
337 sys___acl_set_file(struct lwp *l,
338 const struct sys___acl_set_file_args *uap, register_t *retval)
339 {
340
341 return kern___acl_set_path(l, SCARG(uap, path), SCARG(uap, type),
342 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
343 }
344
345 /*
346 * Given a file path, set an ACL for it; don't follow links.
347 */
348 int
349 sys___acl_set_link(struct lwp *l,
350 const struct sys___acl_set_link_args *uap, register_t *retval)
351 {
352
353 return kern___acl_set_path(l, SCARG(uap, path), SCARG(uap, type),
354 SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
355 }
356
357 int
358 kern___acl_set_path(struct lwp *l, const char *path,
359 acl_type_t type, const struct acl *aclp, namei_simple_flags_t flags)
360 {
361 struct vnode *path_vp;
362 int error;
363
364 error = namei_simple_user(path, flags, &path_vp);
365 if (error == 0) {
366 error = vacl_set_acl(l, path_vp, type, aclp);
367 vrele(path_vp);
368 }
369 return error;
370 }
371
372 /*
373 * Given a file descriptor, get an ACL for it.
374 */
375 int
376 sys___acl_get_fd(struct lwp *l, const struct sys___acl_get_fd_args *uap,
377 register_t *retval)
378 {
379 struct file *fp;
380 int error;
381 error = fd_getvnode(SCARG(uap, filedes), &fp);
382 if (error == 0) {
383 error = vacl_get_acl(l, fp->f_vnode, SCARG(uap, type),
384 SCARG(uap, aclp));
385 fd_putfile(SCARG(uap, filedes));
386 }
387 return error;
388 }
389
390 /*
391 * Given a file descriptor, set an ACL for it.
392 */
393 int
394 sys___acl_set_fd(struct lwp *l, const struct sys___acl_set_fd_args *uap,
395 register_t *retval)
396 {
397 struct file *fp;
398 int error;
399
400 error = fd_getvnode(SCARG(uap, filedes), &fp);
401 if (error == 0) {
402 error = vacl_set_acl(l, fp->f_vnode, SCARG(uap, type),
403 SCARG(uap, aclp));
404 fd_putfile(SCARG(uap, filedes));
405 }
406 return error;
407 }
408
409 /*
410 * Given a file path, delete an ACL from it.
411 */
412 int
413 sys___acl_delete_file(struct lwp *l,
414 const struct sys___acl_delete_file_args *uap, register_t *retval)
415 {
416
417 return kern___acl_delete_path(l, SCARG(uap, path), SCARG(uap, type),
418 NSM_FOLLOW_NOEMULROOT);
419 }
420
421 /*
422 * Given a file path, delete an ACL from it; don't follow links.
423 */
424 int
425 sys___acl_delete_link(struct lwp *l,
426 const struct sys___acl_delete_link_args *uap, register_t *retval)
427 {
428
429 return kern___acl_delete_path(l, SCARG(uap, path), SCARG(uap, type),
430 NSM_NOFOLLOW_NOEMULROOT);
431 }
432
433 int
434 kern___acl_delete_path(struct lwp *l, const char *path,
435 acl_type_t type, namei_simple_flags_t flags)
436 {
437 struct vnode *path_vp;
438 int error;
439
440 error = namei_simple_user(path, flags, &path_vp);
441 if (error == 0) {
442 error = vacl_delete(l, path_vp, type);
443 vrele(path_vp);
444 }
445 return error;
446 }
447
448 /*
449 * Given a file path, delete an ACL from it.
450 */
451 int
452 sys___acl_delete_fd(struct lwp *l,
453 const struct sys___acl_delete_fd_args *uap, register_t *retval)
454 {
455 struct file *fp;
456 int error;
457
458 error = fd_getvnode(SCARG(uap, filedes), &fp);
459 if (error == 0) {
460 error = vacl_delete(l, fp->f_vnode, SCARG(uap, type));
461 fd_putfile(SCARG(uap, filedes));
462 }
463 return error;
464 }
465
466 /*
467 * Given a file path, check an ACL for it.
468 */
469 int
470 sys___acl_aclcheck_file(struct lwp *l,
471 const struct sys___acl_aclcheck_file_args *uap, register_t *retval)
472 {
473
474 return kern___acl_aclcheck_path(l, SCARG(uap, path), SCARG(uap, type),
475 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
476 }
477
478 /*
479 * Given a file path, check an ACL for it; don't follow links.
480 */
481 int
482 sys___acl_aclcheck_link(struct lwp *l,
483 const struct sys___acl_aclcheck_link_args *uap, register_t *retval)
484 {
485 return kern___acl_aclcheck_path(l, SCARG(uap, path),
486 SCARG(uap, type), SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
487 }
488
489 int
490 kern___acl_aclcheck_path(struct lwp *l, const char *path, acl_type_t type,
491 struct acl *aclp, namei_simple_flags_t flags)
492 {
493 struct vnode *path_vp;
494 int error;
495
496 error = namei_simple_user(path, flags, &path_vp);
497 if (error == 0) {
498 error = vacl_aclcheck(l, path_vp, type, aclp);
499 vrele(path_vp);
500
501 }
502 return error;
503 }
504
505 /*
506 * Given a file descriptor, check an ACL for it.
507 */
508 int
509 sys___acl_aclcheck_fd(struct lwp *l,
510 const struct sys___acl_aclcheck_fd_args *uap, register_t *retval)
511 {
512 struct file *fp;
513 int error;
514
515 error = fd_getvnode(SCARG(uap, filedes), &fp);
516 if (error == 0) {
517 error = vacl_aclcheck(l, fp->f_vnode, SCARG(uap, type),
518 SCARG(uap, aclp));
519 fd_putfile(SCARG(uap, filedes));
520 }
521 return error;
522 }
523
524 struct acl *
525 acl_alloc(int flags)
526 {
527 struct acl *aclp;
528
529 aclp = kmem_zalloc(sizeof(*aclp), flags);
530 if (aclp == NULL)
531 return NULL;
532
533 aclp->acl_maxcnt = ACL_MAX_ENTRIES;
534
535 return aclp;
536 }
537
538 void
539 acl_free(struct acl *aclp)
540 {
541
542 kmem_free(aclp, sizeof(*aclp));
543 }
544