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