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