vfs_acl.c revision 198875
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: head/sys/kern/vfs_acl.c 198875 2009-11-04 07:04:15Z trasz $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/sysproto.h>
41#include <sys/fcntl.h>
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/mount.h>
45#include <sys/vnode.h>
46#include <sys/lock.h>
47#include <sys/mutex.h>
48#include <sys/namei.h>
49#include <sys/file.h>
50#include <sys/filedesc.h>
51#include <sys/proc.h>
52#include <sys/sysent.h>
53#include <sys/acl.h>
54
55#include <security/mac/mac_framework.h>
56
57CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
58
59MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
60
61static int	vacl_set_acl(struct thread *td, struct vnode *vp,
62		    acl_type_t type, struct acl *aclp);
63static int	vacl_get_acl(struct thread *td, struct vnode *vp,
64		    acl_type_t type, struct acl *aclp);
65static int	vacl_aclcheck(struct thread *td, struct vnode *vp,
66		    acl_type_t type, struct acl *aclp);
67
68int
69acl_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	bzero(dest, 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
90int
91acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
92{
93	int i;
94
95	if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
96		return (EINVAL);
97
98	bzero(dest, 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 */
123static int
124acl_copyin(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
147static int
148acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type)
149{
150	int error;
151	struct oldacl old;
152
153	switch (type) {
154	case ACL_TYPE_ACCESS_OLD:
155	case ACL_TYPE_DEFAULT_OLD:
156		error = acl_copy_acl_into_oldacl(kernel_acl, &old);
157		if (error != 0)
158			break;
159
160		error = copyout(&old, user_acl, sizeof(old));
161		break;
162
163	default:
164		if (fuword32((char *)user_acl +
165		    offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES)
166			return (EINVAL);
167
168		error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
169	}
170
171	return (error);
172}
173
174/*
175 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
176 * counterpart.  It's required for old (pre-NFS4 ACLs) libc to work
177 * with new kernel.  Fixing 'type' for old binaries with new libc
178 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
179 */
180static int
181acl_type_unold(int type)
182{
183	switch (type) {
184	case ACL_TYPE_ACCESS_OLD:
185		return (ACL_TYPE_ACCESS);
186
187	case ACL_TYPE_DEFAULT_OLD:
188		return (ACL_TYPE_DEFAULT);
189
190	default:
191		return (type);
192	}
193}
194
195/*
196 * These calls wrap the real vnode operations, and are called by the syscall
197 * code once the syscall has converted the path or file descriptor to a vnode
198 * (unlocked).  The aclp pointer is assumed still to point to userland, so
199 * this should not be consumed within the kernel except by syscall code.
200 * Other code should directly invoke VOP_{SET,GET}ACL.
201 */
202
203/*
204 * Given a vnode, set its ACL.
205 */
206static int
207vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
208    struct acl *aclp)
209{
210	struct acl *inkernelacl;
211	struct mount *mp;
212	int error;
213
214	inkernelacl = acl_alloc(M_WAITOK);
215	error = acl_copyin(aclp, inkernelacl, type);
216	if (error != 0)
217		goto out;
218	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
219	if (error != 0)
220		goto out;
221	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
222#ifdef MAC
223	error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
224	if (error != 0)
225		goto out_unlock;
226#endif
227	error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
228	    td->td_ucred, td);
229#ifdef MAC
230out_unlock:
231#endif
232	VOP_UNLOCK(vp, 0);
233	vn_finished_write(mp);
234out:
235	acl_free(inkernelacl);
236	return (error);
237}
238
239/*
240 * Given a vnode, get its ACL.
241 */
242static int
243vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
244    struct acl *aclp)
245{
246	struct acl *inkernelacl;
247	int error;
248
249	inkernelacl = acl_alloc(M_WAITOK);
250	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
251#ifdef MAC
252	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
253	if (error != 0)
254		goto out;
255#endif
256	error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
257	    td->td_ucred, td);
258
259#ifdef MAC
260out:
261#endif
262	VOP_UNLOCK(vp, 0);
263	if (error == 0)
264		error = acl_copyout(inkernelacl, aclp, type);
265	acl_free(inkernelacl);
266	return (error);
267}
268
269/*
270 * Given a vnode, delete its ACL.
271 */
272static int
273vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
274{
275	struct mount *mp;
276	int error;
277
278	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
279	if (error != 0)
280		return (error);
281	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
282#ifdef MAC
283	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
284	if (error != 0)
285		goto out;
286#endif
287	error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
288#ifdef MAC
289out:
290#endif
291	VOP_UNLOCK(vp, 0);
292	vn_finished_write(mp);
293	return (error);
294}
295
296/*
297 * Given a vnode, check whether an ACL is appropriate for it
298 */
299static int
300vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
301    struct acl *aclp)
302{
303	struct acl *inkernelacl;
304	int error;
305
306	inkernelacl = acl_alloc(M_WAITOK);
307	error = acl_copyin(aclp, inkernelacl, type);
308	if (error != 0)
309		goto out;
310	error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td);
311out:
312	acl_free(inkernelacl);
313	return (error);
314}
315
316/*
317 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
318 * need to lock, as the vacl_ code will get/release any locks required.
319 */
320
321/*
322 * Given a file path, get an ACL for it
323 */
324int
325__acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
326{
327	struct nameidata nd;
328	int vfslocked, error;
329
330	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
331	error = namei(&nd);
332	vfslocked = NDHASGIANT(&nd);
333	if (error == 0) {
334		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
335		NDFREE(&nd, 0);
336	}
337	VFS_UNLOCK_GIANT(vfslocked);
338	return (error);
339}
340
341/*
342 * Given a file path, get an ACL for it; don't follow links.
343 */
344int
345__acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
346{
347	struct nameidata nd;
348	int vfslocked, error;
349
350	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
351	error = namei(&nd);
352	vfslocked = NDHASGIANT(&nd);
353	if (error == 0) {
354		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
355		NDFREE(&nd, 0);
356	}
357	VFS_UNLOCK_GIANT(vfslocked);
358	return (error);
359}
360
361/*
362 * Given a file path, set an ACL for it.
363 */
364int
365__acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
366{
367	struct nameidata nd;
368	int vfslocked, error;
369
370	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
371	error = namei(&nd);
372	vfslocked = NDHASGIANT(&nd);
373	if (error == 0) {
374		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
375		NDFREE(&nd, 0);
376	}
377	VFS_UNLOCK_GIANT(vfslocked);
378	return (error);
379}
380
381/*
382 * Given a file path, set an ACL for it; don't follow links.
383 */
384int
385__acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
386{
387	struct nameidata nd;
388	int vfslocked, error;
389
390	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
391	error = namei(&nd);
392	vfslocked = NDHASGIANT(&nd);
393	if (error == 0) {
394		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
395		NDFREE(&nd, 0);
396	}
397	VFS_UNLOCK_GIANT(vfslocked);
398	return (error);
399}
400
401/*
402 * Given a file descriptor, get an ACL for it.
403 */
404int
405__acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
406{
407	struct file *fp;
408	int vfslocked, error;
409
410	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
411	if (error == 0) {
412		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
413		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
414		fdrop(fp, td);
415		VFS_UNLOCK_GIANT(vfslocked);
416	}
417	return (error);
418}
419
420/*
421 * Given a file descriptor, set an ACL for it.
422 */
423int
424__acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
425{
426	struct file *fp;
427	int vfslocked, error;
428
429	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
430	if (error == 0) {
431		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
432		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
433		fdrop(fp, td);
434		VFS_UNLOCK_GIANT(vfslocked);
435	}
436	return (error);
437}
438
439/*
440 * Given a file path, delete an ACL from it.
441 */
442int
443__acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
444{
445	struct nameidata nd;
446	int vfslocked, error;
447
448	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
449	error = namei(&nd);
450	vfslocked = NDHASGIANT(&nd);
451	if (error == 0) {
452		error = vacl_delete(td, nd.ni_vp, uap->type);
453		NDFREE(&nd, 0);
454	}
455	VFS_UNLOCK_GIANT(vfslocked);
456	return (error);
457}
458
459/*
460 * Given a file path, delete an ACL from it; don't follow links.
461 */
462int
463__acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
464{
465	struct nameidata nd;
466	int vfslocked, error;
467
468	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
469	error = namei(&nd);
470	vfslocked = NDHASGIANT(&nd);
471	if (error == 0) {
472		error = vacl_delete(td, nd.ni_vp, uap->type);
473		NDFREE(&nd, 0);
474	}
475	VFS_UNLOCK_GIANT(vfslocked);
476	return (error);
477}
478
479/*
480 * Given a file path, delete an ACL from it.
481 */
482int
483__acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
484{
485	struct file *fp;
486	int vfslocked, error;
487
488	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
489	if (error == 0) {
490		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
491		error = vacl_delete(td, fp->f_vnode, uap->type);
492		fdrop(fp, td);
493		VFS_UNLOCK_GIANT(vfslocked);
494	}
495	return (error);
496}
497
498/*
499 * Given a file path, check an ACL for it.
500 */
501int
502__acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
503{
504	struct nameidata nd;
505	int vfslocked, error;
506
507	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
508	error = namei(&nd);
509	vfslocked = NDHASGIANT(&nd);
510	if (error == 0) {
511		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
512		NDFREE(&nd, 0);
513	}
514	VFS_UNLOCK_GIANT(vfslocked);
515	return (error);
516}
517
518/*
519 * Given a file path, check an ACL for it; don't follow links.
520 */
521int
522__acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
523{
524	struct nameidat nd;
525	int vfslocked, error;
526
527	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
528	error = namei(&nd);
529	vfslocked = NDHASGIANT(&nd);
530	if (error == 0) {
531		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
532		NDFREE(&nd, 0);
533	}
534	VFS_UNLOCK_GIANT(vfslocked);
535	return (error);
536}
537
538/*
539 * Given a file descriptor, check an ACL for it.
540 */
541int
542__acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
543{
544	struct file *fp;
545	int vfslocked, error;
546
547	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
548	if (error == 0) {
549		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
550		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
551		fdrop(fp, td);
552		VFS_UNLOCK_GIANT(vfslocked);
553	}
554	return (error);
555}
556
557struct acl *
558acl_alloc(int flags)
559{
560	struct acl *aclp;
561
562	aclp = malloc(sizeof(*aclp), M_ACL, flags);
563	aclp->acl_maxcnt = ACL_MAX_ENTRIES;
564
565	return (aclp);
566}
567
568void
569acl_free(struct acl *aclp)
570{
571
572	free(aclp, M_ACL);
573}
574