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