]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/vfs_acl.c
Make 'struct acl' larger, as required to support NFSv4 ACLs. Provide
[FreeBSD/FreeBSD.git] / sys / kern / vfs_acl.c
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 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
60
61 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
62
63 static int      vacl_set_acl(struct thread *td, struct vnode *vp,
64                     acl_type_t type, struct acl *aclp);
65 static int      vacl_get_acl(struct thread *td, struct vnode *vp,
66                     acl_type_t type, struct acl *aclp);
67 static int      vacl_aclcheck(struct thread *td, struct vnode *vp,
68                     acl_type_t type, struct acl *aclp);
69
70 int
71 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest)
72 {
73         int i;
74
75         if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
76                 return (EINVAL);
77         
78         bzero(dest, sizeof(*dest));
79
80         dest->acl_cnt = source->acl_cnt;
81         dest->acl_maxcnt = ACL_MAX_ENTRIES;
82
83         for (i = 0; i < dest->acl_cnt; i++) {
84                 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
85                 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
86                 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
87         }
88
89         return (0);
90 }
91
92 int
93 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
94 {
95         int i;
96
97         if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
98                 return (EINVAL);
99
100         bzero(dest, sizeof(*dest));
101
102         dest->acl_cnt = source->acl_cnt;
103
104         for (i = 0; i < dest->acl_cnt; i++) {
105                 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
106                 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
107                 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
108         }
109
110         return (0);
111 }
112
113 /*
114  * At one time, "struct ACL" was extended in order to add support for NFSv4
115  * ACLs.  Instead of creating compatibility versions of all the ACL-related
116  * syscalls, they were left intact.  It's possible to find out what the code
117  * calling these syscalls (libc) expects basing on "type" argument - if it's
118  * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were
119  * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct
120  * oldacl".  If it's something else, then it's the new "struct acl".  In the
121  * latter case, the routines below just copyin/copyout the contents.  In the
122  * former case, they copyin the "struct oldacl" and convert it to the new
123  * format.
124  */
125 static int
126 acl_copyin(void *user_acl, struct acl *kernel_acl, acl_type_t type)
127 {
128         int error;
129         struct oldacl old;
130
131         switch (type) {
132         case ACL_TYPE_ACCESS_OLD:
133         case ACL_TYPE_DEFAULT_OLD:
134                 error = copyin(user_acl, &old, sizeof(old));
135                 if (error != 0)
136                         break;
137                 acl_copy_oldacl_into_acl(&old, kernel_acl);
138                 break;
139
140         default:
141                 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl));
142                 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES)
143                         return (EINVAL);
144         }
145
146         return (error);
147 }
148
149 static int
150 acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type)
151 {
152         int error;
153         struct oldacl old;
154
155         switch (type) {
156         case ACL_TYPE_ACCESS_OLD:
157         case ACL_TYPE_DEFAULT_OLD:
158                 error = acl_copy_acl_into_oldacl(kernel_acl, &old);
159                 if (error != 0)
160                         break;
161
162                 error = copyout(&old, user_acl, sizeof(old));
163                 break;
164
165         default:
166                 if (fuword((char *)user_acl +
167                     offsetof(struct acl, acl_maxcnt)) != ACL_MAX_ENTRIES)
168                         return (EINVAL);
169
170                 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
171         }
172
173         return (error);
174 }
175
176 /*
177  * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
178  * counterpart.  It's required for old (pre-NFS4 ACLs) libc to work
179  * with new kernel.  Fixing 'type' for old binaries with new libc
180  * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
181  */
182 static int
183 acl_type_unold(int type)
184 {
185         switch (type) {
186         case ACL_TYPE_ACCESS_OLD:
187                 return (ACL_TYPE_ACCESS);
188
189         case ACL_TYPE_DEFAULT_OLD:
190                 return (ACL_TYPE_DEFAULT);
191
192         default:
193                 return (type);
194         }
195 }
196
197 /*
198  * These calls wrap the real vnode operations, and are called by the syscall
199  * code once the syscall has converted the path or file descriptor to a vnode
200  * (unlocked).  The aclp pointer is assumed still to point to userland, so
201  * this should not be consumed within the kernel except by syscall code.
202  * Other code should directly invoke VOP_{SET,GET}ACL.
203  */
204
205 /*
206  * Given a vnode, set its ACL.
207  */
208 static int
209 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
210     struct acl *aclp)
211 {
212         struct acl *inkernelacl;
213         struct mount *mp;
214         int error;
215
216         inkernelacl = acl_alloc(M_WAITOK);
217         error = acl_copyin(aclp, inkernelacl, type);
218         if (error)
219                 goto out;
220         error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
221         if (error != 0)
222                 goto out;
223         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
224 #ifdef MAC
225         error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
226         if (error != 0)
227                 goto out_unlock;
228 #endif
229         error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
230             td->td_ucred, td);
231 #ifdef MAC
232 out_unlock:
233 #endif
234         VOP_UNLOCK(vp, 0);
235         vn_finished_write(mp);
236 out:
237         acl_free(inkernelacl);
238         return(error);
239 }
240
241 /*
242  * Given a vnode, get its ACL.
243  */
244 static int
245 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
246     struct acl *aclp)
247 {
248         struct acl *inkernelacl;
249         int error;
250
251         inkernelacl = acl_alloc(M_WAITOK);
252         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
253 #ifdef MAC
254         error = mac_vnode_check_getacl(td->td_ucred, vp, type);
255         if (error != 0)
256                 goto out;
257 #endif
258         error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
259             td->td_ucred, td);
260
261 #ifdef MAC
262 out:
263 #endif
264         VOP_UNLOCK(vp, 0);
265         if (error == 0)
266                 error = acl_copyout(inkernelacl, aclp, type);
267         acl_free(inkernelacl);
268         return (error);
269 }
270
271 /*
272  * Given a vnode, delete its ACL.
273  */
274 static int
275 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
276 {
277         struct mount *mp;
278         int error;
279
280         error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
281         if (error)
282                 return (error);
283         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
284 #ifdef MAC
285         error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
286         if (error)
287                 goto out;
288 #endif
289         error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
290 #ifdef MAC
291 out:
292 #endif
293         VOP_UNLOCK(vp, 0);
294         vn_finished_write(mp);
295         return (error);
296 }
297
298 /*
299  * Given a vnode, check whether an ACL is appropriate for it
300  */
301 static int
302 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
303     struct acl *aclp)
304 {
305         struct acl *inkernelacl;
306         int error;
307
308         inkernelacl = acl_alloc(M_WAITOK);
309         error = acl_copyin(aclp, inkernelacl, type);
310         if (error)
311                 goto out;
312         error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td);
313 out:
314         acl_free(inkernelacl);
315         return (error);
316 }
317
318 /*
319  * syscalls -- convert the path/fd to a vnode, and call vacl_whatever.  Don't
320  * need to lock, as the vacl_ code will get/release any locks required.
321  */
322
323 /*
324  * Given a file path, get an ACL for it
325  */
326 int
327 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
328 {
329         struct nameidata nd;
330         int vfslocked, error;
331
332         NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
333         error = namei(&nd);
334         vfslocked = NDHASGIANT(&nd);
335         if (error == 0) {
336                 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
337                 NDFREE(&nd, 0);
338         }
339         VFS_UNLOCK_GIANT(vfslocked);
340         return (error);
341 }
342
343 /*
344  * Given a file path, get an ACL for it; don't follow links.
345  */
346 int
347 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
348 {
349         struct nameidata nd;
350         int vfslocked, error;
351
352         NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
353         error = namei(&nd);
354         vfslocked = NDHASGIANT(&nd);
355         if (error == 0) {
356                 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp);
357                 NDFREE(&nd, 0);
358         }
359         VFS_UNLOCK_GIANT(vfslocked);
360         return (error);
361 }
362
363 /*
364  * Given a file path, set an ACL for it.
365  */
366 int
367 __acl_set_file(struct thread *td, struct __acl_set_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_set_acl(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, set an ACL for it; don't follow links.
385  */
386 int
387 __acl_set_link(struct thread *td, struct __acl_set_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_set_acl(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, get an ACL for it.
405  */
406 int
407 __acl_get_fd(struct thread *td, struct __acl_get_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_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
416                 fdrop(fp, td);
417                 VFS_UNLOCK_GIANT(vfslocked);
418         }
419         return (error);
420 }
421
422 /*
423  * Given a file descriptor, set an ACL for it.
424  */
425 int
426 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
427 {
428         struct file *fp;
429         int vfslocked, error;
430
431         error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
432         if (error == 0) {
433                 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
434                 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
435                 fdrop(fp, td);
436                 VFS_UNLOCK_GIANT(vfslocked);
437         }
438         return (error);
439 }
440
441 /*
442  * Given a file path, delete an ACL from it.
443  */
444 int
445 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
446 {
447         struct nameidata nd;
448         int vfslocked, error;
449
450         NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
451         error = namei(&nd);
452         vfslocked = NDHASGIANT(&nd);
453         if (error == 0) {
454                 error = vacl_delete(td, nd.ni_vp, uap->type);
455                 NDFREE(&nd, 0);
456         }
457         VFS_UNLOCK_GIANT(vfslocked);
458         return (error);
459 }
460
461 /*
462  * Given a file path, delete an ACL from it; don't follow links.
463  */
464 int
465 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
466 {
467         struct nameidata nd;
468         int vfslocked, error;
469
470         NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
471         error = namei(&nd);
472         vfslocked = NDHASGIANT(&nd);
473         if (error == 0) {
474                 error = vacl_delete(td, nd.ni_vp, uap->type);
475                 NDFREE(&nd, 0);
476         }
477         VFS_UNLOCK_GIANT(vfslocked);
478         return (error);
479 }
480
481 /*
482  * Given a file path, delete an ACL from it.
483  */
484 int
485 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
486 {
487         struct file *fp;
488         int vfslocked, error;
489
490         error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
491         if (error == 0) {
492                 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
493                 error = vacl_delete(td, fp->f_vnode, uap->type);
494                 fdrop(fp, td);
495                 VFS_UNLOCK_GIANT(vfslocked);
496         }
497         return (error);
498 }
499
500 /*
501  * Given a file path, check an ACL for it.
502  */
503 int
504 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
505 {
506         struct nameidata        nd;
507         int vfslocked, error;
508
509         NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td);
510         error = namei(&nd);
511         vfslocked = NDHASGIANT(&nd);
512         if (error == 0) {
513                 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
514                 NDFREE(&nd, 0);
515         }
516         VFS_UNLOCK_GIANT(vfslocked);
517         return (error);
518 }
519
520 /*
521  * Given a file path, check an ACL for it; don't follow links.
522  */
523 int
524 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
525 {
526         struct nameidata        nd;
527         int vfslocked, error;
528
529         NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td);
530         error = namei(&nd);
531         vfslocked = NDHASGIANT(&nd);
532         if (error == 0) {
533                 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp);
534                 NDFREE(&nd, 0);
535         }
536         VFS_UNLOCK_GIANT(vfslocked);
537         return (error);
538 }
539
540 /*
541  * Given a file descriptor, check an ACL for it.
542  */
543 int
544 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
545 {
546         struct file *fp;
547         int vfslocked, error;
548
549         error = getvnode(td->td_proc->p_fd, uap->filedes, &fp);
550         if (error == 0) {
551                 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount);
552                 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
553                 fdrop(fp, td);
554                 VFS_UNLOCK_GIANT(vfslocked);
555         }
556         return (error);
557 }
558
559 struct acl *
560 acl_alloc(int flags)
561 {
562         struct acl *aclp;
563
564         aclp = malloc(sizeof(*aclp), M_ACL, flags);
565         aclp->acl_maxcnt = ACL_MAX_ENTRIES;
566
567         return (aclp);
568 }
569
570 void
571 acl_free(struct acl *aclp)
572 {
573
574         free(aclp, M_ACL);
575 }