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 <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	int error;
152	struct oldacl old;
153
154	switch (type) {
155	case ACL_TYPE_ACCESS_OLD:
156	case ACL_TYPE_DEFAULT_OLD:
157		error = acl_copy_acl_into_oldacl(kernel_acl, &old);
158		if (error != 0)
159			break;
160
161		error = copyout(&old, user_acl, sizeof(old));
162		break;
163
164	default:
165		if (fuword32((char *)user_acl +
166		    offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES)
167			return (EINVAL);
168
169		error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
170	}
171
172	return (error);
173}
174
175/*
176 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
177 * counterpart.  It's required for old (pre-NFSv4 ACLs) libc to work
178 * with new kernel.  Fixing 'type' for old binaries with new libc
179 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
180 */
181static int
182acl_type_unold(int type)
183{
184	switch (type) {
185	case ACL_TYPE_ACCESS_OLD:
186		return (ACL_TYPE_ACCESS);
187
188	case ACL_TYPE_DEFAULT_OLD:
189		return (ACL_TYPE_DEFAULT);
190
191	default:
192		return (type);
193	}
194}
195
196/*
197 * These calls wrap the real vnode operations, and are called by the syscall
198 * code once the syscall has converted the path or file descriptor to a vnode
199 * (unlocked).  The aclp pointer is assumed still to point to userland, so
200 * this should not be consumed within the kernel except by syscall code.
201 * Other code should directly invoke VOP_{SET,GET}ACL.
202 */
203
204/*
205 * Given a vnode, set its ACL.
206 */
207static int
208vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
209    struct acl *aclp)
210{
211	struct acl *inkernelacl;
212	struct mount *mp;
213	int error;
214
215	inkernelacl = acl_alloc(M_WAITOK);
216	error = acl_copyin(aclp, inkernelacl, type);
217	if (error != 0)
218		goto out;
219	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
220	if (error != 0)
221		goto out;
222	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
223#ifdef MAC
224	error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
225	if (error != 0)
226		goto out_unlock;
227#endif
228	error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
229	    td->td_ucred, td);
230#ifdef MAC
231out_unlock:
232#endif
233	VOP_UNLOCK(vp, 0);
234	vn_finished_write(mp);
235out:
236	acl_free(inkernelacl);
237	return (error);
238}
239
240/*
241 * Given a vnode, get its ACL.
242 */
243static int
244vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
245    struct acl *aclp)
246{
247	struct acl *inkernelacl;
248	int error;
249
250	inkernelacl = acl_alloc(M_WAITOK | M_ZERO);
251	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
252#ifdef MAC
253	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
254	if (error != 0)
255		goto out;
256#endif
257	error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
258	    td->td_ucred, td);
259
260#ifdef MAC
261out:
262#endif
263	VOP_UNLOCK(vp, 0);
264	if (error == 0)
265		error = acl_copyout(inkernelacl, aclp, type);
266	acl_free(inkernelacl);
267	return (error);
268}
269
270/*
271 * Given a vnode, delete its ACL.
272 */
273static int
274vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
275{
276	struct mount *mp;
277	int error;
278
279	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
280	if (error != 0)
281		return (error);
282	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
283#ifdef MAC
284	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
285	if (error != 0)
286		goto out;
287#endif
288	error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
289#ifdef MAC
290out:
291#endif
292	VOP_UNLOCK(vp, 0);
293	vn_finished_write(mp);
294	return (error);
295}
296
297/*
298 * Given a vnode, check whether an ACL is appropriate for it
299 */
300static int
301vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
302    struct acl *aclp)
303{
304	struct acl *inkernelacl;
305	int error;
306
307	inkernelacl = acl_alloc(M_WAITOK);
308	error = acl_copyin(aclp, inkernelacl, type);
309	if (error != 0)
310		goto out;
311	error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl,
312	    td->td_ucred, td);
313out:
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 */
326int
327sys___acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
328{
329	struct nameidata nd;
330	int error;
331
332	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td);
333	error = namei(&nd);
334	if (error == 0) {
335		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
336		NDFREE(&nd, 0);
337	}
338	return (error);
339}
340
341/*
342 * Given a file path, get an ACL for it; don't follow links.
343 */
344int
345sys___acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
346{
347	struct nameidata nd;
348	int error;
349
350	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td);
351	error = namei(&nd);
352	if (error == 0) {
353		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
354		NDFREE(&nd, 0);
355	}
356	return (error);
357}
358
359/*
360 * Given a file path, set an ACL for it.
361 */
362int
363sys___acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
364{
365	struct nameidata nd;
366	int error;
367
368	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td);
369	error = namei(&nd);
370	if (error == 0) {
371		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
372		NDFREE(&nd, 0);
373	}
374	return (error);
375}
376
377/*
378 * Given a file path, set an ACL for it; don't follow links.
379 */
380int
381sys___acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
382{
383	struct nameidata nd;
384	int error;
385
386	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td);
387	error = namei(&nd);
388	if (error == 0) {
389		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
390		NDFREE(&nd, 0);
391	}
392	return (error);
393}
394
395/*
396 * Given a file descriptor, get an ACL for it.
397 */
398int
399sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
400{
401	struct file *fp;
402	cap_rights_t rights;
403	int error;
404
405	error = getvnode(td->td_proc->p_fd, uap->filedes,
406	    cap_rights_init(&rights, CAP_ACL_GET), &fp);
407	if (error == 0) {
408		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
409		fdrop(fp, td);
410	}
411	return (error);
412}
413
414/*
415 * Given a file descriptor, set an ACL for it.
416 */
417int
418sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
419{
420	struct file *fp;
421	cap_rights_t rights;
422	int error;
423
424	error = getvnode(td->td_proc->p_fd, uap->filedes,
425	    cap_rights_init(&rights, CAP_ACL_SET), &fp);
426	if (error == 0) {
427		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
428		fdrop(fp, td);
429	}
430	return (error);
431}
432
433/*
434 * Given a file path, delete an ACL from it.
435 */
436int
437sys___acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
438{
439	struct nameidata nd;
440	int error;
441
442	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td);
443	error = namei(&nd);
444	if (error == 0) {
445		error = vacl_delete(td, nd.ni_vp, uap->type);
446		NDFREE(&nd, 0);
447	}
448	return (error);
449}
450
451/*
452 * Given a file path, delete an ACL from it; don't follow links.
453 */
454int
455sys___acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
456{
457	struct nameidata nd;
458	int error;
459
460	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td);
461	error = namei(&nd);
462	if (error == 0) {
463		error = vacl_delete(td, nd.ni_vp, uap->type);
464		NDFREE(&nd, 0);
465	}
466	return (error);
467}
468
469/*
470 * Given a file path, delete an ACL from it.
471 */
472int
473sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
474{
475	struct file *fp;
476	cap_rights_t rights;
477	int error;
478
479	error = getvnode(td->td_proc->p_fd, uap->filedes,
480	    cap_rights_init(&rights, CAP_ACL_DELETE), &fp);
481	if (error == 0) {
482		error = vacl_delete(td, fp->f_vnode, uap->type);
483		fdrop(fp, td);
484	}
485	return (error);
486}
487
488/*
489 * Given a file path, check an ACL for it.
490 */
491int
492sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
493{
494	struct nameidata nd;
495	int error;
496
497	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td);
498	error = namei(&nd);
499	if (error == 0) {
500		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
501		NDFREE(&nd, 0);
502	}
503	return (error);
504}
505
506/*
507 * Given a file path, check an ACL for it; don't follow links.
508 */
509int
510sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
511{
512	struct nameidata nd;
513	int error;
514
515	NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td);
516	error = namei(&nd);
517	if (error == 0) {
518		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
519		NDFREE(&nd, 0);
520	}
521	return (error);
522}
523
524/*
525 * Given a file descriptor, check an ACL for it.
526 */
527int
528sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
529{
530	struct file *fp;
531	cap_rights_t rights;
532	int error;
533
534	error = getvnode(td->td_proc->p_fd, uap->filedes,
535	    cap_rights_init(&rights, CAP_ACL_CHECK), &fp);
536	if (error == 0) {
537		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
538		fdrop(fp, td);
539	}
540	return (error);
541}
542
543struct acl *
544acl_alloc(int flags)
545{
546	struct acl *aclp;
547
548	aclp = malloc(sizeof(*aclp), M_ACL, flags);
549	if (aclp == NULL)
550		return (NULL);
551
552	aclp->acl_maxcnt = ACL_MAX_ENTRIES;
553
554	return (aclp);
555}
556
557void
558acl_free(struct acl *aclp)
559{
560
561	free(aclp, M_ACL);
562}
563