xref: /freebsd/sys/kern/vfs_acl.c (revision 195ebc7e9e4b129de810833791a19dfb4349d6a9)
1 /*-
2  * Copyright (c) 1999-2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * This software was developed by Robert Watson for the TrustedBSD Project.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /*
29  * Developed by the TrustedBSD Project.
30  *
31  * ACL system calls and other functions common across different ACL types.
32  * Type-specific routines go into subr_acl_<type>.c.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include "opt_mac.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/sysproto.h>
43 #include <sys/fcntl.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/mount.h>
47 #include <sys/vnode.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/namei.h>
51 #include <sys/file.h>
52 #include <sys/filedesc.h>
53 #include <sys/proc.h>
54 #include <sys/sysent.h>
55 #include <sys/acl.h>
56 
57 #include <security/mac/mac_framework.h>
58 
59 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
60 
61 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
62 
63 static int	vacl_set_acl(struct thread *td, struct vnode *vp,
64 		    acl_type_t type, struct acl *aclp);
65 static int	vacl_get_acl(struct thread *td, struct vnode *vp,
66 		    acl_type_t type, struct acl *aclp);
67 static int	vacl_aclcheck(struct thread *td, struct vnode *vp,
68 		    acl_type_t type, struct acl *aclp);
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 (EINVAL);
77 
78 	bzero(dest, 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 < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
98 		return (EINVAL);
99 
100 	bzero(dest, 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(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 (EINVAL);
144 	}
145 
146 	return (error);
147 }
148 
149 static int
150 acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type)
151 {
152 	int error;
153 	struct oldacl old;
154 
155 	switch (type) {
156 	case ACL_TYPE_ACCESS_OLD:
157 	case ACL_TYPE_DEFAULT_OLD:
158 		error = acl_copy_acl_into_oldacl(kernel_acl, &old);
159 		if (error != 0)
160 			break;
161 
162 		error = copyout(&old, user_acl, sizeof(old));
163 		break;
164 
165 	default:
166 		if (fuword((char *)user_acl +
167 		    offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES)
168 			return (EINVAL);
169 
170 		error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
171 	}
172 
173 	return (error);
174 }
175 
176 /*
177  * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
178  * counterpart.  It's required for old (pre-NFS4 ACLs) libc to work
179  * with new kernel.  Fixing 'type' for old binaries with new libc
180  * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
181  */
182 static int
183 acl_type_unold(int type)
184 {
185 	switch (type) {
186 	case ACL_TYPE_ACCESS_OLD:
187 		return (ACL_TYPE_ACCESS);
188 
189 	case ACL_TYPE_DEFAULT_OLD:
190 		return (ACL_TYPE_DEFAULT);
191 
192 	default:
193 		return (type);
194 	}
195 }
196 
197 /*
198  * These calls wrap the real vnode operations, and are called by the syscall
199  * code once the syscall has converted the path or file descriptor to a vnode
200  * (unlocked).  The aclp pointer is assumed still to point to userland, so
201  * this should not be consumed within the kernel except by syscall code.
202  * Other code should directly invoke VOP_{SET,GET}ACL.
203  */
204 
205 /*
206  * Given a vnode, set its ACL.
207  */
208 static int
209 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
210     struct acl *aclp)
211 {
212 	struct acl *inkernelacl;
213 	struct mount *mp;
214 	int error;
215 
216 	inkernelacl = acl_alloc(M_WAITOK);
217 	error = acl_copyin(aclp, inkernelacl, type);
218 	if (error)
219 		goto out;
220 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
221 	if (error != 0)
222 		goto out;
223 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
224 #ifdef MAC
225 	error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
226 	if (error != 0)
227 		goto out_unlock;
228 #endif
229 	error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
230 	    td->td_ucred, td);
231 #ifdef MAC
232 out_unlock:
233 #endif
234 	VOP_UNLOCK(vp, 0);
235 	vn_finished_write(mp);
236 out:
237 	acl_free(inkernelacl);
238 	return(error);
239 }
240 
241 /*
242  * Given a vnode, get its ACL.
243  */
244 static int
245 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
246     struct acl *aclp)
247 {
248 	struct acl *inkernelacl;
249 	int error;
250 
251 	inkernelacl = acl_alloc(M_WAITOK);
252 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
253 #ifdef MAC
254 	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
255 	if (error != 0)
256 		goto out;
257 #endif
258 	error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
259 	    td->td_ucred, td);
260 
261 #ifdef MAC
262 out:
263 #endif
264 	VOP_UNLOCK(vp, 0);
265 	if (error == 0)
266 		error = acl_copyout(inkernelacl, aclp, type);
267 	acl_free(inkernelacl);
268 	return (error);
269 }
270 
271 /*
272  * Given a vnode, delete its ACL.
273  */
274 static int
275 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
276 {
277 	struct mount *mp;
278 	int error;
279 
280 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
281 	if (error)
282 		return (error);
283 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
284 #ifdef MAC
285 	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
286 	if (error)
287 		goto out;
288 #endif
289 	error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
290 #ifdef MAC
291 out:
292 #endif
293 	VOP_UNLOCK(vp, 0);
294 	vn_finished_write(mp);
295 	return (error);
296 }
297 
298 /*
299  * Given a vnode, check whether an ACL is appropriate for it
300  */
301 static int
302 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
303     struct acl *aclp)
304 {
305 	struct acl *inkernelacl;
306 	int error;
307 
308 	inkernelacl = acl_alloc(M_WAITOK);
309 	error = acl_copyin(aclp, inkernelacl, type);
310 	if (error)
311 		goto out;
312 	error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td);
313 out:
314 	acl_free(inkernelacl);
315 	return (error);
316 }
317 
318 /*
319  * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
320  * need to lock, as the vacl_ code will get/release any locks required.
321  */
322 
323 /*
324  * Given a file path, get an ACL for it
325  */
326 int
327 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
328 {
329 	struct nameidata nd;
330 	int vfslocked, error;
331 
332 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
333 	error = namei(&nd);
334 	vfslocked = NDHASGIANT(&nd);
335 	if (error == 0) {
336 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
337 		NDFREE(&nd, 0);
338 	}
339 	VFS_UNLOCK_GIANT(vfslocked);
340 	return (error);
341 }
342 
343 /*
344  * Given a file path, get an ACL for it; don't follow links.
345  */
346 int
347 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
348 {
349 	struct nameidata nd;
350 	int vfslocked, error;
351 
352 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
353 	error = namei(&nd);
354 	vfslocked = NDHASGIANT(&nd);
355 	if (error == 0) {
356 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
357 		NDFREE(&nd, 0);
358 	}
359 	VFS_UNLOCK_GIANT(vfslocked);
360 	return (error);
361 }
362 
363 /*
364  * Given a file path, set an ACL for it.
365  */
366 int
367 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
368 {
369 	struct nameidata nd;
370 	int vfslocked, error;
371 
372 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
373 	error = namei(&nd);
374 	vfslocked = NDHASGIANT(&nd);
375 	if (error == 0) {
376 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
377 		NDFREE(&nd, 0);
378 	}
379 	VFS_UNLOCK_GIANT(vfslocked);
380 	return (error);
381 }
382 
383 /*
384  * Given a file path, set an ACL for it; don't follow links.
385  */
386 int
387 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
388 {
389 	struct nameidata nd;
390 	int vfslocked, error;
391 
392 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
393 	error = namei(&nd);
394 	vfslocked = NDHASGIANT(&nd);
395 	if (error == 0) {
396 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
397 		NDFREE(&nd, 0);
398 	}
399 	VFS_UNLOCK_GIANT(vfslocked);
400 	return (error);
401 }
402 
403 /*
404  * Given a file descriptor, get an ACL for it.
405  */
406 int
407 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
408 {
409 	struct file *fp;
410 	int vfslocked, error;
411 
412 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
413 	if (error == 0) {
414 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
415 		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
416 		fdrop(fp, td);
417 		VFS_UNLOCK_GIANT(vfslocked);
418 	}
419 	return (error);
420 }
421 
422 /*
423  * Given a file descriptor, set an ACL for it.
424  */
425 int
426 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
427 {
428 	struct file *fp;
429 	int vfslocked, error;
430 
431 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
432 	if (error == 0) {
433 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
434 		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
435 		fdrop(fp, td);
436 		VFS_UNLOCK_GIANT(vfslocked);
437 	}
438 	return (error);
439 }
440 
441 /*
442  * Given a file path, delete an ACL from it.
443  */
444 int
445 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
446 {
447 	struct nameidata nd;
448 	int vfslocked, error;
449 
450 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
451 	error = namei(&nd);
452 	vfslocked = NDHASGIANT(&nd);
453 	if (error == 0) {
454 		error = vacl_delete(td, nd.ni_vp, uap->type);
455 		NDFREE(&nd, 0);
456 	}
457 	VFS_UNLOCK_GIANT(vfslocked);
458 	return (error);
459 }
460 
461 /*
462  * Given a file path, delete an ACL from it; don't follow links.
463  */
464 int
465 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
466 {
467 	struct nameidata nd;
468 	int vfslocked, error;
469 
470 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
471 	error = namei(&nd);
472 	vfslocked = NDHASGIANT(&nd);
473 	if (error == 0) {
474 		error = vacl_delete(td, nd.ni_vp, uap->type);
475 		NDFREE(&nd, 0);
476 	}
477 	VFS_UNLOCK_GIANT(vfslocked);
478 	return (error);
479 }
480 
481 /*
482  * Given a file path, delete an ACL from it.
483  */
484 int
485 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
486 {
487 	struct file *fp;
488 	int vfslocked, error;
489 
490 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
491 	if (error == 0) {
492 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
493 		error = vacl_delete(td, fp->f_vnode, uap->type);
494 		fdrop(fp, td);
495 		VFS_UNLOCK_GIANT(vfslocked);
496 	}
497 	return (error);
498 }
499 
500 /*
501  * Given a file path, check an ACL for it.
502  */
503 int
504 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
505 {
506 	struct nameidata	nd;
507 	int vfslocked, error;
508 
509 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
510 	error = namei(&nd);
511 	vfslocked = NDHASGIANT(&nd);
512 	if (error == 0) {
513 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
514 		NDFREE(&nd, 0);
515 	}
516 	VFS_UNLOCK_GIANT(vfslocked);
517 	return (error);
518 }
519 
520 /*
521  * Given a file path, check an ACL for it; don't follow links.
522  */
523 int
524 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
525 {
526 	struct nameidata	nd;
527 	int vfslocked, error;
528 
529 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
530 	error = namei(&nd);
531 	vfslocked = NDHASGIANT(&nd);
532 	if (error == 0) {
533 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
534 		NDFREE(&nd, 0);
535 	}
536 	VFS_UNLOCK_GIANT(vfslocked);
537 	return (error);
538 }
539 
540 /*
541  * Given a file descriptor, check an ACL for it.
542  */
543 int
544 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
545 {
546 	struct file *fp;
547 	int vfslocked, error;
548 
549 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
550 	if (error == 0) {
551 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
552 		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
553 		fdrop(fp, td);
554 		VFS_UNLOCK_GIANT(vfslocked);
555 	}
556 	return (error);
557 }
558 
559 struct acl *
560 acl_alloc(int flags)
561 {
562 	struct acl *aclp;
563 
564 	aclp = malloc(sizeof(*aclp), M_ACL, flags);
565 	aclp->acl_maxcnt = ACL_MAX_ENTRIES;
566 
567 	return (aclp);
568 }
569 
570 void
571 acl_free(struct acl *aclp)
572 {
573 
574 	free(aclp, M_ACL);
575 }
576