X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d9a64523371fa019c4575bb400cbbc3a50ac9903..HEAD:/bsd/kern/kern_guarded.c diff --git a/bsd/kern/kern_guarded.c b/bsd/kern/kern_guarded.c index 795eb5667..8fc2889fd 100644 --- a/bsd/kern/kern_guarded.c +++ b/bsd/kern/kern_guarded.c @@ -2,7 +2,7 @@ * Copyright (c) 2018 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ @@ -56,20 +56,18 @@ #include #endif - -#define f_flag f_fglob->fg_flag -#define f_type f_fglob->fg_ops->fo_type +#define f_flag fp_glob->fg_flag extern int dofilewrite(vfs_context_t ctx, struct fileproc *fp, - user_addr_t bufp, user_size_t nbyte, off_t offset, - int flags, user_ssize_t *retval ); -extern int wr_uio(struct proc *p, struct fileproc *fp, uio_t uio, user_ssize_t *retval); + user_addr_t bufp, user_size_t nbyte, off_t offset, + int flags, user_ssize_t *retval ); +extern int do_uiowrite(struct proc *p, struct fileproc *fp, uio_t uio, int flags, user_ssize_t *retval); /* * Experimental guarded file descriptor support. */ kern_return_t task_exception_notify(exception_type_t exception, - mach_exception_data_type_t code, mach_exception_data_type_t subcode); + mach_exception_data_type_t code, mach_exception_data_type_t subcode); kern_return_t task_violated_guard(mach_exception_code_t, mach_exception_subcode_t, void *); /* @@ -77,7 +75,7 @@ kern_return_t task_violated_guard(mach_exception_code_t, mach_exception_subcode_ * guarded_fileproc structs which implement guarded fds. The latter * struct (below) embeds the former. * - * The two types should be distinguished by the "type" portion of f_flags. + * The two types should be distinguished by the "type" portion of fp_flags. * There's also a magic number to help catch misuse and bugs. * * This is a bit unpleasant, but results from the desire to allow @@ -87,17 +85,25 @@ kern_return_t task_violated_guard(mach_exception_code_t, mach_exception_subcode_ struct guarded_fileproc { struct fileproc gf_fileproc; - u_int gf_magic; - u_int gf_attrs; - guardid_t gf_guard; + u_int gf_attrs; + guardid_t gf_guard; }; -const size_t sizeof_guarded_fileproc = sizeof (struct guarded_fileproc); +ZONE_DECLARE(gfp_zone, "guarded_fileproc", + sizeof(struct guarded_fileproc), + ZC_NOENCRYPT | ZC_ZFREE_CLEARMEM); -#define FP_TO_GFP(fp) ((struct guarded_fileproc *)(fp)) -#define GFP_TO_FP(gfp) (&(gfp)->gf_fileproc) +static inline struct guarded_fileproc * +FP_TO_GFP(struct fileproc *fp) +{ + struct guarded_fileproc *gfp = + __container_of(fp, struct guarded_fileproc, gf_fileproc); -#define GUARDED_FILEPROC_MAGIC 0x29083 + zone_require(gfp_zone, gfp); + return gfp; +} + +#define GFP_TO_FP(gfp) (&(gfp)->gf_fileproc) struct gfp_crarg { guardid_t gca_guard; @@ -110,28 +116,23 @@ guarded_fileproc_alloc_init(void *crarg) struct gfp_crarg *aarg = crarg; struct guarded_fileproc *gfp; - if ((gfp = kalloc(sizeof (*gfp))) == NULL) - return (NULL); + gfp = zalloc_flags(gfp_zone, Z_WAITOK | Z_ZERO); + + struct fileproc *fp = &gfp->gf_fileproc; + os_ref_init(&fp->fp_iocount, &f_refgrp); + fp->fp_flags = FTYPE_GUARDED; - bzero(gfp, sizeof (*gfp)); - gfp->gf_fileproc.f_flags = FTYPE_GUARDED; - gfp->gf_magic = GUARDED_FILEPROC_MAGIC; gfp->gf_guard = aarg->gca_guard; gfp->gf_attrs = aarg->gca_attrs; - return (GFP_TO_FP(gfp)); + return GFP_TO_FP(gfp); } void guarded_fileproc_free(struct fileproc *fp) { struct guarded_fileproc *gfp = FP_TO_GFP(fp); - - if (FILEPROC_TYPE(fp) != FTYPE_GUARDED || - GUARDED_FILEPROC_MAGIC != gfp->gf_magic) - panic("%s: corrupt fp %p flags %x", __func__, fp, fp->f_flags); - - kfree(gfp, sizeof (*gfp)); + zfree(gfp_zone, gfp); } static int @@ -141,48 +142,44 @@ fp_lookup_guarded(proc_t p, int fd, guardid_t guard, struct fileproc *fp; int error; - if ((error = fp_lookup(p, fd, &fp, locked)) != 0) - return (error); + if ((error = fp_lookup(p, fd, &fp, locked)) != 0) { + return error; + } if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) { (void) fp_drop(p, fd, fp, locked); - return (EINVAL); + return EINVAL; } struct guarded_fileproc *gfp = FP_TO_GFP(fp); - if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) - panic("%s: corrupt fp %p", __func__, fp); - if (guard != gfp->gf_guard) { (void) fp_drop(p, fd, fp, locked); - return (EPERM); /* *not* a mismatch exception */ + return EPERM; /* *not* a mismatch exception */ } - if (gfpp) + if (gfpp) { *gfpp = gfp; - return (0); + } + return 0; } /* * Expected use pattern: * - * if (FP_ISGUARDED(fp, GUARD_CLOSE)) { - * error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE); + * if (fp_isguarded(fp, GUARD_CLOSE)) { + * error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE); * proc_fdunlock(p); - * return (error); + * return error; * } + * + * Passing `0` to `attrs` returns whether the fp is guarded at all. */ int fp_isguarded(struct fileproc *fp, u_int attrs) { if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) { - struct guarded_fileproc *gfp = FP_TO_GFP(fp); - - if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) - panic("%s: corrupt gfp %p flags %x", - __func__, gfp, fp->f_flags); - return ((attrs & gfp->gf_attrs) == attrs); + return (attrs & FP_TO_GFP(fp)->gf_attrs) == attrs; } - return (0); + return 0; } extern char *proc_name_address(void *p); @@ -190,8 +187,9 @@ extern char *proc_name_address(void *p); int fp_guard_exception(proc_t p, int fd, struct fileproc *fp, u_int flavor) { - if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) - panic("%s corrupt fp %p flags %x", __func__, fp, fp->f_flags); + if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) { + panic("%s corrupt fp %p flags %x", __func__, fp, fp->fp_flags); + } struct guarded_fileproc *gfp = FP_TO_GFP(fp); /* all gfd fields protected via proc_fdlock() */ @@ -204,8 +202,8 @@ fp_guard_exception(proc_t p, int fd, struct fileproc *fp, u_int flavor) mach_exception_subcode_t subcode = gfp->gf_guard; thread_t t = current_thread(); - thread_guard_violation(t, code, subcode); - return (EPERM); + thread_guard_violation(t, code, subcode, TRUE); + return EPERM; } /* @@ -251,16 +249,18 @@ fd_guard_ast( int guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval) { - if ((uap->flags & O_CLOEXEC) == 0) - return (EINVAL); + if ((uap->flags & O_CLOEXEC) == 0) { + return EINVAL; + } #define GUARD_REQUIRED (GUARD_DUP) -#define GUARD_ALL (GUARD_REQUIRED | \ - (GUARD_CLOSE | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE)) +#define GUARD_ALL (GUARD_REQUIRED | \ + (GUARD_CLOSE | GUARD_SOCKET_IPC | GUARD_FILEPORT | GUARD_WRITE)) if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) || - ((uap->guardflags & ~GUARD_ALL) != 0)) - return (EINVAL); + ((uap->guardflags & ~GUARD_ALL) != 0)) { + return EINVAL; + } int error; struct gfp_crarg crarg = { @@ -268,14 +268,16 @@ guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval) }; if ((error = copyin(uap->guard, - &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0) - return (error); + &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) { + return error; + } /* * Disallow certain guard values -- is zero enough? */ - if (crarg.gca_guard == 0) - return (EINVAL); + if (crarg.gca_guard == 0) { + return EINVAL; + } struct filedesc *fdp = p->p_fd; struct vnode_attr va; @@ -288,10 +290,10 @@ guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval) VATTR_SET(&va, va_mode, cmode & ACCESSPERMS); NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE, - uap->path, ctx); + uap->path, ctx); - return (open1(ctx, &nd, uap->flags | O_CLOFORK, &va, - guarded_fileproc_alloc_init, &crarg, retval)); + return open1(ctx, &nd, uap->flags | O_CLOFORK, &va, + guarded_fileproc_alloc_init, &crarg, retval); } /* @@ -305,12 +307,14 @@ guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval) int guarded_open_dprotected_np(proc_t p, struct guarded_open_dprotected_np_args *uap, int32_t *retval) { - if ((uap->flags & O_CLOEXEC) == 0) - return (EINVAL); + if ((uap->flags & O_CLOEXEC) == 0) { + return EINVAL; + } if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) || - ((uap->guardflags & ~GUARD_ALL) != 0)) - return (EINVAL); + ((uap->guardflags & ~GUARD_ALL) != 0)) { + return EINVAL; + } int error; struct gfp_crarg crarg = { @@ -318,14 +322,16 @@ guarded_open_dprotected_np(proc_t p, struct guarded_open_dprotected_np_args *uap }; if ((error = copyin(uap->guard, - &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0) - return (error); + &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) { + return error; + } /* * Disallow certain guard values -- is zero enough? */ - if (crarg.gca_guard == 0) - return (EINVAL); + if (crarg.gca_guard == 0) { + return EINVAL; + } struct filedesc *fdp = p->p_fd; struct vnode_attr va; @@ -338,33 +344,33 @@ guarded_open_dprotected_np(proc_t p, struct guarded_open_dprotected_np_args *uap VATTR_SET(&va, va_mode, cmode & ACCESSPERMS); NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE, - uap->path, ctx); + uap->path, ctx); - /* - * Initialize the extra fields in vnode_attr to pass down dataprotection + /* + * Initialize the extra fields in vnode_attr to pass down dataprotection * extra fields. * 1. target cprotect class. - * 2. set a flag to mark it as requiring open-raw-encrypted semantics. - */ - if (uap->flags & O_CREAT) { + * 2. set a flag to mark it as requiring open-raw-encrypted semantics. + */ + if (uap->flags & O_CREAT) { VATTR_SET(&va, va_dataprotect_class, uap->dpclass); } - - if (uap->dpflags & (O_DP_GETRAWENCRYPTED|O_DP_GETRAWUNENCRYPTED)) { - if ( uap->flags & (O_RDWR | O_WRONLY)) { + + if (uap->dpflags & (O_DP_GETRAWENCRYPTED | O_DP_GETRAWUNENCRYPTED)) { + if (uap->flags & (O_RDWR | O_WRONLY)) { /* Not allowed to write raw encrypted bytes */ - return EINVAL; - } + return EINVAL; + } if (uap->dpflags & O_DP_GETRAWENCRYPTED) { - VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED); + VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED); } if (uap->dpflags & O_DP_GETRAWUNENCRYPTED) { - VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWUNENCRYPTED); + VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWUNENCRYPTED); } } - return (open1(ctx, &nd, uap->flags | O_CLOFORK, &va, - guarded_fileproc_alloc_init, &crarg, retval)); + return open1(ctx, &nd, uap->flags | O_CLOFORK, &va, + guarded_fileproc_alloc_init, &crarg, retval); } /* @@ -380,8 +386,9 @@ int guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval) { if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) || - ((uap->guardflags & ~GUARD_ALL) != 0)) - return (EINVAL); + ((uap->guardflags & ~GUARD_ALL) != 0)) { + return EINVAL; + } int error; struct gfp_crarg crarg = { @@ -389,13 +396,15 @@ guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval) }; if ((error = copyin(uap->guard, - &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0) - return (error); + &(crarg.gca_guard), sizeof(crarg.gca_guard))) != 0) { + return error; + } - if (crarg.gca_guard == 0) - return (EINVAL); + if (crarg.gca_guard == 0) { + return EINVAL; + } - return (kqueue_body(p, guarded_fileproc_alloc_init, &crarg, retval)); + return kqueue_internal(p, guarded_fileproc_alloc_init, &crarg, retval); } /* @@ -412,17 +421,17 @@ guarded_close_np(proc_t p, struct guarded_close_np_args *uap, AUDIT_SYSCLOSE(p, fd); - if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0) - return (error); + if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) { + return error; + } proc_fdlock(p); if ((error = fp_lookup_guarded(p, fd, uguard, &gfp, 1)) != 0) { proc_fdunlock(p); - return (error); + return error; } - error = close_internal_locked(p, fd, GFP_TO_FP(gfp), 0); - proc_fdunlock(p); - return (error); + fp_drop(p, fd, GFP_TO_FP(gfp), 1); + return fp_close_and_unlock(p, fd, GFP_TO_FP(gfp), 0); } /* @@ -451,7 +460,7 @@ guarded_close_np(proc_t p, struct guarded_close_np_args *uap, * If 'nguard' is NULL, fd must be guarded at entry, * must match with what's already guarding the descriptor, and the * result will be to completely remove the guard. Note also that the - * fdflags are copied to the descriptor from the incoming *fdflagsp argument. + * fdflags are copied to the descriptor from the incoming *fdflagsp argument. * * If the descriptor is guarded, and neither 'guard' nor 'nguard' is NULL * and matches what's already guarding the descriptor, @@ -494,30 +503,30 @@ change_fdguard_np(proc_t p, struct change_fdguard_np_args *uap, int nfdflags = 0; if (0 != uap->guard && - 0 != (error = copyin(uap->guard, &oldg, sizeof (oldg)))) - return (error); /* can't copyin current guard */ - + 0 != (error = copyin(uap->guard, &oldg, sizeof(oldg)))) { + return error; /* can't copyin current guard */ + } if (0 != uap->nguard && - 0 != (error = copyin(uap->nguard, &newg, sizeof (newg)))) - return (error); /* can't copyin new guard */ - + 0 != (error = copyin(uap->nguard, &newg, sizeof(newg)))) { + return error; /* can't copyin new guard */ + } if (0 != uap->fdflagsp && - 0 != (error = copyin(uap->fdflagsp, &nfdflags, sizeof (nfdflags)))) - return (error); /* can't copyin new fdflags */ - + 0 != (error = copyin(uap->fdflagsp, &nfdflags, sizeof(nfdflags)))) { + return error; /* can't copyin new fdflags */ + } proc_fdlock(p); restart: if ((error = fp_lookup(p, fd, &fp, 1)) != 0) { proc_fdunlock(p); - return (error); + return error; } if (0 != uap->fdflagsp) { int ofdflags = FDFLAGS_GET(p, fd); int ofl = ((ofdflags & UF_EXCLOSE) ? FD_CLOEXEC : 0) | - ((ofdflags & UF_FORKCLOSE) ? FD_CLOFORK : 0); + ((ofdflags & UF_FORKCLOSE) ? FD_CLOFORK : 0); proc_fdunlock(p); - if (0 != (error = copyout(&ofl, uap->fdflagsp, sizeof (ofl)))) { + if (0 != (error = copyout(&ofl, uap->fdflagsp, sizeof(ofl)))) { proc_fdlock(p); goto dropout; /* can't copyout old fdflags */ } @@ -525,29 +534,34 @@ restart: } if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) { - if (0 == uap->guard || 0 == uap->guardflags) + if (0 == uap->guard || 0 == uap->guardflags) { error = EINVAL; /* missing guard! */ - else if (0 == oldg) + } else if (0 == oldg) { error = EPERM; /* guardids cannot be zero */ + } } else { - if (0 != uap->guard || 0 != uap->guardflags) + if (0 != uap->guard || 0 != uap->guardflags) { error = EINVAL; /* guard provided, but none needed! */ + } } - if (0 != error) + if (0 != error) { goto dropout; + } if (0 != uap->nguard) { /* * There's a new guard in town. */ - if (0 == newg) + if (0 == newg) { error = EINVAL; /* guards cannot contain zero */ - else if (((uap->nguardflags & GUARD_REQUIRED) != GUARD_REQUIRED) || - ((uap->nguardflags & ~GUARD_ALL) != 0)) + } else if (((uap->nguardflags & GUARD_REQUIRED) != GUARD_REQUIRED) || + ((uap->nguardflags & ~GUARD_ALL) != 0)) { error = EINVAL; /* must have valid attributes too */ - if (0 != error) + } + if (0 != error) { goto dropout; + } if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) { /* @@ -555,10 +569,6 @@ restart: */ struct guarded_fileproc *gfp = FP_TO_GFP(fp); - if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) - panic("%s: corrupt gfp %p flags %x", - __func__, gfp, fp->f_flags); - if (oldg == gfp->gf_guard && uap->guardflags == gfp->gf_attrs) { /* @@ -567,12 +577,14 @@ restart: * fdflags "side-effects" as we go. Note that * userland can request FD_CLOFORK semantics. */ - if (gfp->gf_attrs & GUARD_CLOSE) + if (gfp->gf_attrs & GUARD_CLOSE) { FDFLAGS_CLR(p, fd, UF_FORKCLOSE); + } gfp->gf_guard = newg; gfp->gf_attrs = uap->nguardflags; - if (gfp->gf_attrs & GUARD_CLOSE) + if (gfp->gf_attrs & GUARD_CLOSE) { FDFLAGS_SET(p, fd, UF_FORKCLOSE); + } FDFLAGS_SET(p, fd, (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0); /* FG_CONFINED enforced regardless */ @@ -584,7 +596,7 @@ restart: /* * Add a guard to a previously unguarded descriptor */ - switch (FILEGLOB_DTYPE(fp->f_fglob)) { + switch (FILEGLOB_DTYPE(fp->fp_glob)) { case DTYPE_VNODE: case DTYPE_PIPE: case DTYPE_SOCKET: @@ -603,21 +615,22 @@ restart: .gca_attrs = uap->nguardflags }; struct fileproc *nfp = - guarded_fileproc_alloc_init(&crarg); + guarded_fileproc_alloc_init(&crarg); struct guarded_fileproc *gfp; proc_fdlock(p); switch (error = fp_tryswap(p, fd, nfp)) { - case 0: /* guarded-ness comes with side-effects */ + case 0: /* success; guarded-ness comes with side-effects */ + fp = NULL; gfp = FP_TO_GFP(nfp); - if (gfp->gf_attrs & GUARD_CLOSE) + if (gfp->gf_attrs & GUARD_CLOSE) { FDFLAGS_SET(p, fd, UF_FORKCLOSE); + } FDFLAGS_SET(p, fd, UF_EXCLOSE); (void) fp_drop(p, fd, nfp, 1); - fileproc_free(fp); break; - case EKEEPLOOKING: /* f_iocount indicates a collision */ + case EKEEPLOOKING: /* fp_iocount indicates a collision */ (void) fp_drop(p, fd, fp, 1); fileproc_free(nfp); goto restart; @@ -627,7 +640,7 @@ restart: break; } proc_fdunlock(p); - return (error); + return error; } } else { /* @@ -644,10 +657,6 @@ restart: goto dropout; } - if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) - panic("%s: corrupt gfp %p flags %x", - __func__, gfp, fp->f_flags); - if (oldg != gfp->gf_guard || uap->guardflags != gfp->gf_attrs) { error = EPERM; @@ -659,7 +668,8 @@ restart: proc_fdlock(p); switch (error = fp_tryswap(p, fd, nfp)) { - case 0: /* undo side-effects of guarded-ness */ + case 0: /* success; undo side-effects of guarded-ness */ + fp = NULL; FDFLAGS_CLR(p, fd, UF_FORKCLOSE | UF_EXCLOSE); FDFLAGS_SET(p, fd, (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0); @@ -667,9 +677,8 @@ restart: FDFLAGS_SET(p, fd, (nfdflags & FD_CLOEXEC) ? UF_EXCLOSE : 0); (void) fp_drop(p, fd, nfp, 1); - fileproc_free(fp); break; - case EKEEPLOOKING: /* f_iocount indicates collision */ + case EKEEPLOOKING: /* fp_iocount indicates collision */ (void) fp_drop(p, fd, fp, 1); fileproc_free(nfp); goto restart; @@ -679,7 +688,7 @@ restart: break; } proc_fdunlock(p); - return (error); + return error; } else { /* * Not already guarded, and no new guard? @@ -691,7 +700,7 @@ restart: dropout: (void) fp_drop(p, fd, fp, 1); proc_fdunlock(p); - return (error); + return error; } /* @@ -703,39 +712,37 @@ dropout: int guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t *retval) { - int error; + int error; int fd = uap->fd; guardid_t uguard; struct fileproc *fp; struct guarded_fileproc *gfp; - bool wrote_some = false; AUDIT_ARG(fd, fd); - if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0) - return (error); + if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) { + return error; + } error = fp_lookup_guarded(p, fd, uguard, &gfp, 0); - if (error) - return(error); + if (error) { + return error; + } fp = GFP_TO_FP(gfp); if ((fp->f_flag & FWRITE) == 0) { error = EBADF; } else { - struct vfs_context context = *(vfs_context_current()); - context.vc_ucred = fp->f_fglob->fg_cred; + context.vc_ucred = fp->fp_glob->fg_cred; error = dofilewrite(&context, fp, uap->cbuf, uap->nbyte, - (off_t)-1, 0, retval); - wrote_some = *retval > 0; - } - if (wrote_some) - fp_drop_written(p, fd, fp); - else - fp_drop(p, fd, fp, 0); - return(error); + (off_t)-1, 0, retval); + } + + fp_drop(p, fd, fp, 0); + + return error; } /* @@ -744,42 +751,43 @@ guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t * * Initial implementation of guarded pwrites. */ - int - guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize_t *retval) - { +int +guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize_t *retval) +{ struct fileproc *fp; - int error; + int error; int fd = uap->fd; vnode_t vp = (vnode_t)0; guardid_t uguard; struct guarded_fileproc *gfp; - bool wrote_some = false; AUDIT_ARG(fd, fd); - if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0) - return (error); + if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) { + return error; + } error = fp_lookup_guarded(p, fd, uguard, &gfp, 0); - if (error) - return(error); + if (error) { + return error; + } fp = GFP_TO_FP(gfp); if ((fp->f_flag & FWRITE) == 0) { error = EBADF; } else { struct vfs_context context = *vfs_context_current(); - context.vc_ucred = fp->f_fglob->fg_cred; + context.vc_ucred = fp->fp_glob->fg_cred; - if (fp->f_type != DTYPE_VNODE) { + if (FILEGLOB_DTYPE(fp->fp_glob) != DTYPE_VNODE) { error = ESPIPE; goto errout; } - vp = (vnode_t)fp->f_fglob->fg_data; + vp = (vnode_t)fp->fp_glob->fg_data; if (vnode_isfifo(vp)) { error = ESPIPE; goto errout; - } + } if ((vp->v_flag & VISTTY)) { error = ENXIO; goto errout; @@ -790,19 +798,15 @@ guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t } error = dofilewrite(&context, fp, uap->buf, uap->nbyte, - uap->offset, FOF_OFFSET, retval); - wrote_some = *retval > 0; + uap->offset, FOF_OFFSET, retval); } errout: - if (wrote_some) - fp_drop_written(p, fd, fp); - else - fp_drop(p, fd, fp, 0); + fp_drop(p, fd, fp, 0); KERNEL_DEBUG_CONSTANT((BSDDBG_CODE(DBG_BSD_SC_EXTENDED_INFO, SYS_guarded_pwrite_np) | DBG_FUNC_NONE), - uap->fd, uap->nbyte, (unsigned int)((uap->offset >> 32)), (unsigned int)(uap->offset), 0); - - return(error); + uap->fd, uap->nbyte, (unsigned int)((uap->offset >> 32)), (unsigned int)(uap->offset), 0); + + return error; } /* @@ -821,19 +825,19 @@ guarded_writev_np(struct proc *p, struct guarded_writev_np_args *uap, user_ssize struct user_iovec *iovp; guardid_t uguard; struct guarded_fileproc *gfp; - bool wrote_some = false; AUDIT_ARG(fd, uap->fd); /* Verify range bedfore calling uio_create() */ - if (uap->iovcnt <= 0 || uap->iovcnt > UIO_MAXIOV) - return (EINVAL); + if (uap->iovcnt <= 0 || uap->iovcnt > UIO_MAXIOV) { + return EINVAL; + } /* allocate a uio large enough to hold the number of iovecs passed */ auio = uio_create(uap->iovcnt, 0, - (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), - UIO_WRITE); - + (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), + UIO_WRITE); + /* get location of iovecs within the uio. then copyin the iovecs from * user space. */ @@ -843,43 +847,41 @@ guarded_writev_np(struct proc *p, struct guarded_writev_np_args *uap, user_ssize goto ExitThisRoutine; } error = copyin_user_iovec_array(uap->iovp, - IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32, - uap->iovcnt, iovp); + IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32, + uap->iovcnt, iovp); if (error) { goto ExitThisRoutine; } - - /* finalize uio_t for use and do the IO + + /* finalize uio_t for use and do the IO */ error = uio_calculateresid(auio); if (error) { goto ExitThisRoutine; } - if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0) + if ((error = copyin(uap->guard, &uguard, sizeof(uguard))) != 0) { goto ExitThisRoutine; + } error = fp_lookup_guarded(p, uap->fd, uguard, &gfp, 0); - if (error) + if (error) { goto ExitThisRoutine; + } fp = GFP_TO_FP(gfp); if ((fp->f_flag & FWRITE) == 0) { error = EBADF; } else { - error = wr_uio(p, fp, auio, retval); - wrote_some = *retval > 0; - } - - if (wrote_some) - fp_drop_written(p, uap->fd, fp); - else - fp_drop(p, uap->fd, fp, 0); + error = do_uiowrite(p, fp, auio, 0, retval); + } + + fp_drop(p, uap->fd, fp, 0); ExitThisRoutine: if (auio != NULL) { uio_free(auio); } - return (error); + return error; } /* @@ -896,15 +898,16 @@ falloc_guarded(struct proc *p, struct fileproc **fp, int *fd, struct gfp_crarg crarg; if (((attrs & GUARD_REQUIRED) != GUARD_REQUIRED) || - ((attrs & ~GUARD_ALL) != 0) || (*guard == 0)) - return (EINVAL); + ((attrs & ~GUARD_ALL) != 0) || (*guard == 0)) { + return EINVAL; + } - bzero(&crarg, sizeof (crarg)); + bzero(&crarg, sizeof(crarg)); crarg.gca_guard = *guard; crarg.gca_attrs = attrs; - return (falloc_withalloc(p, fp, fd, ctx, guarded_fileproc_alloc_init, - &crarg)); + return falloc_withalloc(p, fp, fd, ctx, guarded_fileproc_alloc_init, + &crarg); } #if CONFIG_MACF && CONFIG_VNGUARD @@ -951,7 +954,7 @@ struct vng_owner { /* lives on the fileglob label */ static struct vng_info * new_vgi(unsigned attrs, guardid_t guard) { - struct vng_info *vgi = kalloc(sizeof (*vgi)); + struct vng_info *vgi = kalloc(sizeof(*vgi)); vgi->vgi_guard = guard; vgi->vgi_attrs = attrs; TAILQ_INIT(&vgi->vgi_owners); @@ -961,8 +964,8 @@ new_vgi(unsigned attrs, guardid_t guard) static struct vng_owner * new_vgo(proc_t p, struct fileglob *fg) { - struct vng_owner *vgo = kalloc(sizeof (*vgo)); - memset(vgo, 0, sizeof (*vgo)); + struct vng_owner *vgo = kalloc(sizeof(*vgo)); + memset(vgo, 0, sizeof(*vgo)); vgo->vgo_p = p; vgo->vgo_fg = fg; return vgo; @@ -988,33 +991,34 @@ free_vgi(struct vng_info *vgi) { assert(TAILQ_EMPTY(&vgi->vgi_owners)); #if DEVELOP || DEBUG - memset(vgi, 0xbeadfade, sizeof (*vgi)); + memset(vgi, 0xbeadfade, sizeof(*vgi)); #endif - kfree(vgi, sizeof (*vgi)); + kfree(vgi, sizeof(*vgi)); } static void free_vgo(struct vng_owner *vgo) { #if DEVELOP || DEBUG - memset(vgo, 0x2bedf1d0, sizeof (*vgo)); + memset(vgo, 0x2bedf1d0, sizeof(*vgo)); #endif - kfree(vgo, sizeof (*vgo)); + kfree(vgo, sizeof(*vgo)); } static int label_slot; -static lck_rw_t llock; -static lck_grp_t *llock_grp; +static LCK_GRP_DECLARE(llock_grp, VNG_POLICY_NAME); +static LCK_RW_DECLARE(llock, &llock_grp); static __inline void * vng_lbl_get(struct label *label) { lck_rw_assert(&llock, LCK_RW_ASSERT_HELD); void *data; - if (NULL == label) + if (NULL == label) { data = NULL; - else + } else { data = (void *)mac_label_get(label, label_slot); + } return data; } @@ -1023,8 +1027,9 @@ vng_lbl_get_withattr(struct label *label, unsigned attrmask) { struct vng_info *vgi = vng_lbl_get(label); assert(NULL == vgi || (vgi->vgi_attrs & ~VNG_ALL) == 0); - if (NULL != vgi && 0 == (vgi->vgi_attrs & attrmask)) + if (NULL != vgi && 0 == (vgi->vgi_attrs & attrmask)) { vgi = NULL; + } return vgi; } @@ -1036,29 +1041,84 @@ vng_lbl_set(struct label *label, void *data) mac_label_set(label, label_slot, (intptr_t)data); } +static int +vnguard_sysc_getguardattr(proc_t p, struct vnguard_getattr *vga) +{ + const int fd = vga->vga_fd; + + if (0 == vga->vga_guard) { + return EINVAL; + } + + int error; + struct fileproc *fp; + if (0 != (error = fp_lookup(p, fd, &fp, 0))) { + return error; + } + do { + struct fileglob *fg = fp->fp_glob; + if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) { + error = EBADF; + break; + } + struct vnode *vp = fg->fg_data; + if (!vnode_isreg(vp) || NULL == vp->v_mount) { + error = EBADF; + break; + } + error = vnode_getwithref(vp); + if (0 != error) { + break; + } + + vga->vga_attrs = 0; + + lck_rw_lock_shared(&llock); + + if (NULL != vp->v_label) { + const struct vng_info *vgi = vng_lbl_get(vp->v_label); + if (NULL != vgi) { + if (vgi->vgi_guard != vga->vga_guard) { + error = EPERM; + } else { + vga->vga_attrs = vgi->vgi_attrs; + } + } + } + + lck_rw_unlock_shared(&llock); + vnode_put(vp); + } while (0); + + fp_drop(p, fd, fp, 0); + return error; +} + static int vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns) { const int fd = vns->vns_fd; if ((vns->vns_attrs & ~VNG_ALL) != 0 || - 0 == vns->vns_attrs || 0 == vns->vns_guard) + 0 == vns->vns_attrs || 0 == vns->vns_guard) { return EINVAL; + } int error; struct fileproc *fp; - if (0 != (error = fp_lookup(p, fd, &fp, 0))) + if (0 != (error = fp_lookup(p, fd, &fp, 0))) { return error; + } do { /* * To avoid trivial DoS, insist that the caller * has read/write access to the file. */ - if ((FREAD|FWRITE) != (fp->f_flag & (FREAD|FWRITE))) { + if ((FREAD | FWRITE) != (fp->f_flag & (FREAD | FWRITE))) { error = EBADF; break; } - struct fileglob *fg = fp->f_fglob; + struct fileglob *fg = fp->fp_glob; if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) { error = EBADF; break; @@ -1079,9 +1139,9 @@ vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns) } error = vnode_getwithref(vp); if (0 != error) { - fp_drop(p, fd, fp, 0); break; } + /* Ensure the target vnode -has- a label */ struct vfs_context *ctx = vfs_context_current(); mac_vnode_label_update(ctx, vp, NULL); @@ -1101,9 +1161,10 @@ vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns) if (NULL == vgi) { /* vnode unguarded, add the first guard */ - if (NULL != vgo) + if (NULL != vgo) { panic("vnguard label on fileglob " - "but not vnode"); + "but not vnode"); + } /* add a kusecount so we can unlabel later */ error = vnode_ref_ext(vp, O_EVTONLY, 0); if (0 == error) { @@ -1118,10 +1179,20 @@ vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns) } else { /* vnode already guarded */ free_vgi(nvgi); - if (vgi->vgi_guard != vns->vns_guard) + if (vgi->vgi_guard != vns->vns_guard) { error = EPERM; /* guard mismatch */ - else if (vgi->vgi_attrs != vns->vns_attrs) - error = EACCES; /* attr mismatch */ + } else if (vgi->vgi_attrs != vns->vns_attrs) { + /* + * Temporary workaround for older versions of SQLite: + * allow newer guard attributes to be silently cleared. + */ + const unsigned mask = ~(VNG_WRITE_OTHER | VNG_TRUNC_OTHER); + if ((vgi->vgi_attrs & mask) == (vns->vns_attrs & mask)) { + vgi->vgi_attrs &= vns->vns_attrs; + } else { + error = EACCES; /* attr mismatch */ + } + } if (0 != error || NULL != vgo) { free_vgo(nvgo); break; @@ -1147,21 +1218,36 @@ vng_policy_syscall(proc_t p, int cmd, user_addr_t arg) switch (cmd) { case VNG_SYSC_PING: - if (0 == arg) + if (0 == arg) { error = 0; + } break; case VNG_SYSC_SET_GUARD: { struct vnguard_set vns; - error = copyin(arg, (void *)&vns, sizeof (vns)); - if (error) + error = copyin(arg, (void *)&vns, sizeof(vns)); + if (error) { break; + } error = vnguard_sysc_setguard(p, &vns); break; } + case VNG_SYSC_GET_ATTR: { + struct vnguard_getattr vga; + error = copyin(arg, (void *)&vga, sizeof(vga)); + if (error) { + break; + } + error = vnguard_sysc_getguardattr(p, &vga); + if (error) { + break; + } + error = copyout((void *)&vga, arg, sizeof(vga)); + break; + } default: break; } - return (error); + return error; } /* @@ -1207,8 +1293,9 @@ static os_reason_t vng_reason_from_pathname(const char *path, uint32_t pathlen) { os_reason_t r = os_reason_create(OS_REASON_GUARD, GUARD_REASON_VNODE); - if (NULL == r) - return (r); + if (NULL == r) { + return r; + } /* * If the pathname is very long, just keep the trailing part */ @@ -1224,15 +1311,20 @@ vng_reason_from_pathname(const char *path, uint32_t pathlen) if (kcdata_get_memory_addr(kcd, EXIT_REASON_USER_DESC, pathlen, &addr) == KERN_SUCCESS) { kcdata_memcpy(kcd, addr, path, pathlen); - return (r); + return r; } } os_reason_free(r); - return (OS_REASON_NULL); + return OS_REASON_NULL; } static int vng_policy_flags; +/* + * Note: if an EXC_GUARD is generated, llock will be dropped and + * subsequently reacquired by this routine. Data derived from + * any label in the caller should be regenerated. + */ static int vng_guard_violation(const struct vng_info *vgi, unsigned opval, vnode_t vp) @@ -1244,7 +1336,7 @@ vng_guard_violation(const struct vng_info *vgi, retval = EPERM; } - if (vng_policy_flags & (kVNG_POLICY_LOGMSG|kVNG_POLICY_UPRINTMSG)) { + if (vng_policy_flags & (kVNG_POLICY_LOGMSG | kVNG_POLICY_UPRINTMSG)) { /* log a message */ const char *op; switch (opval) { @@ -1298,11 +1390,12 @@ vng_guard_violation(const struct vng_info *vgi, proc_pid(vgo->vgo_p), vgi->vgi_guard); } } - if (NULL != nm) + if (NULL != nm) { vnode_putname(nm); + } } - if (vng_policy_flags & (kVNG_POLICY_EXC|kVNG_POLICY_EXC_CORPSE)) { + if (vng_policy_flags & (kVNG_POLICY_EXC | kVNG_POLICY_EXC_CORPSE)) { /* EXC_GUARD exception */ const struct vng_owner *vgo = TAILQ_FIRST(&vgi->vgi_owners); pid_t pid = vgo ? proc_pid(vgo->vgo_p) : 0; @@ -1315,31 +1408,39 @@ vng_guard_violation(const struct vng_info *vgi, EXC_GUARD_ENCODE_TARGET(code, pid); subcode = vgi->vgi_guard; + lck_rw_unlock_shared(&llock); + if (vng_policy_flags & kVNG_POLICY_EXC_CORPSE) { char *path; int len = MAXPATHLEN; - MALLOC(path, char *, len, M_TEMP, M_WAITOK); + + path = zalloc(ZV_NAMEI); + os_reason_t r = NULL; if (NULL != path) { vn_getpath(vp, path, &len); - if (*path && len) + if (*path && len) { r = vng_reason_from_pathname(path, len); + } } task_violated_guard(code, subcode, r); /* not fatal */ - if (NULL != r) + if (NULL != r) { os_reason_free(r); - if (NULL != path) - FREE(path, M_TEMP); + } + + zfree(ZV_NAMEI, path); } else { thread_t t = current_thread(); - thread_guard_violation(t, code, subcode); + thread_guard_violation(t, code, subcode, TRUE); } + + lck_rw_lock_shared(&llock); } else if (vng_policy_flags & kVNG_POLICY_SIGKILL) { proc_t p = current_proc(); psignal(p, SIGKILL); } - return (retval); + return retval; } /* @@ -1374,17 +1475,19 @@ vng_vnode_check_rename(kauth_cred_t __unused cred, lck_rw_lock_shared(&llock); const struct vng_info *vgi = vng_lbl_get_withattr(label, VNG_RENAME_FROM); - if (NULL != vgi) + if (NULL != vgi) { error = vng_guard_violation(vgi, VNG_RENAME_FROM, vp); + } if (0 == error) { vgi = vng_lbl_get_withattr(tlabel, VNG_RENAME_TO); - if (NULL != vgi) + if (NULL != vgi) { error = vng_guard_violation(vgi, VNG_RENAME_TO, tvp); + } } lck_rw_unlock_shared(&llock); } - return (error); + return error; } static int @@ -1396,12 +1499,13 @@ vng_vnode_check_link(kauth_cred_t __unused cred, if (NULL != label) { lck_rw_lock_shared(&llock); const struct vng_info *vgi = - vng_lbl_get_withattr(label, VNG_LINK); - if (vgi) + vng_lbl_get_withattr(label, VNG_LINK); + if (vgi) { error = vng_guard_violation(vgi, VNG_LINK, vp); + } lck_rw_unlock_shared(&llock); } - return (error); + return error; } static int @@ -1414,11 +1518,12 @@ vng_vnode_check_unlink(kauth_cred_t __unused cred, lck_rw_lock_shared(&llock); const struct vng_info *vgi = vng_lbl_get_withattr(label, VNG_UNLINK); - if (vgi) + if (vgi) { error = vng_guard_violation(vgi, VNG_UNLINK, vp); + } lck_rw_unlock_shared(&llock); } - return (error); + return error; } /* @@ -1437,15 +1542,16 @@ vng_vnode_check_write(kauth_cred_t __unused actv_cred, proc_t p = current_proc(); const struct vng_owner *vgo; TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) { - if (vgo->vgo_p == p) + if (vgo->vgo_p == p) { goto done; + } } error = vng_guard_violation(vgi, VNG_WRITE_OTHER, vp); } - done: +done: lck_rw_unlock_shared(&llock); } - return (error); + return error; } /* @@ -1465,12 +1571,13 @@ vng_vnode_check_truncate(kauth_cred_t __unused actv_cred, proc_t p = current_proc(); const struct vng_owner *vgo; TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) { - if (vgo->vgo_p == p) + if (vgo->vgo_p == p) { goto done; + } } error = vng_guard_violation(vgi, VNG_TRUNC_OTHER, vp); } - done: +done: lck_rw_unlock_shared(&llock); } return error; @@ -1485,18 +1592,20 @@ vng_vnode_check_exchangedata(kauth_cred_t __unused cred, if (NULL != flabel || NULL != slabel) { lck_rw_lock_shared(&llock); const struct vng_info *vgi = - vng_lbl_get_withattr(flabel, VNG_EXCHDATA); - if (NULL != vgi) + vng_lbl_get_withattr(flabel, VNG_EXCHDATA); + if (NULL != vgi) { error = vng_guard_violation(vgi, VNG_EXCHDATA, fvp); + } if (0 == error) { vgi = vng_lbl_get_withattr(slabel, VNG_EXCHDATA); - if (NULL != vgi) + if (NULL != vgi) { error = vng_guard_violation(vgi, VNG_EXCHDATA, svp); + } } lck_rw_unlock_shared(&llock); } - return (error); + return error; } /* Intercept open-time truncations (by "other") of a guarded vnode */ @@ -1505,22 +1614,16 @@ static int vng_vnode_check_open(kauth_cred_t cred, struct vnode *vp, struct label *label, int acc_mode) { - if (0 == (acc_mode & O_TRUNC)) - return (0); - return (vng_vnode_check_truncate(cred, NULL, vp, label)); + if (0 == (acc_mode & O_TRUNC)) { + return 0; + } + return vng_vnode_check_truncate(cred, NULL, vp, label); } /* * Configuration gorp */ -static void -vng_init(struct mac_policy_conf *mpc) -{ - llock_grp = lck_grp_alloc_init(mpc->mpc_name, LCK_GRP_ATTR_NULL); - lck_rw_init(&llock, llock_grp, LCK_ATTR_NULL); -} - SECURITY_READ_ONLY_EARLY(static struct mac_policy_ops) vng_policy_ops = { .mpo_file_label_destroy = vng_file_label_destroy, @@ -1533,7 +1636,6 @@ SECURITY_READ_ONLY_EARLY(static struct mac_policy_ops) vng_policy_ops = { .mpo_vnode_check_open = vng_vnode_check_open, .mpo_policy_syscall = vng_policy_syscall, - .mpo_policy_init = vng_init, }; static const char *vng_labelnames[] = { @@ -1553,18 +1655,20 @@ SECURITY_READ_ONLY_LATE(static struct mac_policy_conf) vng_policy_conf = { .mpc_runtime_flags = 0 }; -static mac_policy_handle_t vng_policy_handle; +SECURITY_READ_ONLY_LATE(static mac_policy_handle_t) vng_policy_handle; void vnguard_policy_init(void) { - if (0 == PE_i_can_has_debugger(NULL)) + if (0 == PE_i_can_has_debugger(NULL)) { return; + } vng_policy_flags = kVNG_POLICY_LOGMSG | - kVNG_POLICY_EXC_CORPSE | kVNG_POLICY_UPRINTMSG; - PE_parse_boot_argn("vnguard", &vng_policy_flags, sizeof (vng_policy_flags)); - if (vng_policy_flags) + kVNG_POLICY_EXC_CORPSE | kVNG_POLICY_UPRINTMSG; + PE_parse_boot_argn("vnguard", &vng_policy_flags, sizeof(vng_policy_flags)); + if (vng_policy_flags) { mac_policy_register(&vng_policy_conf, &vng_policy_handle, NULL); + } } #if DEBUG || DEVELOPMENT @@ -1573,7 +1677,7 @@ vnguard_policy_init(void) SYSCTL_DECL(_kern_vnguard); SYSCTL_NODE(_kern, OID_AUTO, vnguard, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "vnguard"); SYSCTL_INT(_kern_vnguard, OID_AUTO, flags, CTLFLAG_RW | CTLFLAG_LOCKED, - &vng_policy_flags, 0, "vnguard policy flags"); + &vng_policy_flags, 0, "vnguard policy flags"); #endif #endif /* CONFIG_MACF && CONFIG_VNGUARD */