1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999-2006, 2016-2017 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed by Robert Watson for the TrustedBSD Project.
8 *
9 * Portions of this software were developed by BAE Systems, the University of
10 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
11 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
12 * Computing (TC) research program.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35 /*
36 * Developed by the TrustedBSD Project.
37 *
38 * ACL system calls and other functions common across different ACL types.
39 * Type-specific routines go into subr_acl_<type>.c.
40 */
41
42 #include <sys/cdefs.h>
43 #if 0
44 __FBSDID("$FreeBSD: head/sys/kern/vfs_acl.c 356337 2020-01-03 22:29:58Z mjg $");
45 #endif
46 __KERNEL_RCSID(0, "$NetBSD: vfs_acl.c,v 1.1 2020/05/16 18:31:50 christos Exp $");
47
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/fcntl.h>
52 #include <sys/kernel.h>
53 #include <sys/mount.h>
54 #include <sys/vnode.h>
55 #include <sys/lock.h>
56 #include <sys/mutex.h>
57 #include <sys/namei.h>
58 #include <sys/file.h>
59 #include <sys/filedesc.h>
60 #include <sys/proc.h>
61 #include <sys/acl.h>
62
63 #include <sys/syscallargs.h>
64
65 __CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
66
67
68 int
acl_copy_oldacl_into_acl(const struct oldacl * source,struct acl * dest)69 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest)
70 {
71 int i;
72
73 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
74 return EINVAL;
75
76 memset(dest, 0, sizeof(*dest));
77
78 dest->acl_cnt = source->acl_cnt;
79 dest->acl_maxcnt = ACL_MAX_ENTRIES;
80
81 for (i = 0; i < dest->acl_cnt; i++) {
82 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
83 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
84 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
85 }
86
87 return 0;
88 }
89
90 int
acl_copy_acl_into_oldacl(const struct acl * source,struct oldacl * dest)91 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
92 {
93 int i;
94
95 if (source->acl_cnt > OLDACL_MAX_ENTRIES)
96 return EINVAL;
97
98 memset(dest, 0, sizeof(*dest));
99
100 dest->acl_cnt = source->acl_cnt;
101
102 for (i = 0; i < dest->acl_cnt; i++) {
103 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
104 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
105 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
106 }
107
108 return 0;
109 }
110
111 /*
112 * At one time, "struct ACL" was extended in order to add support for NFSv4
113 * ACLs. Instead of creating compatibility versions of all the ACL-related
114 * syscalls, they were left intact. It's possible to find out what the code
115 * calling these syscalls (libc) expects basing on "type" argument - if it's
116 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were
117 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct
118 * oldacl". If it's something else, then it's the new "struct acl". In the
119 * latter case, the routines below just copyin/copyout the contents. In the
120 * former case, they copyin the "struct oldacl" and convert it to the new
121 * format.
122 */
123 static int
acl_copyin(const void * user_acl,struct acl * kernel_acl,acl_type_t type)124 acl_copyin(const void *user_acl, struct acl *kernel_acl, acl_type_t type)
125 {
126 int error;
127 struct oldacl old;
128
129 switch (type) {
130 case ACL_TYPE_ACCESS_OLD:
131 case ACL_TYPE_DEFAULT_OLD:
132 error = copyin(user_acl, &old, sizeof(old));
133 if (error != 0)
134 break;
135 acl_copy_oldacl_into_acl(&old, kernel_acl);
136 break;
137
138 default:
139 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl));
140 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES)
141 return EINVAL;
142 }
143
144 return error;
145 }
146
147 static int
acl_copyout(const struct acl * kernel_acl,void * user_acl,acl_type_t type)148 acl_copyout(const struct acl *kernel_acl, void *user_acl, acl_type_t type)
149 {
150 uint32_t am;
151 int error;
152 struct oldacl old;
153
154 switch (type) {
155 case ACL_TYPE_ACCESS_OLD:
156 case ACL_TYPE_DEFAULT_OLD:
157 error = acl_copy_acl_into_oldacl(kernel_acl, &old);
158 if (error != 0)
159 break;
160
161 error = copyout(&old, user_acl, sizeof(old));
162 break;
163
164 default:
165 error = ufetch_32((const uint32_t *)
166 (const void *)((const char *)user_acl +
167 offsetof(struct acl, acl_maxcnt)), &am);
168 if (error)
169 return error;
170 if (am != ACL_MAX_ENTRIES)
171 return EINVAL;
172
173 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
174 }
175
176 return error;
177 }
178
179 /*
180 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
181 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work
182 * with new kernel. Fixing 'type' for old binaries with new libc
183 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
184 */
185 static int
acl_type_unold(int type)186 acl_type_unold(int type)
187 {
188 switch (type) {
189 case ACL_TYPE_ACCESS_OLD:
190 return ACL_TYPE_ACCESS;
191
192 case ACL_TYPE_DEFAULT_OLD:
193 return ACL_TYPE_DEFAULT;
194
195 default:
196 return type;
197 }
198 }
199
200 /*
201 * These calls wrap the real vnode operations, and are called by the syscall
202 * code once the syscall has converted the path or file descriptor to a vnode
203 * (unlocked). The aclp pointer is assumed still to point to userland, so
204 * this should not be consumed within the kernel except by syscall code.
205 * Other code should directly invoke VOP_{SET,GET}ACL.
206 */
207
208 /*
209 * Given a vnode, set its ACL.
210 */
211 int
vacl_set_acl(struct lwp * l,struct vnode * vp,acl_type_t type,const struct acl * aclp)212 vacl_set_acl(struct lwp *l, struct vnode *vp, acl_type_t type,
213 const struct acl *aclp)
214 {
215 struct acl *inkernelacl;
216 int error;
217
218 inkernelacl = acl_alloc(KM_SLEEP);
219 error = acl_copyin(aclp, inkernelacl, type);
220 if (error != 0)
221 goto out;
222 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
223 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, l->l_cred);
224 VOP_UNLOCK(vp);
225 out:
226 acl_free(inkernelacl);
227 return error;
228 }
229
230 /*
231 * Given a vnode, get its ACL.
232 */
233 int
vacl_get_acl(struct lwp * l,struct vnode * vp,acl_type_t type,struct acl * aclp)234 vacl_get_acl(struct lwp *l, struct vnode *vp, acl_type_t type,
235 struct acl *aclp)
236 {
237 struct acl *inkernelacl;
238 int error;
239
240 inkernelacl = acl_alloc(KM_SLEEP);
241 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
242 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, l->l_cred);
243
244 VOP_UNLOCK(vp);
245 if (error == 0)
246 error = acl_copyout(inkernelacl, aclp, type);
247 acl_free(inkernelacl);
248 return error;
249 }
250
251 /*
252 * Given a vnode, delete its ACL.
253 */
254 int
vacl_delete(struct lwp * l,struct vnode * vp,acl_type_t type)255 vacl_delete(struct lwp *l, struct vnode *vp, acl_type_t type)
256 {
257 int error;
258
259 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
260 error = VOP_SETACL(vp, acl_type_unold(type), 0, l->l_cred);
261 VOP_UNLOCK(vp);
262 return error;
263 }
264
265 /*
266 * Given a vnode, check whether an ACL is appropriate for it
267 *
268 * XXXRW: No vnode lock held so can't audit vnode state...?
269 */
270 int
vacl_aclcheck(struct lwp * l,struct vnode * vp,acl_type_t type,const struct acl * aclp)271 vacl_aclcheck(struct lwp *l, struct vnode *vp, acl_type_t type,
272 const struct acl *aclp)
273 {
274 struct acl *inkernelacl;
275 int error;
276
277 inkernelacl = acl_alloc(KM_SLEEP);
278 error = acl_copyin(aclp, inkernelacl, type);
279 if (error != 0)
280 goto out;
281 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl,
282 l->l_cred);
283 out:
284 acl_free(inkernelacl);
285 return error;
286 }
287
288 /*
289 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't
290 * need to lock, as the vacl_ code will get/release any locks required.
291 */
292
293 /*
294 * Given a file path, get an ACL for it
295 */
296 int
sys___acl_get_file(struct lwp * l,const struct sys___acl_get_file_args * uap,register_t * retval)297 sys___acl_get_file(struct lwp *l,
298 const struct sys___acl_get_file_args *uap, register_t *retval)
299 {
300
301 return kern___acl_get_path(l, SCARG(uap, path), SCARG(uap, type),
302 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
303 }
304
305 /*
306 * Given a file path, get an ACL for it; don't follow links.
307 */
308 int
sys___acl_get_link(struct lwp * l,const struct sys___acl_get_link_args * uap,register_t * retval)309 sys___acl_get_link(struct lwp *l,
310 const struct sys___acl_get_link_args *uap, register_t *retval)
311 {
312
313 return kern___acl_get_path(l, SCARG(uap, path), SCARG(uap, type),
314 SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
315 }
316
317 int
kern___acl_get_path(struct lwp * l,const char * path,acl_type_t type,struct acl * aclp,namei_simple_flags_t flags)318 kern___acl_get_path(struct lwp *l, const char *path, acl_type_t type,
319 struct acl *aclp, namei_simple_flags_t flags)
320 {
321 struct vnode *path_vp;
322 int error;
323
324 error = namei_simple_user(path, flags, &path_vp);
325 if (error == 0) {
326 error = vacl_get_acl(l, path_vp, type, aclp);
327 vrele(path_vp);
328 }
329 return error;
330 }
331
332 /*
333 * Given a file path, set an ACL for it.
334 */
335 int
sys___acl_set_file(struct lwp * l,const struct sys___acl_set_file_args * uap,register_t * retval)336 sys___acl_set_file(struct lwp *l,
337 const struct sys___acl_set_file_args *uap, register_t *retval)
338 {
339
340 return kern___acl_set_path(l, SCARG(uap, path), SCARG(uap, type),
341 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
342 }
343
344 /*
345 * Given a file path, set an ACL for it; don't follow links.
346 */
347 int
sys___acl_set_link(struct lwp * l,const struct sys___acl_set_link_args * uap,register_t * retval)348 sys___acl_set_link(struct lwp *l,
349 const struct sys___acl_set_link_args *uap, register_t *retval)
350 {
351
352 return kern___acl_set_path(l, SCARG(uap, path), SCARG(uap, type),
353 SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
354 }
355
356 int
kern___acl_set_path(struct lwp * l,const char * path,acl_type_t type,const struct acl * aclp,namei_simple_flags_t flags)357 kern___acl_set_path(struct lwp *l, const char *path,
358 acl_type_t type, const struct acl *aclp, namei_simple_flags_t flags)
359 {
360 struct vnode *path_vp;
361 int error;
362
363 error = namei_simple_user(path, flags, &path_vp);
364 if (error == 0) {
365 error = vacl_set_acl(l, path_vp, type, aclp);
366 vrele(path_vp);
367 }
368 return error;
369 }
370
371 /*
372 * Given a file descriptor, get an ACL for it.
373 */
374 int
sys___acl_get_fd(struct lwp * l,const struct sys___acl_get_fd_args * uap,register_t * retval)375 sys___acl_get_fd(struct lwp *l, const struct sys___acl_get_fd_args *uap,
376 register_t *retval)
377 {
378 struct file *fp;
379 int error;
380 error = fd_getvnode(SCARG(uap, filedes), &fp);
381 if (error == 0) {
382 error = vacl_get_acl(l, fp->f_vnode, SCARG(uap, type),
383 SCARG(uap, aclp));
384 fd_putfile(SCARG(uap, filedes));
385 }
386 return error;
387 }
388
389 /*
390 * Given a file descriptor, set an ACL for it.
391 */
392 int
sys___acl_set_fd(struct lwp * l,const struct sys___acl_set_fd_args * uap,register_t * retval)393 sys___acl_set_fd(struct lwp *l, const struct sys___acl_set_fd_args *uap,
394 register_t *retval)
395 {
396 struct file *fp;
397 int error;
398
399 error = fd_getvnode(SCARG(uap, filedes), &fp);
400 if (error == 0) {
401 error = vacl_set_acl(l, fp->f_vnode, SCARG(uap, type),
402 SCARG(uap, aclp));
403 fd_putfile(SCARG(uap, filedes));
404 }
405 return error;
406 }
407
408 /*
409 * Given a file path, delete an ACL from it.
410 */
411 int
sys___acl_delete_file(struct lwp * l,const struct sys___acl_delete_file_args * uap,register_t * retval)412 sys___acl_delete_file(struct lwp *l,
413 const struct sys___acl_delete_file_args *uap, register_t *retval)
414 {
415
416 return kern___acl_delete_path(l, SCARG(uap, path), SCARG(uap, type),
417 NSM_FOLLOW_NOEMULROOT);
418 }
419
420 /*
421 * Given a file path, delete an ACL from it; don't follow links.
422 */
423 int
sys___acl_delete_link(struct lwp * l,const struct sys___acl_delete_link_args * uap,register_t * retval)424 sys___acl_delete_link(struct lwp *l,
425 const struct sys___acl_delete_link_args *uap, register_t *retval)
426 {
427
428 return kern___acl_delete_path(l, SCARG(uap, path), SCARG(uap, type),
429 NSM_NOFOLLOW_NOEMULROOT);
430 }
431
432 int
kern___acl_delete_path(struct lwp * l,const char * path,acl_type_t type,namei_simple_flags_t flags)433 kern___acl_delete_path(struct lwp *l, const char *path,
434 acl_type_t type, namei_simple_flags_t flags)
435 {
436 struct vnode *path_vp;
437 int error;
438
439 error = namei_simple_user(path, flags, &path_vp);
440 if (error == 0) {
441 error = vacl_delete(l, path_vp, type);
442 vrele(path_vp);
443 }
444 return error;
445 }
446
447 /*
448 * Given a file path, delete an ACL from it.
449 */
450 int
sys___acl_delete_fd(struct lwp * l,const struct sys___acl_delete_fd_args * uap,register_t * retval)451 sys___acl_delete_fd(struct lwp *l,
452 const struct sys___acl_delete_fd_args *uap, register_t *retval)
453 {
454 struct file *fp;
455 int error;
456
457 error = fd_getvnode(SCARG(uap, filedes), &fp);
458 if (error == 0) {
459 error = vacl_delete(l, fp->f_vnode, SCARG(uap, type));
460 fd_putfile(SCARG(uap, filedes));
461 }
462 return error;
463 }
464
465 /*
466 * Given a file path, check an ACL for it.
467 */
468 int
sys___acl_aclcheck_file(struct lwp * l,const struct sys___acl_aclcheck_file_args * uap,register_t * retval)469 sys___acl_aclcheck_file(struct lwp *l,
470 const struct sys___acl_aclcheck_file_args *uap, register_t *retval)
471 {
472
473 return kern___acl_aclcheck_path(l, SCARG(uap, path), SCARG(uap, type),
474 SCARG(uap, aclp), NSM_FOLLOW_NOEMULROOT);
475 }
476
477 /*
478 * Given a file path, check an ACL for it; don't follow links.
479 */
480 int
sys___acl_aclcheck_link(struct lwp * l,const struct sys___acl_aclcheck_link_args * uap,register_t * retval)481 sys___acl_aclcheck_link(struct lwp *l,
482 const struct sys___acl_aclcheck_link_args *uap, register_t *retval)
483 {
484 return kern___acl_aclcheck_path(l, SCARG(uap, path),
485 SCARG(uap, type), SCARG(uap, aclp), NSM_NOFOLLOW_NOEMULROOT);
486 }
487
488 int
kern___acl_aclcheck_path(struct lwp * l,const char * path,acl_type_t type,struct acl * aclp,namei_simple_flags_t flags)489 kern___acl_aclcheck_path(struct lwp *l, const char *path, acl_type_t type,
490 struct acl *aclp, namei_simple_flags_t flags)
491 {
492 struct vnode *path_vp;
493 int error;
494
495 error = namei_simple_user(path, flags, &path_vp);
496 if (error == 0) {
497 error = vacl_aclcheck(l, path_vp, type, aclp);
498 vrele(path_vp);
499
500 }
501 return error;
502 }
503
504 /*
505 * Given a file descriptor, check an ACL for it.
506 */
507 int
sys___acl_aclcheck_fd(struct lwp * l,const struct sys___acl_aclcheck_fd_args * uap,register_t * retval)508 sys___acl_aclcheck_fd(struct lwp *l,
509 const struct sys___acl_aclcheck_fd_args *uap, register_t *retval)
510 {
511 struct file *fp;
512 int error;
513
514 error = fd_getvnode(SCARG(uap, filedes), &fp);
515 if (error == 0) {
516 error = vacl_aclcheck(l, fp->f_vnode, SCARG(uap, type),
517 SCARG(uap, aclp));
518 fd_putfile(SCARG(uap, filedes));
519 }
520 return error;
521 }
522
523 struct acl *
acl_alloc(int flags)524 acl_alloc(int flags)
525 {
526 struct acl *aclp;
527
528 aclp = kmem_zalloc(sizeof(*aclp), flags);
529 if (aclp == NULL)
530 return NULL;
531
532 aclp->acl_maxcnt = ACL_MAX_ENTRIES;
533
534 return aclp;
535 }
536
537 void
acl_free(struct acl * aclp)538 acl_free(struct acl *aclp)
539 {
540
541 kmem_free(aclp, sizeof(*aclp));
542 }
543