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