1 /*
2 * Copyright (c) 2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/filedesc.h>
32 #include <sys/kernel.h>
33 #include <sys/file_internal.h>
34 #include <sys/guarded.h>
35 #include <sys/sysproto.h>
36 #include <sys/vnode.h>
37 #include <sys/vnode_internal.h>
38 #include <sys/uio_internal.h>
39 #include <sys/ubc_internal.h>
40 #include <vfs/vfs_support.h>
41 #include <security/audit/audit.h>
42 #include <sys/syscall.h>
43 #include <sys/kauth.h>
44 #include <sys/kdebug.h>
45 #include <stdbool.h>
46 #include <vm/vm_protos.h>
47 #include <libkern/section_keywords.h>
48
49 #include <kern/kalloc.h>
50 #include <kern/task.h>
51 #include <kern/exc_guard.h>
52
53 #if CONFIG_MACF && CONFIG_VNGUARD
54 #include <security/mac.h>
55 #include <security/mac_framework.h>
56 #include <security/mac_policy.h>
57 #include <pexpert/pexpert.h>
58 #include <sys/sysctl.h>
59 #include <sys/reason.h>
60 #endif
61
62 #define f_flag fp_glob->fg_flag
63 extern int writev_uio(struct proc *p, int fd, user_addr_t user_iovp,
64 int iovcnt, off_t offset, int flags, guardid_t *puguard,
65 user_ssize_t *retval);
66 extern int write_internal(struct proc *p, int fd, user_addr_t buf,
67 user_size_t nbyte, off_t offset, int flags, guardid_t *puguard,
68 user_ssize_t *retval);
69 extern int exit_with_guard_exception(void *p, mach_exception_data_type_t code,
70 mach_exception_data_type_t subcode);
71 /*
72 * Experimental guarded file descriptor support.
73 */
74
75 kern_return_t task_exception_notify(exception_type_t exception,
76 mach_exception_data_type_t code, mach_exception_data_type_t subcode, const bool fatal);
77
78 #define GUARD_REQUIRED (GUARD_DUP)
79 #define GUARD_ALL (GUARD_REQUIRED | \
80 (GUARD_CLOSE | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE))
81
82 static KALLOC_TYPE_DEFINE(fp_guard_zone, struct fileproc_guard, KT_DEFAULT);
83
84 struct gfp_crarg {
85 guardid_t gca_guard;
86 uint16_t gca_attrs;
87 };
88
89 static struct fileproc_guard *
guarded_fileproc_alloc(guardid_t guard)90 guarded_fileproc_alloc(guardid_t guard)
91 {
92 struct fileproc_guard *fpg;
93
94 fpg = zalloc_flags(fp_guard_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
95 fpg->fpg_guard = guard;
96 return fpg;
97 }
98
99 static void
guarded_fileproc_init(struct fileproc * fp,void * initarg)100 guarded_fileproc_init(struct fileproc *fp, void *initarg)
101 {
102 struct gfp_crarg *arg = initarg;
103
104 assert(arg->gca_attrs);
105 fp->fp_guard = guarded_fileproc_alloc(arg->gca_guard);
106 fp->fp_guard_attrs = arg->gca_attrs;
107 }
108
109 /*
110 * This is called from fdt_fork(),
111 * where it needs to copy a guarded
112 * fd to the new shadow proc.
113 */
114 void
guarded_fileproc_copy_guard(struct fileproc * ofp,struct fileproc * nfp)115 guarded_fileproc_copy_guard(struct fileproc *ofp, struct fileproc *nfp)
116 {
117 struct gfp_crarg arg = {
118 .gca_guard = ofp->fp_guard->fpg_guard,
119 .gca_attrs = ofp->fp_guard_attrs
120 };
121 guarded_fileproc_init(nfp, &arg);
122 }
123
124 /*
125 * This is called from fileproc_free(),
126 * which is why it is safe to call
127 * without holding the proc_fdlock.
128 */
129 void
guarded_fileproc_unguard(struct fileproc * fp)130 guarded_fileproc_unguard(struct fileproc *fp)
131 {
132 struct fileproc_guard *fpg = fp->fp_guard;
133
134 fp->fp_guard_attrs = 0;
135 fp->fp_wset = fpg->fpg_wset;
136
137 zfree(fp_guard_zone, fpg);
138 }
139
140 static int
fp_lookup_guarded_locked(proc_t p,int fd,guardid_t guard,struct fileproc ** fpp)141 fp_lookup_guarded_locked(proc_t p, int fd, guardid_t guard,
142 struct fileproc **fpp)
143 {
144 int error;
145 struct fileproc *fp;
146
147 if ((error = fp_lookup(p, fd, &fp, 1)) != 0) {
148 return error;
149 }
150
151 if (fp->fp_guard_attrs == 0) {
152 (void) fp_drop(p, fd, fp, 1);
153 return EINVAL;
154 }
155
156 if (guard != fp->fp_guard->fpg_guard) {
157 (void) fp_drop(p, fd, fp, 1);
158 return EPERM; /* *not* a mismatch exception */
159 }
160
161 *fpp = fp;
162 return 0;
163 }
164
165 int
fp_lookup_guarded(proc_t p,int fd,guardid_t guard,struct fileproc ** fpp,int locked)166 fp_lookup_guarded(proc_t p, int fd, guardid_t guard,
167 struct fileproc **fpp, int locked)
168 {
169 int error;
170
171 if (!locked) {
172 proc_fdlock_spin(p);
173 }
174
175 error = fp_lookup_guarded_locked(p, fd, guard, fpp);
176
177 if (!locked) {
178 proc_fdunlock(p);
179 }
180
181 return error;
182 }
183
184 /*
185 * Expected use pattern:
186 *
187 * if (fp_isguarded(fp, GUARD_CLOSE)) {
188 * error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE);
189 * proc_fdunlock(p);
190 * return error;
191 * }
192 */
193 int
fp_isguarded(struct fileproc * fp,u_int attrs)194 fp_isguarded(struct fileproc *fp, u_int attrs)
195 {
196 return fp->fp_guard_attrs && (fp->fp_guard_attrs & attrs) == attrs;
197 }
198
199 extern char *proc_name_address(void *p);
200
201 int
fp_guard_exception(proc_t p,int fd,struct fileproc * fp,u_int flavor)202 fp_guard_exception(proc_t p, int fd, struct fileproc *fp, u_int flavor)
203 {
204 /* all fp guard fields protected via proc_fdlock() */
205 proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
206
207 mach_exception_code_t code = 0;
208 EXC_GUARD_ENCODE_TYPE(code, GUARD_TYPE_FD);
209 EXC_GUARD_ENCODE_FLAVOR(code, flavor);
210 EXC_GUARD_ENCODE_TARGET(code, fd);
211 mach_exception_subcode_t subcode = fp->fp_guard->fpg_guard;
212
213 assert(fp->fp_guard_attrs);
214
215 thread_t t = current_thread();
216 thread_guard_violation(t, code, subcode, TRUE);
217 return EPERM;
218 }
219
220 /*
221 * (Invoked before returning to userland from the syscall handler.)
222 */
223 void
fd_guard_ast(thread_t __unused t,mach_exception_code_t code,mach_exception_subcode_t subcode)224 fd_guard_ast(
225 thread_t __unused t,
226 mach_exception_code_t code,
227 mach_exception_subcode_t subcode)
228 {
229 const bool fatal = true;
230 /*
231 * Check if anyone has registered for Synchronous EXC_GUARD, if yes then,
232 * deliver it synchronously and then kill the process, else kill the process
233 * and deliver the exception via EXC_CORPSE_NOTIFY. Always kill the process if we are not in dev mode.
234 */
235 if (task_exception_notify(EXC_GUARD, code, subcode, fatal) == KERN_SUCCESS) {
236 psignal(current_proc(), SIGKILL);
237 } else {
238 exit_with_guard_exception(current_proc(), code, subcode);
239 }
240 }
241
242 /*
243 * Experimental guarded file descriptor SPIs
244 */
245
246 /*
247 * int guarded_open_np(const char *pathname, int flags,
248 * const guardid_t *guard, u_int guardflags, ...);
249 *
250 * In this initial implementation, GUARD_DUP must be specified.
251 * GUARD_CLOSE, GUARD_SOCKET_IPC and GUARD_FILEPORT are optional.
252 *
253 * If GUARD_DUP wasn't specified, then we'd have to do the (extra) work
254 * to allow dup-ing a descriptor to inherit the guard onto the new
255 * descriptor. (Perhaps GUARD_DUP behaviours should just always be true
256 * for a guarded fd? Or, more sanely, all the dup operations should
257 * just always propagate the guard?)
258 *
259 * Guarded descriptors are always close-on-exec, and GUARD_CLOSE
260 * requires close-on-fork; O_CLOEXEC must be set in flags.
261 * This setting is immutable; attempts to clear the flag will
262 * cause a guard exception.
263 *
264 * XXX It's somewhat broken that change_fdguard_np() can completely
265 * remove the guard and thus revoke down the immutability
266 * promises above. Ick.
267 */
268 int
guarded_open_np(proc_t p,struct guarded_open_np_args * uap,int32_t * retval)269 guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval)
270 {
271 if ((uap->flags & O_CLOEXEC) == 0) {
272 return EINVAL;
273 }
274
275 if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
276 ((uap->guardflags & ~GUARD_ALL) != 0)) {
277 return EINVAL;
278 }
279
280 int error;
281 struct gfp_crarg crarg = {
282 .gca_attrs = (uint16_t)uap->guardflags
283 };
284
285 if ((error = copyin(uap->guard,
286 &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) {
287 return error;
288 }
289
290 /*
291 * Disallow certain guard values -- is zero enough?
292 */
293 if (crarg.gca_guard == 0) {
294 return EINVAL;
295 }
296
297 struct vnode_attr va;
298 struct nameidata nd;
299 vfs_context_t ctx = vfs_context_current();
300 int cmode;
301
302 VATTR_INIT(&va);
303 cmode = ((uap->mode & ~p->p_fd.fd_cmask) & ALLPERMS) & ~S_ISTXT;
304 VATTR_SET(&va, va_mode, cmode & ACCESSPERMS);
305
306 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE,
307 uap->path, ctx);
308
309 return open1(ctx, &nd, uap->flags | O_CLOFORK, &va,
310 guarded_fileproc_init, &crarg, retval, AUTH_OPEN_NOAUTHFD);
311 }
312
313 /*
314 * int guarded_open_dprotected_np(const char *pathname, int flags,
315 * const guardid_t *guard, u_int guardflags, int dpclass, int dpflags, ...);
316 *
317 * This SPI is extension of guarded_open_np() to include dataprotection class on creation
318 * in "dpclass" and dataprotection flags 'dpflags'. Otherwise behaviors are same as in
319 * guarded_open_np()
320 */
321 int
guarded_open_dprotected_np(proc_t p,struct guarded_open_dprotected_np_args * uap,int32_t * retval)322 guarded_open_dprotected_np(proc_t p, struct guarded_open_dprotected_np_args *uap, int32_t *retval)
323 {
324 if ((uap->flags & O_CLOEXEC) == 0) {
325 return EINVAL;
326 }
327
328 if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
329 ((uap->guardflags & ~GUARD_ALL) != 0)) {
330 return EINVAL;
331 }
332
333 int error;
334 struct gfp_crarg crarg = {
335 .gca_attrs = (uint16_t)uap->guardflags
336 };
337
338 if ((error = copyin(uap->guard,
339 &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) {
340 return error;
341 }
342
343 /*
344 * Disallow certain guard values -- is zero enough?
345 */
346 if (crarg.gca_guard == 0) {
347 return EINVAL;
348 }
349
350 struct vnode_attr va;
351 struct nameidata nd;
352 vfs_context_t ctx = vfs_context_current();
353 int cmode;
354
355 VATTR_INIT(&va);
356 cmode = ((uap->mode & ~p->p_fd.fd_cmask) & ALLPERMS) & ~S_ISTXT;
357 VATTR_SET(&va, va_mode, cmode & ACCESSPERMS);
358
359 NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE,
360 uap->path, ctx);
361
362 /*
363 * Initialize the extra fields in vnode_attr to pass down dataprotection
364 * extra fields.
365 * 1. target cprotect class.
366 * 2. set a flag to mark it as requiring open-raw-encrypted semantics.
367 */
368 if (uap->flags & O_CREAT) {
369 VATTR_SET(&va, va_dataprotect_class, uap->dpclass);
370 }
371
372 if (uap->dpflags & (O_DP_GETRAWENCRYPTED | O_DP_GETRAWUNENCRYPTED)) {
373 if (uap->flags & (O_RDWR | O_WRONLY)) {
374 /* Not allowed to write raw encrypted bytes */
375 return EINVAL;
376 }
377 if (uap->dpflags & O_DP_GETRAWENCRYPTED) {
378 VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED);
379 }
380 if (uap->dpflags & O_DP_GETRAWUNENCRYPTED) {
381 VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWUNENCRYPTED);
382 }
383 }
384
385 return open1(ctx, &nd, uap->flags | O_CLOFORK, &va,
386 guarded_fileproc_init, &crarg, retval, AUTH_OPEN_NOAUTHFD);
387 }
388
389 /*
390 * int guarded_kqueue_np(const guardid_t *guard, u_int guardflags);
391 *
392 * Create a guarded kqueue descriptor with guardid and guardflags.
393 *
394 * Same restrictions on guardflags as for guarded_open_np().
395 * All kqueues are -always- close-on-exec and close-on-fork by themselves
396 * and are not sendable.
397 */
398 int
guarded_kqueue_np(proc_t p,struct guarded_kqueue_np_args * uap,int32_t * retval)399 guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval)
400 {
401 if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
402 ((uap->guardflags & ~GUARD_ALL) != 0)) {
403 return EINVAL;
404 }
405
406 int error;
407 struct gfp_crarg crarg = {
408 .gca_attrs = (uint16_t)uap->guardflags
409 };
410
411 if ((error = copyin(uap->guard,
412 &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) {
413 return error;
414 }
415
416 if (crarg.gca_guard == 0) {
417 return EINVAL;
418 }
419
420 return kqueue_internal(p, guarded_fileproc_init, &crarg, retval);
421 }
422
423 /*
424 * int guarded_close_np(int fd, const guardid_t *guard);
425 */
426 int
guarded_close_np(proc_t p,struct guarded_close_np_args * uap,__unused int32_t * retval)427 guarded_close_np(proc_t p, struct guarded_close_np_args *uap,
428 __unused int32_t *retval)
429 {
430 struct fileproc *fp;
431 int fd = uap->fd;
432 int error;
433 guardid_t uguard;
434
435 AUDIT_SYSCLOSE(p, fd);
436
437 if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) {
438 return error;
439 }
440
441 proc_fdlock(p);
442 if ((error = fp_lookup_guarded(p, fd, uguard, &fp, 1)) != 0) {
443 proc_fdunlock(p);
444 return error;
445 }
446 fp_drop(p, fd, fp, 1);
447 return fp_close_and_unlock(p, fd, fp, 0);
448 }
449
450 /*
451 * int
452 * change_fdguard_np(int fd, const guardid_t *guard, u_int guardflags,
453 * const guardid_t *nguard, u_int nguardflags, int *fdflagsp);
454 *
455 * Given a file descriptor, atomically exchange <guard, guardflags> for
456 * a new guard <nguard, nguardflags>, returning the previous fd
457 * flags (see fcntl:F_SETFD) in *fdflagsp.
458 *
459 * This syscall can be used to either (a) add a new guard to an existing
460 * unguarded file descriptor (b) remove the old guard from an existing
461 * guarded file descriptor or (c) change the guard (guardid and/or
462 * guardflags) on a guarded file descriptor.
463 *
464 * If 'guard' is NULL, fd must be unguarded at entry. If the call completes
465 * successfully the fd will be guarded with <nguard, nguardflags>.
466 *
467 * Guarding a file descriptor has some side-effects on the "fp_flags"
468 * associated with the descriptor - in particular FD_CLOEXEC is
469 * forced ON unconditionally, and FD_CLOFORK is forced ON by GUARD_CLOSE.
470 * Callers who wish to subsequently restore the state of the fd should save
471 * the value of *fdflagsp after a successful invocation.
472 *
473 * If 'nguard' is NULL, fd must be guarded at entry, <guard, guardflags>
474 * must match with what's already guarding the descriptor, and the
475 * result will be to completely remove the guard.
476 *
477 * If the descriptor is guarded, and neither 'guard' nor 'nguard' is NULL
478 * and <guard, guardflags> matches what's already guarding the descriptor,
479 * then <nguard, nguardflags> becomes the new guard. In this case, even if
480 * the GUARD_CLOSE flag is being cleared, it is still possible to continue
481 * to keep FD_CLOFORK on the descriptor by passing FD_CLOFORK via fdflagsp.
482 *
483 * (File descriptors whose underlying fileglobs are marked FG_CONFINED are
484 * still close-on-fork, regardless of the setting of FD_CLOFORK.)
485 *
486 * Example 1: Guard an unguarded descriptor during a set of operations,
487 * then restore the original state of the descriptor.
488 *
489 * int sav_flags = 0;
490 * change_fdguard_np(fd, NULL, 0, &myguard, GUARD_CLOSE, &sav_flags);
491 * // do things with now guarded 'fd'
492 * change_fdguard_np(fd, &myguard, GUARD_CLOSE, NULL, 0, &sav_flags);
493 * // fd now unguarded.
494 *
495 * Example 2: Change the guard of a guarded descriptor during a set of
496 * operations, then restore the original state of the descriptor.
497 *
498 * int sav_flags = (gdflags & GUARD_CLOSE) ? FD_CLOFORK : 0;
499 * change_fdguard_np(fd, &gd, gdflags, &myguard, GUARD_CLOSE, &sav_flags);
500 * // do things with 'fd' with a different guard
501 * change_fdguard_np(fd, &myg, GUARD_CLOSE, &gd, gdflags, &sav_flags);
502 * // back to original guarded state
503 *
504 * XXX This SPI is too much of a chainsaw and should be revised.
505 */
506
507 int
change_fdguard_np(proc_t p,struct change_fdguard_np_args * uap,__unused int32_t * retval)508 change_fdguard_np(proc_t p, struct change_fdguard_np_args *uap,
509 __unused int32_t *retval)
510 {
511 struct fileproc_guard *fpg = NULL;
512 struct fileproc *fp;
513 int fd = uap->fd;
514 int error;
515 guardid_t oldg = 0, newg = 0;
516 int nfdflags = 0;
517
518 if (0 != uap->guard &&
519 0 != (error = copyin(uap->guard, &oldg, sizeof(oldg)))) {
520 return error; /* can't copyin current guard */
521 }
522 if (0 != uap->nguard &&
523 0 != (error = copyin(uap->nguard, &newg, sizeof(newg)))) {
524 return error; /* can't copyin new guard */
525 }
526 if (0 != uap->fdflagsp &&
527 0 != (error = copyin(uap->fdflagsp, &nfdflags, sizeof(nfdflags)))) {
528 return error; /* can't copyin new fdflags */
529 }
530
531 if (oldg == 0 && newg) {
532 fpg = guarded_fileproc_alloc(newg);
533 }
534
535 proc_fdlock(p);
536
537 if ((error = fp_lookup(p, fd, &fp, 1)) != 0) {
538 proc_fdunlock(p);
539 return error;
540 }
541
542 if (0 != uap->fdflagsp) {
543 int ofl = 0;
544 if (fp->fp_flags & FP_CLOEXEC) {
545 ofl |= FD_CLOEXEC;
546 }
547 if (fp->fp_flags & FP_CLOFORK) {
548 ofl |= FD_CLOFORK;
549 }
550 proc_fdunlock(p);
551 if (0 != (error = copyout(&ofl, uap->fdflagsp, sizeof(ofl)))) {
552 proc_fdlock(p);
553 goto dropout; /* can't copyout old fdflags */
554 }
555 proc_fdlock(p);
556 }
557
558 if (fp->fp_guard_attrs) {
559 if (0 == uap->guard || 0 == uap->guardflags) {
560 error = EINVAL; /* missing guard! */
561 } else if (0 == oldg) {
562 error = EPERM; /* guardids cannot be zero */
563 }
564 } else {
565 if (0 != uap->guard || 0 != uap->guardflags) {
566 error = EINVAL; /* guard provided, but none needed! */
567 }
568 }
569
570 if (0 != error) {
571 goto dropout;
572 }
573
574 if (0 != uap->nguard) {
575 /*
576 * There's a new guard in town.
577 */
578 if (0 == newg) {
579 error = EINVAL; /* guards cannot contain zero */
580 } else if (((uap->nguardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
581 ((uap->nguardflags & ~GUARD_ALL) != 0)) {
582 error = EINVAL; /* must have valid attributes too */
583 }
584 if (0 != error) {
585 goto dropout;
586 }
587
588 if (fp->fp_guard_attrs) {
589 /*
590 * Replace old guard with new guard
591 */
592 if (oldg == fp->fp_guard->fpg_guard &&
593 uap->guardflags == fp->fp_guard_attrs) {
594 /*
595 * Must match existing guard + attributes
596 * before we'll swap them to new ones, managing
597 * fdflags "side-effects" as we go. Note that
598 * userland can request FD_CLOFORK semantics.
599 */
600 if (fp->fp_guard_attrs & GUARD_CLOSE) {
601 fp->fp_flags &= ~FP_CLOFORK;
602 }
603 fp->fp_guard->fpg_guard = newg;
604 fp->fp_guard_attrs = (uint16_t)uap->nguardflags;
605 if ((fp->fp_guard_attrs & GUARD_CLOSE) ||
606 (nfdflags & FD_CLOFORK)) {
607 fp->fp_flags |= FP_CLOFORK;
608 }
609 /* FG_CONFINED enforced regardless */
610 } else {
611 error = EPERM;
612 }
613 } else {
614 /*
615 * Add a guard to a previously unguarded descriptor
616 */
617 switch (FILEGLOB_DTYPE(fp->fp_glob)) {
618 case DTYPE_VNODE:
619 case DTYPE_PIPE:
620 case DTYPE_SOCKET:
621 case DTYPE_KQUEUE:
622 case DTYPE_NETPOLICY:
623 break;
624 default:
625 error = ENOTSUP;
626 goto dropout;
627 }
628
629 fp->fp_guard_attrs = (uint16_t)uap->nguardflags;
630 fpg->fpg_wset = fp->fp_wset;
631 fp->fp_guard = fpg;
632 fpg = NULL;
633 if (fp->fp_guard_attrs & GUARD_CLOSE) {
634 fp->fp_flags |= FP_CLOFORK;
635 }
636 fp->fp_flags |= FP_CLOEXEC;
637 }
638 } else {
639 if (fp->fp_guard_attrs) {
640 /*
641 * Remove the guard altogether.
642 */
643 if (0 != uap->nguardflags) {
644 error = EINVAL;
645 goto dropout;
646 }
647
648 if (oldg != fp->fp_guard->fpg_guard ||
649 uap->guardflags != fp->fp_guard_attrs) {
650 error = EPERM;
651 goto dropout;
652 }
653
654 assert(fpg == NULL);
655 fp->fp_guard_attrs = 0;
656 fpg = fp->fp_guard;
657 fp->fp_wset = fpg->fpg_wset;
658
659 fp->fp_flags &= ~(FP_CLOEXEC | FP_CLOFORK);
660 if (nfdflags & FD_CLOFORK) {
661 fp->fp_flags |= FP_CLOFORK;
662 }
663 if (nfdflags & FD_CLOEXEC) {
664 fp->fp_flags |= FP_CLOEXEC;
665 }
666 } else {
667 /*
668 * Not already guarded, and no new guard?
669 */
670 error = EINVAL;
671 }
672 }
673
674 dropout:
675 (void) fp_drop(p, fd, fp, 1);
676 proc_fdunlock(p);
677
678 if (fpg) {
679 zfree(fp_guard_zone, fpg);
680 }
681 return error;
682 }
683
684 /*
685 * user_ssize_t guarded_write_np(int fd, const guardid_t *guard,
686 * user_addr_t cbuf, user_ssize_t nbyte);
687 *
688 * Initial implementation of guarded writes.
689 */
690 int
guarded_write_np(struct proc * p,struct guarded_write_np_args * uap,user_ssize_t * retval)691 guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t *retval)
692 {
693 int error;
694 guardid_t uguard;
695
696 AUDIT_ARG(fd, uap->fd);
697
698 if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) {
699 return error;
700 }
701
702 return write_internal(p, uap->fd, uap->cbuf, uap->nbyte, 0, 0, &uguard, retval);
703 }
704
705 /*
706 * user_ssize_t guarded_pwrite_np(int fd, const guardid_t *guard,
707 * user_addr_t buf, user_size_t nbyte, off_t offset);
708 *
709 * Initial implementation of guarded pwrites.
710 */
711 int
guarded_pwrite_np(struct proc * p,struct guarded_pwrite_np_args * uap,user_ssize_t * retval)712 guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize_t *retval)
713 {
714 int error;
715 guardid_t uguard;
716
717 AUDIT_ARG(fd, uap->fd);
718
719 if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) {
720 return error;
721 }
722
723 KERNEL_DEBUG_CONSTANT((BSDDBG_CODE(DBG_BSD_SC_EXTENDED_INFO, SYS_guarded_pwrite_np) | DBG_FUNC_NONE),
724 uap->fd, uap->nbyte, (unsigned int)((uap->offset >> 32)), (unsigned int)(uap->offset), 0);
725
726 return write_internal(p, uap->fd, uap->buf, uap->nbyte, uap->offset, FOF_OFFSET,
727 &uguard, retval);
728 }
729
730 /*
731 * user_ssize_t guarded_writev_np(int fd, const guardid_t *guard,
732 * struct iovec *iovp, u_int iovcnt);
733 *
734 * Initial implementation of guarded writev.
735 *
736 */
737 int
guarded_writev_np(struct proc * p,struct guarded_writev_np_args * uap,user_ssize_t * retval)738 guarded_writev_np(struct proc *p, struct guarded_writev_np_args *uap, user_ssize_t *retval)
739 {
740 int error;
741 guardid_t uguard;
742
743 AUDIT_ARG(fd, uap->fd);
744
745 if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) {
746 return error;
747 }
748
749 return writev_uio(p, uap->fd, uap->iovp, uap->iovcnt, 0, 0, &uguard, retval);
750 }
751
752 /*
753 * int falloc_guarded(struct proc *p, struct fileproc **fp, int *fd,
754 * vfs_context_t ctx, const guardid_t *guard, u_int attrs);
755 *
756 * This SPI is the guarded variant of falloc(). It borrows the same
757 * restrictions as those used by the rest of the guarded_* routines.
758 */
759 int
falloc_guarded(struct proc * p,struct fileproc ** fp,int * fd,vfs_context_t ctx,const guardid_t * guard,u_int attrs)760 falloc_guarded(struct proc *p, struct fileproc **fp, int *fd,
761 vfs_context_t ctx, const guardid_t *guard, u_int attrs)
762 {
763 struct gfp_crarg crarg;
764
765 if (((attrs & GUARD_REQUIRED) != GUARD_REQUIRED) ||
766 ((attrs & ~GUARD_ALL) != 0) || (*guard == 0)) {
767 return EINVAL;
768 }
769
770 bzero(&crarg, sizeof(crarg));
771 crarg.gca_guard = *guard;
772 crarg.gca_attrs = (uint16_t)attrs;
773
774 return falloc_withinit(p, fp, fd, ctx, guarded_fileproc_init, &crarg);
775 }
776
777 #if CONFIG_MACF && CONFIG_VNGUARD
778
779 /*
780 * Guarded vnodes
781 *
782 * Uses MAC hooks to guard operations on vnodes in the system. Given an fd,
783 * add data to the label on the fileglob and the vnode it points at.
784 * The data contains a pointer to the fileglob, the set of attributes to
785 * guard, a guard value for uniquification, and the pid of the process
786 * who set the guard up in the first place.
787 *
788 * The fd must have been opened read/write, and the underlying
789 * fileglob is FG_CONFINED so that there's no ambiguity about the
790 * owning process.
791 *
792 * When there's a callback for a vnode operation of interest (rename, unlink,
793 * etc.) check to see if the guard permits that operation, and if not
794 * take an action e.g. log a message or generate a crash report.
795 *
796 * The label is removed from the vnode and the fileglob when the fileglob
797 * is closed.
798 *
799 * The initial action to be taken can be specified by a boot arg (vnguard=0x42)
800 * and change via the "kern.vnguard.flags" sysctl.
801 */
802
803 struct vng_owner;
804
805 struct vng_info { /* lives on the vnode label */
806 guardid_t vgi_guard;
807 unsigned vgi_attrs;
808 TAILQ_HEAD(, vng_owner) vgi_owners;
809 };
810
811 struct vng_owner { /* lives on the fileglob label */
812 proc_t vgo_p;
813 struct vng_info *vgo_vgi;
814 TAILQ_ENTRY(vng_owner) vgo_link;
815 };
816
817 static struct vng_info *
new_vgi(unsigned attrs,guardid_t guard)818 new_vgi(unsigned attrs, guardid_t guard)
819 {
820 struct vng_info *vgi = kalloc_type(struct vng_info, Z_WAITOK);
821 vgi->vgi_guard = guard;
822 vgi->vgi_attrs = attrs;
823 TAILQ_INIT(&vgi->vgi_owners);
824 return vgi;
825 }
826
827 static struct vng_owner *
new_vgo(proc_t p)828 new_vgo(proc_t p)
829 {
830 struct vng_owner *vgo = kalloc_type(struct vng_owner, Z_WAITOK | Z_ZERO);
831 vgo->vgo_p = p;
832 return vgo;
833 }
834
835 static void
vgi_add_vgo(struct vng_info * vgi,struct vng_owner * vgo)836 vgi_add_vgo(struct vng_info *vgi, struct vng_owner *vgo)
837 {
838 vgo->vgo_vgi = vgi;
839 TAILQ_INSERT_HEAD(&vgi->vgi_owners, vgo, vgo_link);
840 }
841
842 static boolean_t
vgi_remove_vgo(struct vng_info * vgi,struct vng_owner * vgo)843 vgi_remove_vgo(struct vng_info *vgi, struct vng_owner *vgo)
844 {
845 TAILQ_REMOVE(&vgi->vgi_owners, vgo, vgo_link);
846 vgo->vgo_vgi = NULL;
847 return TAILQ_EMPTY(&vgi->vgi_owners);
848 }
849
850 static void
free_vgi(struct vng_info * vgi)851 free_vgi(struct vng_info *vgi)
852 {
853 assert(TAILQ_EMPTY(&vgi->vgi_owners));
854 #if DEVELOP || DEBUG
855 memset(vgi, 0xbeadfade, sizeof(*vgi));
856 #endif
857 kfree_type(struct vng_info, vgi);
858 }
859
860 static void
free_vgo(struct vng_owner * vgo)861 free_vgo(struct vng_owner *vgo)
862 {
863 #if DEVELOP || DEBUG
864 memset(vgo, 0x2bedf1d0, sizeof(*vgo));
865 #endif
866 kfree_type(struct vng_owner, vgo);
867 }
868
869 static int label_slot;
870 static LCK_GRP_DECLARE(llock_grp, VNG_POLICY_NAME);
871 static LCK_RW_DECLARE(llock, &llock_grp);
872
873 static __inline void *
vng_lbl_get(struct label * label)874 vng_lbl_get(struct label *label)
875 {
876 lck_rw_assert(&llock, LCK_RW_ASSERT_HELD);
877 void *data;
878 if (NULL == label) {
879 data = NULL;
880 } else {
881 data = (void *)mac_label_get(label, label_slot);
882 }
883 return data;
884 }
885
886 static __inline struct vng_info *
vng_lbl_get_withattr(struct label * label,unsigned attrmask)887 vng_lbl_get_withattr(struct label *label, unsigned attrmask)
888 {
889 struct vng_info *vgi = vng_lbl_get(label);
890 assert(NULL == vgi || (vgi->vgi_attrs & ~VNG_ALL) == 0);
891 if (NULL != vgi && 0 == (vgi->vgi_attrs & attrmask)) {
892 vgi = NULL;
893 }
894 return vgi;
895 }
896
897 static __inline void
vng_lbl_set(struct label * label,void * data)898 vng_lbl_set(struct label *label, void *data)
899 {
900 assert(NULL != label);
901 lck_rw_assert(&llock, LCK_RW_ASSERT_EXCLUSIVE);
902 mac_label_set(label, label_slot, (intptr_t)data);
903 }
904
905 static int
vnguard_sysc_getguardattr(proc_t p,struct vnguard_getattr * vga)906 vnguard_sysc_getguardattr(proc_t p, struct vnguard_getattr *vga)
907 {
908 const int fd = vga->vga_fd;
909
910 if (0 == vga->vga_guard) {
911 return EINVAL;
912 }
913
914 int error;
915 struct fileproc *fp;
916 if (0 != (error = fp_lookup(p, fd, &fp, 0))) {
917 return error;
918 }
919 do {
920 struct fileglob *fg = fp->fp_glob;
921 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
922 error = EBADF;
923 break;
924 }
925 struct vnode *vp = fg_get_data(fg);
926 if (!vnode_isreg(vp) || NULL == vp->v_mount) {
927 error = EBADF;
928 break;
929 }
930 error = vnode_getwithref(vp);
931 if (0 != error) {
932 break;
933 }
934
935 vga->vga_attrs = 0;
936
937 lck_rw_lock_shared(&llock);
938
939 if (NULL != mac_vnode_label(vp)) {
940 const struct vng_info *vgi = vng_lbl_get(mac_vnode_label(vp));
941 if (NULL != vgi) {
942 if (vgi->vgi_guard != vga->vga_guard) {
943 error = EPERM;
944 } else {
945 vga->vga_attrs = vgi->vgi_attrs;
946 }
947 }
948 }
949
950 lck_rw_unlock_shared(&llock);
951 vnode_put(vp);
952 } while (0);
953
954 fp_drop(p, fd, fp, 0);
955 return error;
956 }
957
958 static int
vnguard_sysc_setguard(proc_t p,const struct vnguard_set * vns)959 vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns)
960 {
961 const int fd = vns->vns_fd;
962
963 if ((vns->vns_attrs & ~VNG_ALL) != 0 ||
964 0 == vns->vns_attrs || 0 == vns->vns_guard) {
965 return EINVAL;
966 }
967
968 int error;
969 struct fileproc *fp;
970 if (0 != (error = fp_lookup(p, fd, &fp, 0))) {
971 return error;
972 }
973 do {
974 /*
975 * To avoid trivial DoS, insist that the caller
976 * has read/write access to the file.
977 */
978 if ((FREAD | FWRITE) != (fp->f_flag & (FREAD | FWRITE))) {
979 error = EBADF;
980 break;
981 }
982 struct fileglob *fg = fp->fp_glob;
983 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
984 error = EBADF;
985 break;
986 }
987 /*
988 * Confinement means there's only one fd pointing at
989 * this fileglob, and will always be associated with
990 * this pid.
991 */
992 if (0 == (FG_CONFINED & fg->fg_lflags)) {
993 error = EBADF;
994 break;
995 }
996 struct vnode *vp = fg_get_data(fg);
997 if (!vnode_isreg(vp) || NULL == vp->v_mount) {
998 error = EBADF;
999 break;
1000 }
1001 error = vnode_getwithref(vp);
1002 if (0 != error) {
1003 break;
1004 }
1005
1006 /* Ensure the target vnode -has- a label */
1007 struct vfs_context *ctx = vfs_context_current();
1008 mac_vnode_label_update(ctx, vp, NULL);
1009
1010 struct vng_info *nvgi = new_vgi(vns->vns_attrs, vns->vns_guard);
1011 struct vng_owner *nvgo = new_vgo(p);
1012
1013 lck_rw_lock_exclusive(&llock);
1014
1015 do {
1016 /*
1017 * A vnode guard is associated with one or more
1018 * fileglobs in one or more processes.
1019 */
1020 struct vng_info *vgi = vng_lbl_get(mac_vnode_label(vp));
1021 struct vng_owner *vgo = fg->fg_vgo;
1022
1023 if (NULL == vgi) {
1024 /* vnode unguarded, add the first guard */
1025 if (NULL != vgo) {
1026 panic("vnguard label on fileglob "
1027 "but not vnode");
1028 }
1029 /* add a kusecount so we can unlabel later */
1030 error = vnode_ref_ext(vp, O_EVTONLY, 0);
1031 if (0 == error) {
1032 /* add the guard */
1033 vgi_add_vgo(nvgi, nvgo);
1034 vng_lbl_set(mac_vnode_label(vp), nvgi);
1035 fg->fg_vgo = nvgo;
1036 } else {
1037 free_vgo(nvgo);
1038 free_vgi(nvgi);
1039 }
1040 } else {
1041 /* vnode already guarded */
1042 free_vgi(nvgi);
1043 if (vgi->vgi_guard != vns->vns_guard) {
1044 error = EPERM; /* guard mismatch */
1045 } else if (vgi->vgi_attrs != vns->vns_attrs) {
1046 /*
1047 * Temporary workaround for older versions of SQLite:
1048 * allow newer guard attributes to be silently cleared.
1049 */
1050 const unsigned mask = ~(VNG_WRITE_OTHER | VNG_TRUNC_OTHER);
1051 if ((vgi->vgi_attrs & mask) == (vns->vns_attrs & mask)) {
1052 vgi->vgi_attrs &= vns->vns_attrs;
1053 } else {
1054 error = EACCES; /* attr mismatch */
1055 }
1056 }
1057 if (0 != error || NULL != vgo) {
1058 free_vgo(nvgo);
1059 break;
1060 }
1061 /* record shared ownership */
1062 vgi_add_vgo(vgi, nvgo);
1063 fg->fg_vgo = nvgo;
1064 }
1065 } while (0);
1066
1067 lck_rw_unlock_exclusive(&llock);
1068 vnode_put(vp);
1069 } while (0);
1070
1071 fp_drop(p, fd, fp, 0);
1072 return error;
1073 }
1074
1075 static int
vng_policy_syscall(proc_t p,int cmd,user_addr_t arg)1076 vng_policy_syscall(proc_t p, int cmd, user_addr_t arg)
1077 {
1078 int error = EINVAL;
1079
1080 switch (cmd) {
1081 case VNG_SYSC_PING:
1082 if (0 == arg) {
1083 error = 0;
1084 }
1085 break;
1086 case VNG_SYSC_SET_GUARD: {
1087 struct vnguard_set vns;
1088 error = copyin(arg, (void *)&vns, sizeof(vns));
1089 if (error) {
1090 break;
1091 }
1092 error = vnguard_sysc_setguard(p, &vns);
1093 break;
1094 }
1095 case VNG_SYSC_GET_ATTR: {
1096 struct vnguard_getattr vga;
1097 error = copyin(arg, (void *)&vga, sizeof(vga));
1098 if (error) {
1099 break;
1100 }
1101 error = vnguard_sysc_getguardattr(p, &vga);
1102 if (error) {
1103 break;
1104 }
1105 error = copyout((void *)&vga, arg, sizeof(vga));
1106 break;
1107 }
1108 default:
1109 break;
1110 }
1111 return error;
1112 }
1113
1114 /*
1115 * This is called just before the fileglob disappears in fg_free().
1116 * Take the exclusive lock: no other thread can add or remove
1117 * a vng_info to any vnode in the system.
1118 */
1119 void
vng_file_label_destroy(struct fileglob * fg)1120 vng_file_label_destroy(struct fileglob *fg)
1121 {
1122 struct vng_owner *lvgo = fg->fg_vgo;
1123 struct vng_info *vgi = NULL;
1124
1125 if (lvgo) {
1126 lck_rw_lock_exclusive(&llock);
1127 fg->fg_vgo = NULL;
1128 vgi = lvgo->vgo_vgi;
1129 assert(vgi);
1130 if (vgi_remove_vgo(vgi, lvgo)) {
1131 /* that was the last reference */
1132 vgi->vgi_attrs = 0;
1133 if (DTYPE_VNODE == FILEGLOB_DTYPE(fg)) {
1134 struct vnode *vp = fg_get_data(fg);
1135 int error = vnode_getwithref(vp);
1136 if (0 == error) {
1137 vng_lbl_set(mac_vnode_label(vp), 0);
1138 lck_rw_unlock_exclusive(&llock);
1139 /* may trigger VNOP_INACTIVE */
1140 vnode_rele_ext(vp, O_EVTONLY, 0);
1141 vnode_put(vp);
1142 free_vgi(vgi);
1143 free_vgo(lvgo);
1144 return;
1145 }
1146 }
1147 }
1148 lck_rw_unlock_exclusive(&llock);
1149 free_vgo(lvgo);
1150 }
1151 }
1152
1153 static os_reason_t
vng_reason_from_pathname(const char * path,uint32_t pathlen)1154 vng_reason_from_pathname(const char *path, uint32_t pathlen)
1155 {
1156 os_reason_t r = os_reason_create(OS_REASON_GUARD, GUARD_REASON_VNODE);
1157 if (NULL == r) {
1158 return r;
1159 }
1160 /*
1161 * If the pathname is very long, just keep the trailing part
1162 */
1163 const uint32_t pathmax = 3 * EXIT_REASON_USER_DESC_MAX_LEN / 4;
1164 if (pathlen > pathmax) {
1165 path += (pathlen - pathmax);
1166 pathlen = pathmax;
1167 }
1168 uint32_t rsize = kcdata_estimate_required_buffer_size(1, pathlen);
1169 if (0 == os_reason_alloc_buffer(r, rsize)) {
1170 struct kcdata_descriptor *kcd = &r->osr_kcd_descriptor;
1171 mach_vm_address_t addr;
1172 if (kcdata_get_memory_addr(kcd,
1173 EXIT_REASON_USER_DESC, pathlen, &addr) == KERN_SUCCESS) {
1174 kcdata_memcpy(kcd, addr, path, pathlen);
1175 return r;
1176 }
1177 }
1178 os_reason_free(r);
1179 return OS_REASON_NULL;
1180 }
1181
1182 static int vng_policy_flags;
1183
1184 /*
1185 * Note: if an EXC_GUARD is generated, llock will be dropped and
1186 * subsequently reacquired by this routine. Data derived from
1187 * any label in the caller should be regenerated.
1188 */
1189 static int
vng_guard_violation(const struct vng_info * vgi,unsigned opval,vnode_t vp)1190 vng_guard_violation(const struct vng_info *vgi,
1191 unsigned opval, vnode_t vp)
1192 {
1193 int retval = 0;
1194
1195 if (vng_policy_flags & kVNG_POLICY_EPERM) {
1196 /* deny the operation */
1197 retval = EPERM;
1198 }
1199
1200 if (vng_policy_flags & (kVNG_POLICY_LOGMSG | kVNG_POLICY_UPRINTMSG)) {
1201 /* log a message */
1202 const char *op;
1203 switch (opval) {
1204 case VNG_RENAME_FROM:
1205 op = "rename-from";
1206 break;
1207 case VNG_RENAME_TO:
1208 op = "rename-to";
1209 break;
1210 case VNG_UNLINK:
1211 op = "unlink";
1212 break;
1213 case VNG_LINK:
1214 op = "link";
1215 break;
1216 case VNG_EXCHDATA:
1217 op = "exchdata";
1218 break;
1219 case VNG_WRITE_OTHER:
1220 op = "write";
1221 break;
1222 case VNG_TRUNC_OTHER:
1223 op = "truncate";
1224 break;
1225 default:
1226 op = "(unknown)";
1227 break;
1228 }
1229
1230 const char *nm = vnode_getname(vp);
1231 proc_t p = current_proc();
1232 const struct vng_owner *vgo;
1233 TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) {
1234 const char fmt[] =
1235 "%s[%d]: %s%s: '%s' guarded by %s[%d] (0x%llx)\n";
1236
1237 if (vng_policy_flags & kVNG_POLICY_LOGMSG) {
1238 printf(fmt,
1239 proc_name_address(p), proc_pid(p), op,
1240 0 != retval ? " denied" : "",
1241 NULL != nm ? nm : "(unknown)",
1242 proc_name_address(vgo->vgo_p),
1243 proc_pid(vgo->vgo_p), vgi->vgi_guard);
1244 }
1245 if (vng_policy_flags & kVNG_POLICY_UPRINTMSG) {
1246 uprintf(fmt,
1247 proc_name_address(p), proc_pid(p), op,
1248 0 != retval ? " denied" : "",
1249 NULL != nm ? nm : "(unknown)",
1250 proc_name_address(vgo->vgo_p),
1251 proc_pid(vgo->vgo_p), vgi->vgi_guard);
1252 }
1253 }
1254 if (NULL != nm) {
1255 vnode_putname(nm);
1256 }
1257 }
1258
1259 if (vng_policy_flags & (kVNG_POLICY_EXC | kVNG_POLICY_EXC_CORPSE)) {
1260 /* EXC_GUARD exception */
1261 const struct vng_owner *vgo = TAILQ_FIRST(&vgi->vgi_owners);
1262 pid_t pid = vgo ? proc_pid(vgo->vgo_p) : 0;
1263 mach_exception_code_t code;
1264 mach_exception_subcode_t subcode;
1265
1266 code = 0;
1267 EXC_GUARD_ENCODE_TYPE(code, GUARD_TYPE_VN);
1268 EXC_GUARD_ENCODE_FLAVOR(code, opval);
1269 EXC_GUARD_ENCODE_TARGET(code, pid);
1270 subcode = vgi->vgi_guard;
1271
1272 lck_rw_unlock_shared(&llock);
1273
1274 if (vng_policy_flags & kVNG_POLICY_EXC_CORPSE) {
1275 char *path;
1276 int len = MAXPATHLEN;
1277
1278 path = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_NOFAIL);
1279
1280 os_reason_t r = NULL;
1281 vn_getpath(vp, path, &len);
1282 if (*path && len) {
1283 r = vng_reason_from_pathname(path, len);
1284 }
1285 task_violated_guard(code, subcode, r, TRUE); /* not fatal */
1286 if (NULL != r) {
1287 os_reason_free(r);
1288 }
1289
1290 zfree(ZV_NAMEI, path);
1291 } else {
1292 thread_t t = current_thread();
1293 thread_guard_violation(t, code, subcode, TRUE);
1294 }
1295
1296 lck_rw_lock_shared(&llock);
1297 } else if (vng_policy_flags & kVNG_POLICY_SIGKILL) {
1298 proc_t p = current_proc();
1299 psignal(p, SIGKILL);
1300 }
1301
1302 return retval;
1303 }
1304
1305 /*
1306 * A fatal vnode guard was tripped on this thread.
1307 *
1308 * (Invoked before returning to userland from the syscall handler.)
1309 */
1310 void
vn_guard_ast(thread_t __unused t,mach_exception_data_type_t code,mach_exception_data_type_t subcode)1311 vn_guard_ast(thread_t __unused t,
1312 mach_exception_data_type_t code, mach_exception_data_type_t subcode)
1313 {
1314 const bool fatal = true;
1315 /*
1316 * Check if anyone has registered for Synchronous EXC_GUARD, if yes then,
1317 * deliver it synchronously and then kill the process, else kill the process
1318 * and deliver the exception via EXC_CORPSE_NOTIFY. Always kill the process if we are not in dev mode.
1319 */
1320 if (task_exception_notify(EXC_GUARD, code, subcode, fatal) == KERN_SUCCESS) {
1321 psignal(current_proc(), SIGKILL);
1322 } else {
1323 exit_with_guard_exception(current_proc(), code, subcode);
1324 }
1325 }
1326
1327 /*
1328 * vnode callbacks
1329 */
1330
1331 static int
vng_vnode_check_rename(kauth_cred_t __unused cred,struct vnode * __unused dvp,struct label * __unused dlabel,struct vnode * vp,struct label * label,struct componentname * __unused cnp,struct vnode * __unused tdvp,struct label * __unused tdlabel,struct vnode * tvp,struct label * tlabel,struct componentname * __unused tcnp)1332 vng_vnode_check_rename(kauth_cred_t __unused cred,
1333 struct vnode *__unused dvp, struct label *__unused dlabel,
1334 struct vnode *vp, struct label *label,
1335 struct componentname *__unused cnp,
1336 struct vnode *__unused tdvp, struct label *__unused tdlabel,
1337 struct vnode *tvp, struct label *tlabel,
1338 struct componentname *__unused tcnp)
1339 {
1340 int error = 0;
1341 if (NULL != label || NULL != tlabel) {
1342 lck_rw_lock_shared(&llock);
1343 const struct vng_info *vgi =
1344 vng_lbl_get_withattr(label, VNG_RENAME_FROM);
1345 if (NULL != vgi) {
1346 error = vng_guard_violation(vgi, VNG_RENAME_FROM, vp);
1347 }
1348 if (0 == error) {
1349 vgi = vng_lbl_get_withattr(tlabel, VNG_RENAME_TO);
1350 if (NULL != vgi) {
1351 error = vng_guard_violation(vgi,
1352 VNG_RENAME_TO, tvp);
1353 }
1354 }
1355 lck_rw_unlock_shared(&llock);
1356 }
1357 return error;
1358 }
1359
1360 static int
vng_vnode_check_link(kauth_cred_t __unused cred,struct vnode * __unused dvp,struct label * __unused dlabel,struct vnode * vp,struct label * label,struct componentname * __unused cnp)1361 vng_vnode_check_link(kauth_cred_t __unused cred,
1362 struct vnode *__unused dvp, struct label *__unused dlabel,
1363 struct vnode *vp, struct label *label, struct componentname *__unused cnp)
1364 {
1365 int error = 0;
1366 if (NULL != label) {
1367 lck_rw_lock_shared(&llock);
1368 const struct vng_info *vgi =
1369 vng_lbl_get_withattr(label, VNG_LINK);
1370 if (vgi) {
1371 error = vng_guard_violation(vgi, VNG_LINK, vp);
1372 }
1373 lck_rw_unlock_shared(&llock);
1374 }
1375 return error;
1376 }
1377
1378 static int
vng_vnode_check_unlink(kauth_cred_t __unused cred,struct vnode * __unused dvp,struct label * __unused dlabel,struct vnode * vp,struct label * label,struct componentname * __unused cnp)1379 vng_vnode_check_unlink(kauth_cred_t __unused cred,
1380 struct vnode *__unused dvp, struct label *__unused dlabel,
1381 struct vnode *vp, struct label *label, struct componentname *__unused cnp)
1382 {
1383 int error = 0;
1384 if (NULL != label) {
1385 lck_rw_lock_shared(&llock);
1386 const struct vng_info *vgi =
1387 vng_lbl_get_withattr(label, VNG_UNLINK);
1388 if (vgi) {
1389 error = vng_guard_violation(vgi, VNG_UNLINK, vp);
1390 }
1391 lck_rw_unlock_shared(&llock);
1392 }
1393 return error;
1394 }
1395
1396 /*
1397 * Only check violations for writes performed by "other processes"
1398 */
1399 static int
vng_vnode_check_write(kauth_cred_t __unused actv_cred,kauth_cred_t __unused file_cred,struct vnode * vp,struct label * label)1400 vng_vnode_check_write(kauth_cred_t __unused actv_cred,
1401 kauth_cred_t __unused file_cred, struct vnode *vp, struct label *label)
1402 {
1403 int error = 0;
1404 if (NULL != label) {
1405 lck_rw_lock_shared(&llock);
1406 const struct vng_info *vgi =
1407 vng_lbl_get_withattr(label, VNG_WRITE_OTHER);
1408 if (vgi) {
1409 proc_t p = current_proc();
1410 const struct vng_owner *vgo;
1411 TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) {
1412 if (vgo->vgo_p == p) {
1413 goto done;
1414 }
1415 }
1416 error = vng_guard_violation(vgi, VNG_WRITE_OTHER, vp);
1417 }
1418 done:
1419 lck_rw_unlock_shared(&llock);
1420 }
1421 return error;
1422 }
1423
1424 /*
1425 * Only check violations for truncates performed by "other processes"
1426 */
1427 static int
vng_vnode_check_truncate(kauth_cred_t __unused actv_cred,kauth_cred_t __unused file_cred,struct vnode * vp,struct label * label)1428 vng_vnode_check_truncate(kauth_cred_t __unused actv_cred,
1429 kauth_cred_t __unused file_cred, struct vnode *vp,
1430 struct label *label)
1431 {
1432 int error = 0;
1433 if (NULL != label) {
1434 lck_rw_lock_shared(&llock);
1435 const struct vng_info *vgi =
1436 vng_lbl_get_withattr(label, VNG_TRUNC_OTHER);
1437 if (vgi) {
1438 proc_t p = current_proc();
1439 const struct vng_owner *vgo;
1440 TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) {
1441 if (vgo->vgo_p == p) {
1442 goto done;
1443 }
1444 }
1445 error = vng_guard_violation(vgi, VNG_TRUNC_OTHER, vp);
1446 }
1447 done:
1448 lck_rw_unlock_shared(&llock);
1449 }
1450 return error;
1451 }
1452
1453 static int
vng_vnode_check_exchangedata(kauth_cred_t __unused cred,struct vnode * fvp,struct label * flabel,struct vnode * svp,struct label * slabel)1454 vng_vnode_check_exchangedata(kauth_cred_t __unused cred,
1455 struct vnode *fvp, struct label *flabel,
1456 struct vnode *svp, struct label *slabel)
1457 {
1458 int error = 0;
1459 if (NULL != flabel || NULL != slabel) {
1460 lck_rw_lock_shared(&llock);
1461 const struct vng_info *vgi =
1462 vng_lbl_get_withattr(flabel, VNG_EXCHDATA);
1463 if (NULL != vgi) {
1464 error = vng_guard_violation(vgi, VNG_EXCHDATA, fvp);
1465 }
1466 if (0 == error) {
1467 vgi = vng_lbl_get_withattr(slabel, VNG_EXCHDATA);
1468 if (NULL != vgi) {
1469 error = vng_guard_violation(vgi,
1470 VNG_EXCHDATA, svp);
1471 }
1472 }
1473 lck_rw_unlock_shared(&llock);
1474 }
1475 return error;
1476 }
1477
1478 /* Intercept open-time truncations (by "other") of a guarded vnode */
1479
1480 static int
vng_vnode_check_open(kauth_cred_t cred,struct vnode * vp,struct label * label,int acc_mode)1481 vng_vnode_check_open(kauth_cred_t cred,
1482 struct vnode *vp, struct label *label, int acc_mode)
1483 {
1484 if (0 == (acc_mode & O_TRUNC)) {
1485 return 0;
1486 }
1487 return vng_vnode_check_truncate(cred, NULL, vp, label);
1488 }
1489
1490 /*
1491 * Configuration gorp
1492 */
1493
1494 SECURITY_READ_ONLY_EARLY(static struct mac_policy_ops) vng_policy_ops = {
1495 .mpo_vnode_check_link = vng_vnode_check_link,
1496 .mpo_vnode_check_unlink = vng_vnode_check_unlink,
1497 .mpo_vnode_check_rename = vng_vnode_check_rename,
1498 .mpo_vnode_check_write = vng_vnode_check_write,
1499 .mpo_vnode_check_truncate = vng_vnode_check_truncate,
1500 .mpo_vnode_check_exchangedata = vng_vnode_check_exchangedata,
1501 .mpo_vnode_check_open = vng_vnode_check_open,
1502
1503 .mpo_policy_syscall = vng_policy_syscall,
1504 };
1505
1506 static const char *vng_labelnames[] = {
1507 "vnguard",
1508 };
1509
1510 #define ACOUNT(arr) ((unsigned)(sizeof (arr) / sizeof (arr[0])))
1511
1512 SECURITY_READ_ONLY_LATE(static struct mac_policy_conf) vng_policy_conf = {
1513 .mpc_name = VNG_POLICY_NAME,
1514 .mpc_fullname = "Guarded vnode policy",
1515 .mpc_field_off = &label_slot,
1516 .mpc_labelnames = vng_labelnames,
1517 .mpc_labelname_count = ACOUNT(vng_labelnames),
1518 .mpc_ops = &vng_policy_ops,
1519 .mpc_loadtime_flags = 0,
1520 .mpc_runtime_flags = 0
1521 };
1522
1523 SECURITY_READ_ONLY_LATE(static mac_policy_handle_t) vng_policy_handle;
1524
1525 void
vnguard_policy_init(void)1526 vnguard_policy_init(void)
1527 {
1528 if (0 == PE_i_can_has_debugger(NULL)) {
1529 return;
1530 }
1531 vng_policy_flags = kVNG_POLICY_LOGMSG |
1532 kVNG_POLICY_EXC_CORPSE | kVNG_POLICY_UPRINTMSG;
1533 PE_parse_boot_argn("vnguard", &vng_policy_flags, sizeof(vng_policy_flags));
1534 if (vng_policy_flags) {
1535 mac_policy_register(&vng_policy_conf, &vng_policy_handle, NULL);
1536 }
1537 }
1538
1539 #if DEBUG || DEVELOPMENT
1540 #include <sys/sysctl.h>
1541
1542 SYSCTL_DECL(_kern_vnguard);
1543 SYSCTL_NODE(_kern, OID_AUTO, vnguard, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "vnguard");
1544 SYSCTL_INT(_kern_vnguard, OID_AUTO, flags, CTLFLAG_RW | CTLFLAG_LOCKED,
1545 &vng_policy_flags, 0, "vnguard policy flags");
1546 #endif
1547
1548 #endif /* CONFIG_MACF && CONFIG_VNGUARD */
1549