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