xref: /freebsd/sys/kern/vfs_acl.c (revision db612abe8df3355d1eb23bb3b50fdd97bc21e979)
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 #include <vm/uma.h>
60 
61 uma_zone_t	acl_zone;
62 static int	vacl_set_acl(struct thread *td, struct vnode *vp,
63 		    acl_type_t type, struct acl *aclp);
64 static int	vacl_get_acl(struct thread *td, struct vnode *vp,
65 		    acl_type_t type, struct acl *aclp);
66 static int	vacl_aclcheck(struct thread *td, struct vnode *vp,
67 		    acl_type_t type, struct acl *aclp);
68 
69 /*
70  * These calls wrap the real vnode operations, and are called by the syscall
71  * code once the syscall has converted the path or file descriptor to a vnode
72  * (unlocked).  The aclp pointer is assumed still to point to userland, so
73  * this should not be consumed within the kernel except by syscall code.
74  * Other code should directly invoke VOP_{SET,GET}ACL.
75  */
76 
77 /*
78  * Given a vnode, set its ACL.
79  */
80 static int
81 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
82     struct acl *aclp)
83 {
84 	struct acl inkernacl;
85 	struct mount *mp;
86 	int error;
87 
88 	error = copyin(aclp, &inkernacl, sizeof(struct acl));
89 	if (error)
90 		return(error);
91 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
92 	if (error != 0)
93 		return (error);
94 	VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
95 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
96 #ifdef MAC
97 	error = mac_vnode_check_setacl(td->td_ucred, vp, type, &inkernacl);
98 	if (error != 0)
99 		goto out;
100 #endif
101 	error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td);
102 #ifdef MAC
103 out:
104 #endif
105 	VOP_UNLOCK(vp, 0);
106 	vn_finished_write(mp);
107 	return(error);
108 }
109 
110 /*
111  * Given a vnode, get its ACL.
112  */
113 static int
114 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
115     struct acl *aclp)
116 {
117 	struct acl inkernelacl;
118 	int error;
119 
120 	VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
121 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
122 #ifdef MAC
123 	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
124 	if (error != 0)
125 		goto out;
126 #endif
127 	error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td);
128 #ifdef MAC
129 out:
130 #endif
131 	VOP_UNLOCK(vp, 0);
132 	if (error == 0)
133 		error = copyout(&inkernelacl, aclp, sizeof(struct acl));
134 	return (error);
135 }
136 
137 /*
138  * Given a vnode, delete its ACL.
139  */
140 static int
141 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
142 {
143 	struct mount *mp;
144 	int error;
145 
146 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
147 	if (error)
148 		return (error);
149 	VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE);
150 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
151 #ifdef MAC
152 	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
153 	if (error)
154 		goto out;
155 #endif
156 	error = VOP_SETACL(vp, type, 0, td->td_ucred, td);
157 #ifdef MAC
158 out:
159 #endif
160 	VOP_UNLOCK(vp, 0);
161 	vn_finished_write(mp);
162 	return (error);
163 }
164 
165 /*
166  * Given a vnode, check whether an ACL is appropriate for it
167  */
168 static int
169 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
170     struct acl *aclp)
171 {
172 	struct acl inkernelacl;
173 	int error;
174 
175 	error = copyin(aclp, &inkernelacl, sizeof(struct acl));
176 	if (error)
177 		return(error);
178 	error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td);
179 	return (error);
180 }
181 
182 /*
183  * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
184  * need to lock, as the vacl_ code will get/release any locks required.
185  */
186 
187 /*
188  * Given a file path, get an ACL for it
189  */
190 int
191 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
192 {
193 	struct nameidata nd;
194 	int vfslocked, error;
195 
196 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
197 	error = namei(&nd);
198 	vfslocked = NDHASGIANT(&nd);
199 	if (error == 0) {
200 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
201 		NDFREE(&nd, 0);
202 	}
203 	VFS_UNLOCK_GIANT(vfslocked);
204 	return (error);
205 }
206 
207 /*
208  * Given a file path, get an ACL for it; don't follow links.
209  */
210 int
211 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
212 {
213 	struct nameidata nd;
214 	int vfslocked, error;
215 
216 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
217 	error = namei(&nd);
218 	vfslocked = NDHASGIANT(&nd);
219 	if (error == 0) {
220 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
221 		NDFREE(&nd, 0);
222 	}
223 	VFS_UNLOCK_GIANT(vfslocked);
224 	return (error);
225 }
226 
227 /*
228  * Given a file path, set an ACL for it.
229  */
230 int
231 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
232 {
233 	struct nameidata nd;
234 	int vfslocked, error;
235 
236 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
237 	error = namei(&nd);
238 	vfslocked = NDHASGIANT(&nd);
239 	if (error == 0) {
240 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
241 		NDFREE(&nd, 0);
242 	}
243 	VFS_UNLOCK_GIANT(vfslocked);
244 	return (error);
245 }
246 
247 /*
248  * Given a file path, set an ACL for it; don't follow links.
249  */
250 int
251 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
252 {
253 	struct nameidata nd;
254 	int vfslocked, error;
255 
256 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
257 	error = namei(&nd);
258 	vfslocked = NDHASGIANT(&nd);
259 	if (error == 0) {
260 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
261 		NDFREE(&nd, 0);
262 	}
263 	VFS_UNLOCK_GIANT(vfslocked);
264 	return (error);
265 }
266 
267 /*
268  * Given a file descriptor, get an ACL for it.
269  */
270 int
271 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
272 {
273 	struct file *fp;
274 	int vfslocked, error;
275 
276 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
277 	if (error == 0) {
278 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
279 		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
280 		fdrop(fp, td);
281 		VFS_UNLOCK_GIANT(vfslocked);
282 	}
283 	return (error);
284 }
285 
286 /*
287  * Given a file descriptor, set an ACL for it.
288  */
289 int
290 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
291 {
292 	struct file *fp;
293 	int vfslocked, error;
294 
295 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
296 	if (error == 0) {
297 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
298 		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
299 		fdrop(fp, td);
300 		VFS_UNLOCK_GIANT(vfslocked);
301 	}
302 	return (error);
303 }
304 
305 /*
306  * Given a file path, delete an ACL from it.
307  */
308 int
309 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
310 {
311 	struct nameidata nd;
312 	int vfslocked, error;
313 
314 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
315 	error = namei(&nd);
316 	vfslocked = NDHASGIANT(&nd);
317 	if (error == 0) {
318 		error = vacl_delete(td, nd.ni_vp, uap->type);
319 		NDFREE(&nd, 0);
320 	}
321 	VFS_UNLOCK_GIANT(vfslocked);
322 	return (error);
323 }
324 
325 /*
326  * Given a file path, delete an ACL from it; don't follow links.
327  */
328 int
329 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
330 {
331 	struct nameidata nd;
332 	int vfslocked, error;
333 
334 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
335 	error = namei(&nd);
336 	vfslocked = NDHASGIANT(&nd);
337 	if (error == 0) {
338 		error = vacl_delete(td, nd.ni_vp, uap->type);
339 		NDFREE(&nd, 0);
340 	}
341 	VFS_UNLOCK_GIANT(vfslocked);
342 	return (error);
343 }
344 
345 /*
346  * Given a file path, delete an ACL from it.
347  */
348 int
349 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
350 {
351 	struct file *fp;
352 	int vfslocked, error;
353 
354 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
355 	if (error == 0) {
356 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
357 		error = vacl_delete(td, fp->f_vnode, uap->type);
358 		fdrop(fp, td);
359 		VFS_UNLOCK_GIANT(vfslocked);
360 	}
361 	return (error);
362 }
363 
364 /*
365  * Given a file path, check an ACL for it.
366  */
367 int
368 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
369 {
370 	struct nameidata	nd;
371 	int vfslocked, error;
372 
373 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
374 	error = namei(&nd);
375 	vfslocked = NDHASGIANT(&nd);
376 	if (error == 0) {
377 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
378 		NDFREE(&nd, 0);
379 	}
380 	VFS_UNLOCK_GIANT(vfslocked);
381 	return (error);
382 }
383 
384 /*
385  * Given a file path, check an ACL for it; don't follow links.
386  */
387 int
388 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
389 {
390 	struct nameidata	nd;
391 	int vfslocked, error;
392 
393 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
394 	error = namei(&nd);
395 	vfslocked = NDHASGIANT(&nd);
396 	if (error == 0) {
397 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
398 		NDFREE(&nd, 0);
399 	}
400 	VFS_UNLOCK_GIANT(vfslocked);
401 	return (error);
402 }
403 
404 /*
405  * Given a file descriptor, check an ACL for it.
406  */
407 int
408 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
409 {
410 	struct file *fp;
411 	int vfslocked, error;
412 
413 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
414 	if (error == 0) {
415 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
416 		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
417 		fdrop(fp, td);
418 		VFS_UNLOCK_GIANT(vfslocked);
419 	}
420 	return (error);
421 }
422 
423 /* ARGUSED */
424 
425 static void
426 aclinit(void *dummy __unused)
427 {
428 
429 	acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl),
430 	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
431 }
432 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL);
433