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