xref: /freebsd/sys/kern/vfs_acl.c (revision 1f474190)
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 __FBSDID("$FreeBSD$");
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/sysproto.h>
48 #include <sys/capsicum.h>
49 #include <sys/fcntl.h>
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <sys/lock.h>
55 #include <sys/mutex.h>
56 #include <sys/namei.h>
57 #include <sys/file.h>
58 #include <sys/filedesc.h>
59 #include <sys/proc.h>
60 #include <sys/sysent.h>
61 #include <sys/acl.h>
62 
63 #include <security/audit/audit.h>
64 #include <security/mac/mac_framework.h>
65 
66 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
67 
68 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
69 
70 static int	kern___acl_aclcheck_path(struct thread *td, const char *path,
71 		    acl_type_t type, struct acl *aclp, int follow);
72 static int	kern___acl_delete_path(struct thread *td, const char *path,
73 		    acl_type_t type, int follow);
74 static int	kern___acl_get_path(struct thread *td, const char *path,
75 		    acl_type_t type, struct acl *aclp, int follow);
76 static int	kern___acl_set_path(struct thread *td, const char *path,
77 		    acl_type_t type, const struct acl *aclp, int follow);
78 static int	vacl_set_acl(struct thread *td, struct vnode *vp,
79 		    acl_type_t type, const struct acl *aclp);
80 static int	vacl_get_acl(struct thread *td, struct vnode *vp,
81 		    acl_type_t type, struct acl *aclp);
82 static int	vacl_aclcheck(struct thread *td, struct vnode *vp,
83 		    acl_type_t type, const struct acl *aclp);
84 
85 int
86 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest)
87 {
88 	int i;
89 
90 	if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
91 		return (EINVAL);
92 
93 	bzero(dest, sizeof(*dest));
94 
95 	dest->acl_cnt = source->acl_cnt;
96 	dest->acl_maxcnt = ACL_MAX_ENTRIES;
97 
98 	for (i = 0; i < dest->acl_cnt; i++) {
99 		dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
100 		dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
101 		dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
102 	}
103 
104 	return (0);
105 }
106 
107 int
108 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
109 {
110 	int i;
111 
112 	if (source->acl_cnt > OLDACL_MAX_ENTRIES)
113 		return (EINVAL);
114 
115 	bzero(dest, sizeof(*dest));
116 
117 	dest->acl_cnt = source->acl_cnt;
118 
119 	for (i = 0; i < dest->acl_cnt; i++) {
120 		dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
121 		dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
122 		dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
123 	}
124 
125 	return (0);
126 }
127 
128 /*
129  * At one time, "struct ACL" was extended in order to add support for NFSv4
130  * ACLs.  Instead of creating compatibility versions of all the ACL-related
131  * syscalls, they were left intact.  It's possible to find out what the code
132  * calling these syscalls (libc) expects basing on "type" argument - if it's
133  * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were
134  * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct
135  * oldacl".  If it's something else, then it's the new "struct acl".  In the
136  * latter case, the routines below just copyin/copyout the contents.  In the
137  * former case, they copyin the "struct oldacl" and convert it to the new
138  * format.
139  */
140 static int
141 acl_copyin(const void *user_acl, struct acl *kernel_acl, acl_type_t type)
142 {
143 	int error;
144 	struct oldacl old;
145 
146 	switch (type) {
147 	case ACL_TYPE_ACCESS_OLD:
148 	case ACL_TYPE_DEFAULT_OLD:
149 		error = copyin(user_acl, &old, sizeof(old));
150 		if (error != 0)
151 			break;
152 		acl_copy_oldacl_into_acl(&old, kernel_acl);
153 		break;
154 
155 	default:
156 		error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl));
157 		if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES)
158 			return (EINVAL);
159 	}
160 
161 	return (error);
162 }
163 
164 static int
165 acl_copyout(const struct acl *kernel_acl, void *user_acl, acl_type_t type)
166 {
167 	uint32_t am;
168 	int error;
169 	struct oldacl old;
170 
171 	switch (type) {
172 	case ACL_TYPE_ACCESS_OLD:
173 	case ACL_TYPE_DEFAULT_OLD:
174 		error = acl_copy_acl_into_oldacl(kernel_acl, &old);
175 		if (error != 0)
176 			break;
177 
178 		error = copyout(&old, user_acl, sizeof(old));
179 		break;
180 
181 	default:
182 		error = fueword32((char *)user_acl +
183 		    offsetof(struct acl, acl_maxcnt), &am);
184 		if (error == -1)
185 			return (EFAULT);
186 		if (am != ACL_MAX_ENTRIES)
187 			return (EINVAL);
188 
189 		error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
190 	}
191 
192 	return (error);
193 }
194 
195 /*
196  * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
197  * counterpart.  It's required for old (pre-NFSv4 ACLs) libc to work
198  * with new kernel.  Fixing 'type' for old binaries with new libc
199  * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
200  */
201 static int
202 acl_type_unold(int type)
203 {
204 	switch (type) {
205 	case ACL_TYPE_ACCESS_OLD:
206 		return (ACL_TYPE_ACCESS);
207 
208 	case ACL_TYPE_DEFAULT_OLD:
209 		return (ACL_TYPE_DEFAULT);
210 
211 	default:
212 		return (type);
213 	}
214 }
215 
216 /*
217  * These calls wrap the real vnode operations, and are called by the syscall
218  * code once the syscall has converted the path or file descriptor to a vnode
219  * (unlocked).  The aclp pointer is assumed still to point to userland, so
220  * this should not be consumed within the kernel except by syscall code.
221  * Other code should directly invoke VOP_{SET,GET}ACL.
222  */
223 
224 /*
225  * Given a vnode, set its ACL.
226  */
227 static int
228 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
229     const struct acl *aclp)
230 {
231 	struct acl *inkernelacl;
232 	struct mount *mp;
233 	int error;
234 
235 	AUDIT_ARG_VALUE(type);
236 	inkernelacl = acl_alloc(M_WAITOK);
237 	error = acl_copyin(aclp, inkernelacl, type);
238 	if (error != 0)
239 		goto out;
240 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
241 	if (error != 0)
242 		goto out;
243 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
244 	AUDIT_ARG_VNODE1(vp);
245 #ifdef MAC
246 	error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
247 	if (error != 0)
248 		goto out_unlock;
249 #endif
250 	error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
251 	    td->td_ucred, td);
252 #ifdef MAC
253 out_unlock:
254 #endif
255 	VOP_UNLOCK(vp);
256 	vn_finished_write(mp);
257 out:
258 	acl_free(inkernelacl);
259 	return (error);
260 }
261 
262 /*
263  * Given a vnode, get its ACL.
264  */
265 static int
266 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
267     struct acl *aclp)
268 {
269 	struct acl *inkernelacl;
270 	int error;
271 
272 	AUDIT_ARG_VALUE(type);
273 	inkernelacl = acl_alloc(M_WAITOK | M_ZERO);
274 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
275 	AUDIT_ARG_VNODE1(vp);
276 #ifdef MAC
277 	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
278 	if (error != 0)
279 		goto out;
280 #endif
281 	error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
282 	    td->td_ucred, td);
283 
284 #ifdef MAC
285 out:
286 #endif
287 	VOP_UNLOCK(vp);
288 	if (error == 0)
289 		error = acl_copyout(inkernelacl, aclp, type);
290 	acl_free(inkernelacl);
291 	return (error);
292 }
293 
294 /*
295  * Given a vnode, delete its ACL.
296  */
297 static int
298 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
299 {
300 	struct mount *mp;
301 	int error;
302 
303 	AUDIT_ARG_VALUE(type);
304 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
305 	if (error != 0)
306 		return (error);
307 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
308 	AUDIT_ARG_VNODE1(vp);
309 #ifdef MAC
310 	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
311 	if (error != 0)
312 		goto out;
313 #endif
314 	error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
315 #ifdef MAC
316 out:
317 #endif
318 	VOP_UNLOCK(vp);
319 	vn_finished_write(mp);
320 	return (error);
321 }
322 
323 /*
324  * Given a vnode, check whether an ACL is appropriate for it
325  *
326  * XXXRW: No vnode lock held so can't audit vnode state...?
327  */
328 static int
329 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
330     const struct acl *aclp)
331 {
332 	struct acl *inkernelacl;
333 	int error;
334 
335 	inkernelacl = acl_alloc(M_WAITOK);
336 	error = acl_copyin(aclp, inkernelacl, type);
337 	if (error != 0)
338 		goto out;
339 	error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl,
340 	    td->td_ucred, td);
341 out:
342 	acl_free(inkernelacl);
343 	return (error);
344 }
345 
346 /*
347  * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
348  * need to lock, as the vacl_ code will get/release any locks required.
349  */
350 
351 /*
352  * Given a file path, get an ACL for it
353  */
354 int
355 sys___acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
356 {
357 
358 	return (kern___acl_get_path(td, uap->path, uap->type, uap->aclp,
359 	    FOLLOW));
360 }
361 
362 /*
363  * Given a file path, get an ACL for it; don't follow links.
364  */
365 int
366 sys___acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
367 {
368 
369 	return(kern___acl_get_path(td, uap->path, uap->type, uap->aclp,
370 	    NOFOLLOW));
371 }
372 
373 static int
374 kern___acl_get_path(struct thread *td, const char *path, acl_type_t type,
375     struct acl *aclp, int follow)
376 {
377 	struct nameidata nd;
378 	int error;
379 
380 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td);
381 	error = namei(&nd);
382 	if (error == 0) {
383 		error = vacl_get_acl(td, nd.ni_vp, type, aclp);
384 		NDFREE(&nd, 0);
385 	}
386 	return (error);
387 }
388 
389 /*
390  * Given a file path, set an ACL for it.
391  */
392 int
393 sys___acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
394 {
395 
396 	return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp,
397 	    FOLLOW));
398 }
399 
400 /*
401  * Given a file path, set an ACL for it; don't follow links.
402  */
403 int
404 sys___acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
405 {
406 
407 	return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp,
408 	    NOFOLLOW));
409 }
410 
411 static int
412 kern___acl_set_path(struct thread *td, const char *path,
413     acl_type_t type, const struct acl *aclp, int follow)
414 {
415 	struct nameidata nd;
416 	int error;
417 
418 	NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td);
419 	error = namei(&nd);
420 	if (error == 0) {
421 		error = vacl_set_acl(td, nd.ni_vp, type, aclp);
422 		NDFREE(&nd, 0);
423 	}
424 	return (error);
425 }
426 
427 /*
428  * Given a file descriptor, get an ACL for it.
429  */
430 int
431 sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
432 {
433 	struct file *fp;
434 	cap_rights_t rights;
435 	int error;
436 
437 	AUDIT_ARG_FD(uap->filedes);
438 	error = getvnode(td, uap->filedes,
439 	    cap_rights_init_one(&rights, CAP_ACL_GET), &fp);
440 	if (error == 0) {
441 		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
442 		fdrop(fp, td);
443 	}
444 	return (error);
445 }
446 
447 /*
448  * Given a file descriptor, set an ACL for it.
449  */
450 int
451 sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
452 {
453 	struct file *fp;
454 	cap_rights_t rights;
455 	int error;
456 
457 	AUDIT_ARG_FD(uap->filedes);
458 	error = getvnode(td, uap->filedes,
459 	    cap_rights_init_one(&rights, CAP_ACL_SET), &fp);
460 	if (error == 0) {
461 		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
462 		fdrop(fp, td);
463 	}
464 	return (error);
465 }
466 
467 /*
468  * Given a file path, delete an ACL from it.
469  */
470 int
471 sys___acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
472 {
473 
474 	return (kern___acl_delete_path(td, uap->path, uap->type, FOLLOW));
475 }
476 
477 /*
478  * Given a file path, delete an ACL from it; don't follow links.
479  */
480 int
481 sys___acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
482 {
483 
484 	return (kern___acl_delete_path(td, uap->path, uap->type, NOFOLLOW));
485 }
486 
487 static int
488 kern___acl_delete_path(struct thread *td, const char *path,
489     acl_type_t type, int follow)
490 {
491 	struct nameidata nd;
492 	int error;
493 
494 	NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td);
495 	error = namei(&nd);
496 	if (error == 0) {
497 		error = vacl_delete(td, nd.ni_vp, type);
498 		NDFREE(&nd, 0);
499 	}
500 	return (error);
501 }
502 
503 /*
504  * Given a file path, delete an ACL from it.
505  */
506 int
507 sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
508 {
509 	struct file *fp;
510 	cap_rights_t rights;
511 	int error;
512 
513 	AUDIT_ARG_FD(uap->filedes);
514 	error = getvnode(td, uap->filedes,
515 	    cap_rights_init_one(&rights, CAP_ACL_DELETE), &fp);
516 	if (error == 0) {
517 		error = vacl_delete(td, fp->f_vnode, uap->type);
518 		fdrop(fp, td);
519 	}
520 	return (error);
521 }
522 
523 /*
524  * Given a file path, check an ACL for it.
525  */
526 int
527 sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
528 {
529 
530 	return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp,
531 	    FOLLOW));
532 }
533 
534 /*
535  * Given a file path, check an ACL for it; don't follow links.
536  */
537 int
538 sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
539 {
540 	return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp,
541 	    NOFOLLOW));
542 }
543 
544 static int
545 kern___acl_aclcheck_path(struct thread *td, const char *path, acl_type_t type,
546     struct acl *aclp, int follow)
547 {
548 	struct nameidata nd;
549 	int error;
550 
551 	NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td);
552 	error = namei(&nd);
553 	if (error == 0) {
554 		error = vacl_aclcheck(td, nd.ni_vp, type, aclp);
555 		NDFREE(&nd, 0);
556 	}
557 	return (error);
558 }
559 
560 /*
561  * Given a file descriptor, check an ACL for it.
562  */
563 int
564 sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
565 {
566 	struct file *fp;
567 	cap_rights_t rights;
568 	int error;
569 
570 	AUDIT_ARG_FD(uap->filedes);
571 	error = getvnode(td, uap->filedes,
572 	    cap_rights_init_one(&rights, CAP_ACL_CHECK), &fp);
573 	if (error == 0) {
574 		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
575 		fdrop(fp, td);
576 	}
577 	return (error);
578 }
579 
580 struct acl *
581 acl_alloc(int flags)
582 {
583 	struct acl *aclp;
584 
585 	aclp = malloc(sizeof(*aclp), M_ACL, flags);
586 	if (aclp == NULL)
587 		return (NULL);
588 
589 	aclp->acl_maxcnt = ACL_MAX_ENTRIES;
590 
591 	return (aclp);
592 }
593 
594 void
595 acl_free(struct acl *aclp)
596 {
597 
598 	free(aclp, M_ACL);
599 }
600