xref: /xnu-8019.80.24/bsd/kern/kern_guarded.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
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