vfs_acl.c revision 1.3 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