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