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