FreeBSD/Linux Kernel Cross Reference
sys/kern/vfs_acl.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999-2006, 2016-2017 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed by Robert Watson for the TrustedBSD Project.
8 *
9 * Portions of this software were developed by BAE Systems, the University of
10 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
11 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
12 * Computing (TC) research program.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35 /*
36 * Developed by the TrustedBSD Project.
37 *
38 * ACL system calls and other functions common across different ACL types.
39 * Type-specific routines go into subr_acl_<type>.c.
40 */
41
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD$");
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/sysproto.h>
48 #include <sys/capsicum.h>
49 #include <sys/fcntl.h>
50 #include <sys/kernel.h>
51 #include <sys/malloc.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <sys/lock.h>
55 #include <sys/mutex.h>
56 #include <sys/namei.h>
57 #include <sys/file.h>
58 #include <sys/filedesc.h>
59 #include <sys/proc.h>
60 #include <sys/sysent.h>
61 #include <sys/acl.h>
62
63 #include <security/audit/audit.h>
64 #include <security/mac/mac_framework.h>
65
66 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES);
67
68 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists");
69
70 static int kern___acl_aclcheck_path(struct thread *td, const char *path,
71 acl_type_t type, struct acl *aclp, int follow);
72 static int kern___acl_delete_path(struct thread *td, const char *path,
73 acl_type_t type, int follow);
74 static int kern___acl_get_path(struct thread *td, const char *path,
75 acl_type_t type, struct acl *aclp, int follow);
76 static int kern___acl_set_path(struct thread *td, const char *path,
77 acl_type_t type, const struct acl *aclp, int follow);
78 static int vacl_set_acl(struct thread *td, struct vnode *vp,
79 acl_type_t type, const struct acl *aclp);
80 static int vacl_get_acl(struct thread *td, struct vnode *vp,
81 acl_type_t type, struct acl *aclp);
82 static int vacl_aclcheck(struct thread *td, struct vnode *vp,
83 acl_type_t type, const struct acl *aclp);
84
85 int
86 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest)
87 {
88 int i;
89
90 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES)
91 return (EINVAL);
92
93 bzero(dest, sizeof(*dest));
94
95 dest->acl_cnt = source->acl_cnt;
96 dest->acl_maxcnt = ACL_MAX_ENTRIES;
97
98 for (i = 0; i < dest->acl_cnt; i++) {
99 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
100 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
101 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
102 }
103
104 return (0);
105 }
106
107 int
108 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest)
109 {
110 int i;
111
112 if (source->acl_cnt > OLDACL_MAX_ENTRIES)
113 return (EINVAL);
114
115 bzero(dest, sizeof(*dest));
116
117 dest->acl_cnt = source->acl_cnt;
118
119 for (i = 0; i < dest->acl_cnt; i++) {
120 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag;
121 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id;
122 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm;
123 }
124
125 return (0);
126 }
127
128 /*
129 * At one time, "struct ACL" was extended in order to add support for NFSv4
130 * ACLs. Instead of creating compatibility versions of all the ACL-related
131 * syscalls, they were left intact. It's possible to find out what the code
132 * calling these syscalls (libc) expects basing on "type" argument - if it's
133 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were
134 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct
135 * oldacl". If it's something else, then it's the new "struct acl". In the
136 * latter case, the routines below just copyin/copyout the contents. In the
137 * former case, they copyin the "struct oldacl" and convert it to the new
138 * format.
139 */
140 static int
141 acl_copyin(const void *user_acl, struct acl *kernel_acl, acl_type_t type)
142 {
143 int error;
144 struct oldacl old;
145
146 switch (type) {
147 case ACL_TYPE_ACCESS_OLD:
148 case ACL_TYPE_DEFAULT_OLD:
149 error = copyin(user_acl, &old, sizeof(old));
150 if (error != 0)
151 break;
152 acl_copy_oldacl_into_acl(&old, kernel_acl);
153 break;
154
155 default:
156 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl));
157 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES)
158 return (EINVAL);
159 }
160
161 return (error);
162 }
163
164 static int
165 acl_copyout(const struct acl *kernel_acl, void *user_acl, acl_type_t type)
166 {
167 uint32_t am;
168 int error;
169 struct oldacl old;
170
171 switch (type) {
172 case ACL_TYPE_ACCESS_OLD:
173 case ACL_TYPE_DEFAULT_OLD:
174 error = acl_copy_acl_into_oldacl(kernel_acl, &old);
175 if (error != 0)
176 break;
177
178 error = copyout(&old, user_acl, sizeof(old));
179 break;
180
181 default:
182 error = fueword32((char *)user_acl +
183 offsetof(struct acl, acl_maxcnt), &am);
184 if (error == -1)
185 return (EFAULT);
186 if (am != ACL_MAX_ENTRIES)
187 return (EINVAL);
188
189 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl));
190 }
191
192 return (error);
193 }
194
195 /*
196 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new"
197 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work
198 * with new kernel. Fixing 'type' for old binaries with new libc
199 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold().
200 */
201 static int
202 acl_type_unold(int type)
203 {
204 switch (type) {
205 case ACL_TYPE_ACCESS_OLD:
206 return (ACL_TYPE_ACCESS);
207
208 case ACL_TYPE_DEFAULT_OLD:
209 return (ACL_TYPE_DEFAULT);
210
211 default:
212 return (type);
213 }
214 }
215
216 /*
217 * These calls wrap the real vnode operations, and are called by the syscall
218 * code once the syscall has converted the path or file descriptor to a vnode
219 * (unlocked). The aclp pointer is assumed still to point to userland, so
220 * this should not be consumed within the kernel except by syscall code.
221 * Other code should directly invoke VOP_{SET,GET}ACL.
222 */
223
224 /*
225 * Given a vnode, set its ACL.
226 */
227 static int
228 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type,
229 const struct acl *aclp)
230 {
231 struct acl *inkernelacl;
232 struct mount *mp;
233 int error;
234
235 AUDIT_ARG_VALUE(type);
236 inkernelacl = acl_alloc(M_WAITOK);
237 error = acl_copyin(aclp, inkernelacl, type);
238 if (error != 0)
239 goto out;
240 error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
241 if (error != 0)
242 goto out;
243 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
244 AUDIT_ARG_VNODE1(vp);
245 #ifdef MAC
246 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl);
247 if (error != 0)
248 goto out_unlock;
249 #endif
250 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl,
251 td->td_ucred, td);
252 #ifdef MAC
253 out_unlock:
254 #endif
255 VOP_UNLOCK(vp);
256 vn_finished_write(mp);
257 out:
258 acl_free(inkernelacl);
259 return (error);
260 }
261
262 /*
263 * Given a vnode, get its ACL.
264 */
265 static int
266 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type,
267 struct acl *aclp)
268 {
269 struct acl *inkernelacl;
270 int error;
271
272 AUDIT_ARG_VALUE(type);
273 inkernelacl = acl_alloc(M_WAITOK | M_ZERO);
274 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
275 AUDIT_ARG_VNODE1(vp);
276 #ifdef MAC
277 error = mac_vnode_check_getacl(td->td_ucred, vp, type);
278 if (error != 0)
279 goto out;
280 #endif
281 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl,
282 td->td_ucred, td);
283
284 #ifdef MAC
285 out:
286 #endif
287 VOP_UNLOCK(vp);
288 if (error == 0)
289 error = acl_copyout(inkernelacl, aclp, type);
290 acl_free(inkernelacl);
291 return (error);
292 }
293
294 /*
295 * Given a vnode, delete its ACL.
296 */
297 static int
298 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type)
299 {
300 struct mount *mp;
301 int error;
302
303 AUDIT_ARG_VALUE(type);
304 error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
305 if (error != 0)
306 return (error);
307 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
308 AUDIT_ARG_VNODE1(vp);
309 #ifdef MAC
310 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type);
311 if (error != 0)
312 goto out;
313 #endif
314 error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td);
315 #ifdef MAC
316 out:
317 #endif
318 VOP_UNLOCK(vp);
319 vn_finished_write(mp);
320 return (error);
321 }
322
323 /*
324 * Given a vnode, check whether an ACL is appropriate for it
325 *
326 * XXXRW: No vnode lock held so can't audit vnode state...?
327 */
328 static int
329 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type,
330 const struct acl *aclp)
331 {
332 struct acl *inkernelacl;
333 int error;
334
335 inkernelacl = acl_alloc(M_WAITOK);
336 error = acl_copyin(aclp, inkernelacl, type);
337 if (error != 0)
338 goto out;
339 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl,
340 td->td_ucred, td);
341 out:
342 acl_free(inkernelacl);
343 return (error);
344 }
345
346 /*
347 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't
348 * need to lock, as the vacl_ code will get/release any locks required.
349 */
350
351 /*
352 * Given a file path, get an ACL for it
353 */
354 int
355 sys___acl_get_file(struct thread *td, struct __acl_get_file_args *uap)
356 {
357
358 return (kern___acl_get_path(td, uap->path, uap->type, uap->aclp,
359 FOLLOW));
360 }
361
362 /*
363 * Given a file path, get an ACL for it; don't follow links.
364 */
365 int
366 sys___acl_get_link(struct thread *td, struct __acl_get_link_args *uap)
367 {
368
369 return(kern___acl_get_path(td, uap->path, uap->type, uap->aclp,
370 NOFOLLOW));
371 }
372
373 static int
374 kern___acl_get_path(struct thread *td, const char *path, acl_type_t type,
375 struct acl *aclp, int follow)
376 {
377 struct nameidata nd;
378 int error;
379
380 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td);
381 error = namei(&nd);
382 if (error == 0) {
383 error = vacl_get_acl(td, nd.ni_vp, type, aclp);
384 NDFREE(&nd, 0);
385 }
386 return (error);
387 }
388
389 /*
390 * Given a file path, set an ACL for it.
391 */
392 int
393 sys___acl_set_file(struct thread *td, struct __acl_set_file_args *uap)
394 {
395
396 return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp,
397 FOLLOW));
398 }
399
400 /*
401 * Given a file path, set an ACL for it; don't follow links.
402 */
403 int
404 sys___acl_set_link(struct thread *td, struct __acl_set_link_args *uap)
405 {
406
407 return(kern___acl_set_path(td, uap->path, uap->type, uap->aclp,
408 NOFOLLOW));
409 }
410
411 static int
412 kern___acl_set_path(struct thread *td, const char *path,
413 acl_type_t type, const struct acl *aclp, int follow)
414 {
415 struct nameidata nd;
416 int error;
417
418 NDINIT(&nd, LOOKUP, follow | AUDITVNODE1, UIO_USERSPACE, path, td);
419 error = namei(&nd);
420 if (error == 0) {
421 error = vacl_set_acl(td, nd.ni_vp, type, aclp);
422 NDFREE(&nd, 0);
423 }
424 return (error);
425 }
426
427 /*
428 * Given a file descriptor, get an ACL for it.
429 */
430 int
431 sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap)
432 {
433 struct file *fp;
434 cap_rights_t rights;
435 int error;
436
437 AUDIT_ARG_FD(uap->filedes);
438 error = getvnode(td, uap->filedes,
439 cap_rights_init_one(&rights, CAP_ACL_GET), &fp);
440 if (error == 0) {
441 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
442 fdrop(fp, td);
443 }
444 return (error);
445 }
446
447 /*
448 * Given a file descriptor, set an ACL for it.
449 */
450 int
451 sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap)
452 {
453 struct file *fp;
454 cap_rights_t rights;
455 int error;
456
457 AUDIT_ARG_FD(uap->filedes);
458 error = getvnode(td, uap->filedes,
459 cap_rights_init_one(&rights, CAP_ACL_SET), &fp);
460 if (error == 0) {
461 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp);
462 fdrop(fp, td);
463 }
464 return (error);
465 }
466
467 /*
468 * Given a file path, delete an ACL from it.
469 */
470 int
471 sys___acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap)
472 {
473
474 return (kern___acl_delete_path(td, uap->path, uap->type, FOLLOW));
475 }
476
477 /*
478 * Given a file path, delete an ACL from it; don't follow links.
479 */
480 int
481 sys___acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap)
482 {
483
484 return (kern___acl_delete_path(td, uap->path, uap->type, NOFOLLOW));
485 }
486
487 static int
488 kern___acl_delete_path(struct thread *td, const char *path,
489 acl_type_t type, int follow)
490 {
491 struct nameidata nd;
492 int error;
493
494 NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td);
495 error = namei(&nd);
496 if (error == 0) {
497 error = vacl_delete(td, nd.ni_vp, type);
498 NDFREE(&nd, 0);
499 }
500 return (error);
501 }
502
503 /*
504 * Given a file path, delete an ACL from it.
505 */
506 int
507 sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap)
508 {
509 struct file *fp;
510 cap_rights_t rights;
511 int error;
512
513 AUDIT_ARG_FD(uap->filedes);
514 error = getvnode(td, uap->filedes,
515 cap_rights_init_one(&rights, CAP_ACL_DELETE), &fp);
516 if (error == 0) {
517 error = vacl_delete(td, fp->f_vnode, uap->type);
518 fdrop(fp, td);
519 }
520 return (error);
521 }
522
523 /*
524 * Given a file path, check an ACL for it.
525 */
526 int
527 sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap)
528 {
529
530 return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp,
531 FOLLOW));
532 }
533
534 /*
535 * Given a file path, check an ACL for it; don't follow links.
536 */
537 int
538 sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap)
539 {
540 return (kern___acl_aclcheck_path(td, uap->path, uap->type, uap->aclp,
541 NOFOLLOW));
542 }
543
544 static int
545 kern___acl_aclcheck_path(struct thread *td, const char *path, acl_type_t type,
546 struct acl *aclp, int follow)
547 {
548 struct nameidata nd;
549 int error;
550
551 NDINIT(&nd, LOOKUP, follow, UIO_USERSPACE, path, td);
552 error = namei(&nd);
553 if (error == 0) {
554 error = vacl_aclcheck(td, nd.ni_vp, type, aclp);
555 NDFREE(&nd, 0);
556 }
557 return (error);
558 }
559
560 /*
561 * Given a file descriptor, check an ACL for it.
562 */
563 int
564 sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap)
565 {
566 struct file *fp;
567 cap_rights_t rights;
568 int error;
569
570 AUDIT_ARG_FD(uap->filedes);
571 error = getvnode(td, uap->filedes,
572 cap_rights_init_one(&rights, CAP_ACL_CHECK), &fp);
573 if (error == 0) {
574 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
575 fdrop(fp, td);
576 }
577 return (error);
578 }
579
580 struct acl *
581 acl_alloc(int flags)
582 {
583 struct acl *aclp;
584
585 aclp = malloc(sizeof(*aclp), M_ACL, flags);
586 if (aclp == NULL)
587 return (NULL);
588
589 aclp->acl_maxcnt = ACL_MAX_ENTRIES;
590
591 return (aclp);
592 }
593
594 void
595 acl_free(struct acl *aclp)
596 {
597
598 free(aclp, M_ACL);
599 }
Cache object: bb3a910e37375e24644466fd38774330
|