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