/*
- * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
+ * 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
* 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,
* 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@
*/
#include <security/mac_policy.h>
#include <pexpert/pexpert.h>
#include <sys/sysctl.h>
+#include <sys/reason.h>
#endif
#define f_flag f_fglob->fg_flag
#define f_type f_fglob->fg_ops->fo_type
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 );
+ 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);
/*
*/
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 *);
/*
struct guarded_fileproc {
struct fileproc gf_fileproc;
- u_int gf_magic;
- u_int gf_attrs;
- guardid_t gf_guard;
+ u_int gf_magic;
+ u_int gf_attrs;
+ guardid_t gf_guard;
};
-const size_t sizeof_guarded_fileproc = sizeof (struct guarded_fileproc);
+const size_t sizeof_guarded_fileproc = sizeof(struct guarded_fileproc);
-#define FP_TO_GFP(fp) ((struct guarded_fileproc *)(fp))
-#define GFP_TO_FP(gfp) (&(gfp)->gf_fileproc)
+#define FP_TO_GFP(fp) ((struct guarded_fileproc *)(fp))
+#define GFP_TO_FP(gfp) (&(gfp)->gf_fileproc)
-#define GUARDED_FILEPROC_MAGIC 0x29083
+#define GUARDED_FILEPROC_MAGIC 0x29083
struct gfp_crarg {
guardid_t gca_guard;
u_int gca_attrs;
};
+#ifdef OS_REFCNT_DEBUG
+extern struct os_refgrp f_iocount_refgrp;
+#endif
+
static struct fileproc *
guarded_fileproc_alloc_init(void *crarg)
{
struct gfp_crarg *aarg = crarg;
struct guarded_fileproc *gfp;
- if ((gfp = kalloc(sizeof (*gfp))) == NULL)
- return (NULL);
+ if ((gfp = kalloc(sizeof(*gfp))) == NULL) {
+ return NULL;
+ }
+
+ bzero(gfp, sizeof(*gfp));
+
+ struct fileproc *fp = &gfp->gf_fileproc;
+ os_ref_init(&fp->f_iocount, &f_iocount_refgrp);
+ fp->f_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
struct guarded_fileproc *gfp = FP_TO_GFP(fp);
if (FILEPROC_TYPE(fp) != FTYPE_GUARDED ||
- GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ GUARDED_FILEPROC_MAGIC != gfp->gf_magic) {
panic("%s: corrupt fp %p flags %x", __func__, fp, fp->f_flags);
+ }
- kfree(gfp, sizeof (*gfp));
+ kfree(gfp, sizeof(*gfp));
}
static int
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)
+ 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);
+ * error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE);
* proc_fdunlock(p);
- * return (error);
+ * return error;
* }
*/
if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
struct guarded_fileproc *gfp = FP_TO_GFP(fp);
- if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ 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 & gfp->gf_attrs) == attrs;
}
- return (0);
+ return 0;
}
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)
+ if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) {
panic("%s corrupt fp %p flags %x", __func__, fp, fp->f_flags);
+ }
struct guarded_fileproc *gfp = FP_TO_GFP(fp);
/* all gfd fields protected via proc_fdlock() */
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;
}
/*
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 = {
};
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;
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);
}
/*
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 = {
};
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;
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);
}
/*
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 = {
};
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);
}
/*
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);
+ return error;
}
/*
* If 'nguard' is NULL, fd must be guarded at entry, <guard, guardflags>
* 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 <guard, guardflags> matches what's already guarding the descriptor,
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 */
}
}
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) {
/*
*/
struct guarded_fileproc *gfp = FP_TO_GFP(fp);
- if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) {
panic("%s: corrupt gfp %p flags %x",
- __func__, gfp, fp->f_flags);
+ __func__, gfp, fp->f_flags);
+ }
if (oldg == gfp->gf_guard &&
uap->guardflags == gfp->gf_attrs) {
* 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 */
.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 */
(void) fp_drop(p, fd, fp, 1);
break;
}
proc_fdunlock(p);
- return (error);
+ return error;
}
} else {
/*
goto dropout;
}
- if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic) {
panic("%s: corrupt gfp %p flags %x",
- __func__, gfp, fp->f_flags);
+ __func__, gfp, fp->f_flags);
+ }
if (oldg != gfp->gf_guard ||
uap->guardflags != gfp->gf_attrs) {
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);
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 */
(void) fp_drop(p, fd, fp, 1);
break;
}
proc_fdunlock(p);
- return (error);
+ return error;
} else {
/*
* Not already guarded, and no new guard?
dropout:
(void) fp_drop(p, fd, fp, 1);
proc_fdunlock(p);
- return (error);
+ return error;
}
/*
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;
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;
error = dofilewrite(&context, fp, uap->cbuf, uap->nbyte,
- (off_t)-1, 0, retval);
+ (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);
+ if (wrote_some) {
+ fp_drop_written(p, fd, fp);
+ } else {
+ fp_drop(p, fd, fp, 0);
+ }
+ return error;
}
/*
*
* 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;
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) {
if (vnode_isfifo(vp)) {
error = ESPIPE;
goto errout;
- }
+ }
if ((vp->v_flag & VISTTY)) {
error = ENXIO;
goto errout;
}
error = dofilewrite(&context, fp, uap->buf, uap->nbyte,
- uap->offset, FOF_OFFSET, retval);
+ uap->offset, FOF_OFFSET, retval);
wrote_some = *retval > 0;
}
errout:
- if (wrote_some)
- fp_drop_written(p, fd, fp);
- else
- fp_drop(p, fd, fp, 0);
+ if (wrote_some) {
+ fp_drop_written(p, fd, fp);
+ } else {
+ 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;
}
/*
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.
*/
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 = 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);
+
+ if (wrote_some) {
+ fp_drop_written(p, uap->fd, fp);
+ } else {
+ fp_drop(p, uap->fd, fp, 0);
+ }
ExitThisRoutine:
if (auio != NULL) {
uio_free(auio);
}
- return (error);
+ return error;
}
/*
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
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);
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;
{
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;
{
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;
}
{
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;
}
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->f_fglob;
+ 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;
}
}
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);
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) {
} 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;
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;
}
/*
lck_rw_unlock_exclusive(&llock);
}
+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 the pathname is very long, just keep the trailing part
+ */
+ const uint32_t pathmax = 3 * EXIT_REASON_USER_DESC_MAX_LEN / 4;
+ if (pathlen > pathmax) {
+ path += (pathlen - pathmax);
+ pathlen = pathmax;
+ }
+ uint32_t rsize = kcdata_estimate_required_buffer_size(1, pathlen);
+ if (0 == os_reason_alloc_buffer(r, rsize)) {
+ struct kcdata_descriptor *kcd = &r->osr_kcd_descriptor;
+ mach_vm_address_t addr;
+ if (kcdata_get_memory_addr(kcd,
+ EXIT_REASON_USER_DESC, pathlen, &addr) == KERN_SUCCESS) {
+ kcdata_memcpy(kcd, addr, path, pathlen);
+ return r;
+ }
+ }
+ os_reason_free(r);
+ 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, const char *nm)
+ unsigned opval, vnode_t vp)
{
int retval = 0;
retval = EPERM;
}
- if (vng_policy_flags & kVNG_POLICY_LOGMSG) {
+ if (vng_policy_flags & (kVNG_POLICY_LOGMSG | kVNG_POLICY_UPRINTMSG)) {
/* log a message */
const char *op;
switch (opval) {
op = "(unknown)";
break;
}
+
+ const char *nm = vnode_getname(vp);
proc_t p = current_proc();
const struct vng_owner *vgo;
TAILQ_FOREACH(vgo, &vgi->vgi_owners, vgo_link) {
- printf("%s[%d]: %s%s: '%s' guarded by %s[%d] (0x%llx)\n",
- proc_name_address(p), proc_pid(p), op,
- 0 != retval ? " denied" : "",
- NULL != nm ? nm : "(unknown)",
- proc_name_address(vgo->vgo_p), proc_pid(vgo->vgo_p),
- vgi->vgi_guard);
+ const char fmt[] =
+ "%s[%d]: %s%s: '%s' guarded by %s[%d] (0x%llx)\n";
+
+ if (vng_policy_flags & kVNG_POLICY_LOGMSG) {
+ printf(fmt,
+ proc_name_address(p), proc_pid(p), op,
+ 0 != retval ? " denied" : "",
+ NULL != nm ? nm : "(unknown)",
+ proc_name_address(vgo->vgo_p),
+ proc_pid(vgo->vgo_p), vgi->vgi_guard);
+ }
+ if (vng_policy_flags & kVNG_POLICY_UPRINTMSG) {
+ uprintf(fmt,
+ proc_name_address(p), proc_pid(p), op,
+ 0 != retval ? " denied" : "",
+ NULL != nm ? nm : "(unknown)",
+ proc_name_address(vgo->vgo_p),
+ proc_pid(vgo->vgo_p), vgi->vgi_guard);
+ }
+ }
+ 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;
EXC_GUARD_ENCODE_TARGET(code, pid);
subcode = vgi->vgi_guard;
+ lck_rw_unlock_shared(&llock);
+
if (vng_policy_flags & kVNG_POLICY_EXC_CORPSE) {
- task_violated_guard(code, subcode, NULL);
- /* not fatal */
+ char *path;
+ int len = MAXPATHLEN;
+ MALLOC(path, char *, len, M_TEMP, M_WAITOK);
+ os_reason_t r = NULL;
+ if (NULL != path) {
+ vn_getpath(vp, path, &len);
+ if (*path && len) {
+ r = vng_reason_from_pathname(path, len);
+ }
+ }
+ task_violated_guard(code, subcode, r); /* not fatal */
+ if (NULL != r) {
+ os_reason_free(r);
+ }
+ if (NULL != path) {
+ FREE(path, M_TEMP);
+ }
} 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);
}
/*
- * A vnode guard was tripped on this thread.
+ * A fatal vnode guard was tripped on this thread.
*
* (Invoked before returning to userland from the syscall handler.)
*/
static int
vng_vnode_check_rename(kauth_cred_t __unused cred,
struct vnode *__unused dvp, struct label *__unused dlabel,
- struct vnode *__unused vp, struct label *label,
- struct componentname *cnp,
+ struct vnode *vp, struct label *label,
+ struct componentname *__unused cnp,
struct vnode *__unused tdvp, struct label *__unused tdlabel,
- struct vnode *__unused tvp, struct label *tlabel,
- struct componentname *tcnp)
+ struct vnode *tvp, struct label *tlabel,
+ struct componentname *__unused tcnp)
{
int error = 0;
if (NULL != label || NULL != tlabel) {
lck_rw_lock_shared(&llock);
const struct vng_info *vgi =
vng_lbl_get_withattr(label, VNG_RENAME_FROM);
- if (NULL != vgi)
- error = vng_guard_violation(vgi,
- VNG_RENAME_FROM, cnp->cn_nameptr);
+ 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, tcnp->cn_nameptr);
+ VNG_RENAME_TO, tvp);
+ }
}
lck_rw_unlock_shared(&llock);
}
if (NULL != label) {
lck_rw_lock_shared(&llock);
const struct vng_info *vgi =
- vng_lbl_get_withattr(label, VNG_LINK);
+ vng_lbl_get_withattr(label, VNG_LINK);
if (vgi) {
- const char *nm = vnode_getname(vp);
- error = vng_guard_violation(vgi, VNG_LINK, nm);
- if (nm)
- vnode_putname(nm);
+ error = vng_guard_violation(vgi, VNG_LINK, vp);
}
lck_rw_unlock_shared(&llock);
}
static int
vng_vnode_check_unlink(kauth_cred_t __unused cred,
struct vnode *__unused dvp, struct label *__unused dlabel,
- struct vnode *__unused vp, struct label *label, struct componentname *cnp)
+ struct vnode *vp, struct label *label, struct componentname *__unused cnp)
{
int error = 0;
if (NULL != label) {
lck_rw_lock_shared(&llock);
const struct vng_info *vgi =
vng_lbl_get_withattr(label, VNG_UNLINK);
- if (vgi)
- error = vng_guard_violation(vgi, VNG_UNLINK,
- cnp->cn_nameptr);
+ if (vgi) {
+ error = vng_guard_violation(vgi, VNG_UNLINK, vp);
+ }
lck_rw_unlock_shared(&llock);
}
return error;
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;
+ }
}
- const char *nm = vnode_getname(vp);
- error = vng_guard_violation(vgi,
- VNG_WRITE_OTHER, nm);
- if (nm)
- vnode_putname(nm);
+ error = vng_guard_violation(vgi, VNG_WRITE_OTHER, vp);
}
- done:
+done:
lck_rw_unlock_shared(&llock);
}
return error;
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;
+ }
}
- const char *nm = vnode_getname(vp);
- error = vng_guard_violation(vgi,
- VNG_TRUNC_OTHER, nm);
- if (nm)
- vnode_putname(nm);
+ error = vng_guard_violation(vgi, VNG_TRUNC_OTHER, vp);
}
- done:
+done:
lck_rw_unlock_shared(&llock);
}
return error;
if (NULL != flabel || NULL != slabel) {
lck_rw_lock_shared(&llock);
const struct vng_info *vgi =
- vng_lbl_get_withattr(flabel, VNG_EXCHDATA);
+ vng_lbl_get_withattr(flabel, VNG_EXCHDATA);
if (NULL != vgi) {
- const char *nm = vnode_getname(fvp);
- error = vng_guard_violation(vgi,
- VNG_EXCHDATA, nm);
- if (nm)
- vnode_putname(nm);
+ error = vng_guard_violation(vgi, VNG_EXCHDATA, fvp);
}
if (0 == error) {
vgi = vng_lbl_get_withattr(slabel, VNG_EXCHDATA);
if (NULL != vgi) {
- const char *nm = vnode_getname(svp);
error = vng_guard_violation(vgi,
- VNG_EXCHDATA, nm);
- if (nm)
- vnode_putname(nm);
+ VNG_EXCHDATA, svp);
}
}
lck_rw_unlock_shared(&llock);
return error;
}
+/* Intercept open-time truncations (by "other") of a guarded vnode */
+
+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);
+}
+
/*
* Configuration gorp
*/
.mpo_vnode_check_write = vng_vnode_check_write,
.mpo_vnode_check_truncate = vng_vnode_check_truncate,
.mpo_vnode_check_exchangedata = vng_vnode_check_exchangedata,
+ .mpo_vnode_check_open = vng_vnode_check_open,
.mpo_policy_syscall = vng_policy_syscall,
.mpo_policy_init = vng_init,
.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;
- PE_parse_boot_argn("vnguard", &vng_policy_flags, sizeof (vng_policy_flags));
- if (vng_policy_flags)
+ }
+ 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) {
mac_policy_register(&vng_policy_conf, &vng_policy_handle, NULL);
+ }
}
#if DEBUG || DEVELOPMENT
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 */