xref: /freebsd/sys/kern/vfs_acl.c (revision c0020399a650364d0134f79f3fa319f84064372d)
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 static MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
60 
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 *inkernelacl;
84 	struct mount *mp;
85 	int error;
86 
87 	inkernelacl = acl_alloc(M_WAITOK);
88 	error = copyin(aclp, inkernelacl, sizeof(struct acl));
89 	if (error)
90 		goto out;
91 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
92 	if (error != 0)
93 		goto out;
94 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
95 #ifdef MAC
96 	error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
97 	if (error != 0)
98 		goto out_unlock;
99 #endif
100 	error = VOP_SETACL(vp, type, inkernelacl, td->td_ucred, td);
101 #ifdef MAC
102 out_unlock:
103 #endif
104 	VOP_UNLOCK(vp, 0);
105 	vn_finished_write(mp);
106 out:
107 	acl_free(inkernelacl);
108 	return(error);
109 }
110 
111 /*
112  * Given a vnode, get its ACL.
113  */
114 static int
115 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
116     struct acl *aclp)
117 {
118 	struct acl *inkernelacl;
119 	int error;
120 
121 	inkernelacl = acl_alloc(M_WAITOK);
122 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
123 #ifdef MAC
124 	error = mac_vnode_check_getacl(td->td_ucred, vp, type);
125 	if (error != 0)
126 		goto out;
127 #endif
128 	error = VOP_GETACL(vp, type, inkernelacl, td->td_ucred, td);
129 #ifdef MAC
130 out:
131 #endif
132 	VOP_UNLOCK(vp, 0);
133 	if (error == 0)
134 		error = copyout(inkernelacl, aclp, sizeof(struct acl));
135 	acl_free(inkernelacl);
136 	return (error);
137 }
138 
139 /*
140  * Given a vnode, delete its ACL.
141  */
142 static int
143 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
144 {
145 	struct mount *mp;
146 	int error;
147 
148 	error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
149 	if (error)
150 		return (error);
151 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
152 #ifdef MAC
153 	error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
154 	if (error)
155 		goto out;
156 #endif
157 	error = VOP_SETACL(vp, type, 0, td->td_ucred, td);
158 #ifdef MAC
159 out:
160 #endif
161 	VOP_UNLOCK(vp, 0);
162 	vn_finished_write(mp);
163 	return (error);
164 }
165 
166 /*
167  * Given a vnode, check whether an ACL is appropriate for it
168  */
169 static int
170 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
171     struct acl *aclp)
172 {
173 	struct acl *inkernelacl;
174 	int error;
175 
176 	inkernelacl = acl_alloc(M_WAITOK);
177 	error = copyin(aclp, inkernelacl, sizeof(struct acl));
178 	if (error)
179 		goto out;
180 	error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td);
181 out:
182 	acl_free(inkernelacl);
183 	return (error);
184 }
185 
186 /*
187  * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
188  * need to lock, as the vacl_ code will get/release any locks required.
189  */
190 
191 /*
192  * Given a file path, get an ACL for it
193  */
194 int
195 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
196 {
197 	struct nameidata nd;
198 	int vfslocked, error;
199 
200 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
201 	error = namei(&nd);
202 	vfslocked = NDHASGIANT(&nd);
203 	if (error == 0) {
204 		error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
205 		NDFREE(&nd, 0);
206 	}
207 	VFS_UNLOCK_GIANT(vfslocked);
208 	return (error);
209 }
210 
211 /*
212  * Given a file path, get an ACL for it; don't follow links.
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 int
235 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
236 {
237 	struct nameidata nd;
238 	int vfslocked, error;
239 
240 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
241 	error = namei(&nd);
242 	vfslocked = NDHASGIANT(&nd);
243 	if (error == 0) {
244 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
245 		NDFREE(&nd, 0);
246 	}
247 	VFS_UNLOCK_GIANT(vfslocked);
248 	return (error);
249 }
250 
251 /*
252  * Given a file path, set an ACL for it; don't follow links.
253  */
254 int
255 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
256 {
257 	struct nameidata nd;
258 	int vfslocked, error;
259 
260 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
261 	error = namei(&nd);
262 	vfslocked = NDHASGIANT(&nd);
263 	if (error == 0) {
264 		error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp);
265 		NDFREE(&nd, 0);
266 	}
267 	VFS_UNLOCK_GIANT(vfslocked);
268 	return (error);
269 }
270 
271 /*
272  * Given a file descriptor, get an ACL for it.
273  */
274 int
275 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
276 {
277 	struct file *fp;
278 	int vfslocked, error;
279 
280 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
281 	if (error == 0) {
282 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
283 		error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
284 		fdrop(fp, td);
285 		VFS_UNLOCK_GIANT(vfslocked);
286 	}
287 	return (error);
288 }
289 
290 /*
291  * Given a file descriptor, set an ACL for it.
292  */
293 int
294 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
295 {
296 	struct file *fp;
297 	int vfslocked, error;
298 
299 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
300 	if (error == 0) {
301 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
302 		error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
303 		fdrop(fp, td);
304 		VFS_UNLOCK_GIANT(vfslocked);
305 	}
306 	return (error);
307 }
308 
309 /*
310  * Given a file path, delete an ACL from it.
311  */
312 int
313 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
314 {
315 	struct nameidata nd;
316 	int vfslocked, error;
317 
318 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
319 	error = namei(&nd);
320 	vfslocked = NDHASGIANT(&nd);
321 	if (error == 0) {
322 		error = vacl_delete(td, nd.ni_vp, uap->type);
323 		NDFREE(&nd, 0);
324 	}
325 	VFS_UNLOCK_GIANT(vfslocked);
326 	return (error);
327 }
328 
329 /*
330  * Given a file path, delete an ACL from it; don't follow links.
331  */
332 int
333 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
334 {
335 	struct nameidata nd;
336 	int vfslocked, error;
337 
338 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
339 	error = namei(&nd);
340 	vfslocked = NDHASGIANT(&nd);
341 	if (error == 0) {
342 		error = vacl_delete(td, nd.ni_vp, uap->type);
343 		NDFREE(&nd, 0);
344 	}
345 	VFS_UNLOCK_GIANT(vfslocked);
346 	return (error);
347 }
348 
349 /*
350  * Given a file path, delete an ACL from it.
351  */
352 int
353 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
354 {
355 	struct file *fp;
356 	int vfslocked, error;
357 
358 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
359 	if (error == 0) {
360 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
361 		error = vacl_delete(td, fp->f_vnode, uap->type);
362 		fdrop(fp, td);
363 		VFS_UNLOCK_GIANT(vfslocked);
364 	}
365 	return (error);
366 }
367 
368 /*
369  * Given a file path, check an ACL for it.
370  */
371 int
372 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
373 {
374 	struct nameidata	nd;
375 	int vfslocked, error;
376 
377 	NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
378 	error = namei(&nd);
379 	vfslocked = NDHASGIANT(&nd);
380 	if (error == 0) {
381 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
382 		NDFREE(&nd, 0);
383 	}
384 	VFS_UNLOCK_GIANT(vfslocked);
385 	return (error);
386 }
387 
388 /*
389  * Given a file path, check an ACL for it; don't follow links.
390  */
391 int
392 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
393 {
394 	struct nameidata	nd;
395 	int vfslocked, error;
396 
397 	NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
398 	error = namei(&nd);
399 	vfslocked = NDHASGIANT(&nd);
400 	if (error == 0) {
401 		error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
402 		NDFREE(&nd, 0);
403 	}
404 	VFS_UNLOCK_GIANT(vfslocked);
405 	return (error);
406 }
407 
408 /*
409  * Given a file descriptor, check an ACL for it.
410  */
411 int
412 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
413 {
414 	struct file *fp;
415 	int vfslocked, error;
416 
417 	error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
418 	if (error == 0) {
419 		vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
420 		error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
421 		fdrop(fp, td);
422 		VFS_UNLOCK_GIANT(vfslocked);
423 	}
424 	return (error);
425 }
426 
427 struct acl *
428 acl_alloc(int flags)
429 {
430 	struct acl *aclp;
431 
432 	aclp = malloc(sizeof(*aclp), M_ACL, flags);
433 
434 	return (aclp);
435 }
436 
437 void
438 acl_free(struct acl *aclp)
439 {
440 
441 	free(aclp, M_ACL);
442 }
443