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