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