xref: /freebsd/sys/kern/vfs_acl.c (revision acd3428b7d3e94cef0e1881c868cb4b131d4ff41)
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/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 
58 #include <vm/uma.h>
59 
60 uma_zone_t	acl_zone;
61 static int	vacl_set_acl(struct thread *td, struct vnode *vp,
62 		    acl_type_t type, struct acl *aclp);
63 static int	vacl_get_acl(struct thread *td, struct vnode *vp,
64 		    acl_type_t type, struct acl *aclp);
65 static int	vacl_aclcheck(struct thread *td, struct vnode *vp,
66 		    acl_type_t type, struct acl *aclp);
67 
68 /*
69  * These calls wrap the real vnode operations, and are called by the
70  * syscall code once the syscall has converted the path or file
71  * descriptor to a vnode (unlocked).  The aclp pointer is assumed
72  * still to point to userland, so this should not be consumed within
73  * the kernel except by syscall code.  Other code should directly
74  * 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, td);
96 #ifdef MAC
97 	error = mac_check_vnode_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, td);
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, td);
122 #ifdef MAC
123 	error = mac_check_vnode_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, td);
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, td);
151 #ifdef MAC
152 	error = mac_check_vnode_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, td);
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.
184  * Don't need to lock, as the vacl_ code will get/release any locks
185  * required.
186  */
187 
188 /*
189  * Given a file path, get an ACL for it
190  *
191  * MPSAFE
192  */
193 int
194 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
195 {
196 	struct nameidata nd;
197 	int vfslocked, error;
198 
199 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
200 	error = namei(&nd);
201 	vfslocked = NDHASGIANT(&nd);
202 	if (error == 0) {
203 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
204 		NDFREE(&nd, 0);
205 	}
206 	VFS_UNLOCK_GIANT(vfslocked);
207 	return (error);
208 }
209 
210 /*
211  * Given a file path, get an ACL for it; don't follow links.
212  *
213  * MPSAFE
214  */
215 int
216 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
217 {
218 	struct nameidata nd;
219 	int vfslocked, error;
220 
221 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
222 	error = namei(&nd);
223 	vfslocked = NDHASGIANT(&nd);
224 	if (error == 0) {
225 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
226 		NDFREE(&nd, 0);
227 	}
228 	VFS_UNLOCK_GIANT(vfslocked);
229 	return (error);
230 }
231 
232 /*
233  * Given a file path, set an ACL for it
234  *
235  * MPSAFE
236  */
237 int
238 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
239 {
240 	struct nameidata nd;
241 	int vfslocked, error;
242 
243 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
244 	error = namei(&nd);
245 	vfslocked = NDHASGIANT(&nd);
246 	if (error == 0) {
247 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
248 		NDFREE(&nd, 0);
249 	}
250 	VFS_UNLOCK_GIANT(vfslocked);
251 	return (error);
252 }
253 
254 /*
255  * Given a file path, set an ACL for it; don't follow links.
256  *
257  * MPSAFE
258  */
259 int
260 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
261 {
262 	struct nameidata nd;
263 	int vfslocked, error;
264 
265 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
266 	error = namei(&nd);
267 	vfslocked = NDHASGIANT(&nd);
268 	if (error == 0) {
269 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
270 		NDFREE(&nd, 0);
271 	}
272 	VFS_UNLOCK_GIANT(vfslocked);
273 	return (error);
274 }
275 
276 /*
277  * Given a file descriptor, get an ACL for it
278  *
279  * MPSAFE
280  */
281 int
282 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
283 {
284 	struct file *fp;
285 	int vfslocked, error;
286 
287 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
288 	if (error == 0) {
289 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
290 		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
291 		fdrop(fp, td);
292 		VFS_UNLOCK_GIANT(vfslocked);
293 	}
294 	return (error);
295 }
296 
297 /*
298  * Given a file descriptor, set an ACL for it
299  *
300  * MPSAFE
301  */
302 int
303 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
304 {
305 	struct file *fp;
306 	int vfslocked, error;
307 
308 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
309 	if (error == 0) {
310 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
311 		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
312 		fdrop(fp, td);
313 		VFS_UNLOCK_GIANT(vfslocked);
314 	}
315 	return (error);
316 }
317 
318 /*
319  * Given a file path, delete an ACL from it.
320  *
321  * MPSAFE
322  */
323 int
324 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
325 {
326 	struct nameidata nd;
327 	int vfslocked, error;
328 
329 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
330 	error = namei(&nd);
331 	vfslocked = NDHASGIANT(&nd);
332 	if (error == 0) {
333 		error = vacl_delete(td, nd.ni_vp, uap->type);
334 		NDFREE(&nd, 0);
335 	}
336 	VFS_UNLOCK_GIANT(vfslocked);
337 	return (error);
338 }
339 
340 /*
341  * Given a file path, delete an ACL from it; don't follow links.
342  *
343  * MPSAFE
344  */
345 int
346 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
347 {
348 	struct nameidata nd;
349 	int vfslocked, error;
350 
351 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
352 	error = namei(&nd);
353 	vfslocked = NDHASGIANT(&nd);
354 	if (error == 0) {
355 		error = vacl_delete(td, nd.ni_vp, uap->type);
356 		NDFREE(&nd, 0);
357 	}
358 	VFS_UNLOCK_GIANT(vfslocked);
359 	return (error);
360 }
361 
362 /*
363  * Given a file path, delete an ACL from it.
364  *
365  * MPSAFE
366  */
367 int
368 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
369 {
370 	struct file *fp;
371 	int vfslocked, error;
372 
373 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
374 	if (error == 0) {
375 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
376 		error = vacl_delete(td, fp->f_vnode, uap->type);
377 		fdrop(fp, td);
378 		VFS_UNLOCK_GIANT(vfslocked);
379 	}
380 	return (error);
381 }
382 
383 /*
384  * Given a file path, check an ACL for it
385  *
386  * MPSAFE
387  */
388 int
389 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
390 {
391 	struct nameidata	nd;
392 	int vfslocked, error;
393 
394 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
395 	error = namei(&nd);
396 	vfslocked = NDHASGIANT(&nd);
397 	if (error == 0) {
398 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
399 		NDFREE(&nd, 0);
400 	}
401 	VFS_UNLOCK_GIANT(vfslocked);
402 	return (error);
403 }
404 
405 /*
406  * Given a file path, check an ACL for it; don't follow links.
407  *
408  * MPSAFE
409  */
410 int
411 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
412 {
413 	struct nameidata	nd;
414 	int vfslocked, error;
415 
416 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
417 	error = namei(&nd);
418 	vfslocked = NDHASGIANT(&nd);
419 	if (error == 0) {
420 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
421 		NDFREE(&nd, 0);
422 	}
423 	VFS_UNLOCK_GIANT(vfslocked);
424 	return (error);
425 }
426 
427 /*
428  * Given a file descriptor, check an ACL for it
429  *
430  * MPSAFE
431  */
432 int
433 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
434 {
435 	struct file *fp;
436 	int vfslocked, error;
437 
438 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
439 	if (error == 0) {
440 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
441 		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
442 		fdrop(fp, td);
443 		VFS_UNLOCK_GIANT(vfslocked);
444 	}
445 	return (error);
446 }
447 
448 /* ARGUSED */
449 
450 static void
451 aclinit(void *dummy __unused)
452 {
453 
454 	acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl),
455 	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
456 }
457 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL)
458