#include <sys/reason.h>
#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);
+extern int do_uiowrite(struct proc *p, struct fileproc *fp, uio_t uio, int flags, user_ssize_t *retval);
/*
* Experimental guarded file descriptor support.
* 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
struct guarded_fileproc {
struct fileproc gf_fileproc;
- u_int gf_magic;
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;
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;
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
}
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 */
/*
* Expected use pattern:
*
- * if (FP_ISGUARDED(fp, GUARD_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;
}
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);
+ panic("%s corrupt fp %p flags %x", __func__, fp, fp->fp_flags);
}
struct guarded_fileproc *gfp = FP_TO_GFP(fp);
mach_exception_subcode_t subcode = gfp->gf_guard;
thread_t t = current_thread();
- thread_guard_violation(t, code, subcode);
+ thread_guard_violation(t, code, subcode, TRUE);
return EPERM;
}
return EINVAL;
}
- return kqueue_body(p, guarded_fileproc_alloc_init, &crarg, retval);
+ return kqueue_internal(p, guarded_fileproc_alloc_init, &crarg, retval);
}
/*
proc_fdunlock(p);
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);
}
/*
*/
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) {
/*
/*
* 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:
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) {
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;
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;
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 */
+ case EKEEPLOOKING: /* fp_iocount indicates collision */
(void) fp_drop(p, fd, fp, 1);
fileproc_free(nfp);
goto restart;
guardid_t uguard;
struct fileproc *fp;
struct guarded_fileproc *gfp;
- bool wrote_some = false;
AUDIT_ARG(fd, fd);
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);
}
+
+ fp_drop(p, fd, fp, 0);
+
return error;
}
vnode_t vp = (vnode_t)0;
guardid_t uguard;
struct guarded_fileproc *gfp;
- bool wrote_some = false;
AUDIT_ARG(fd, fd);
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;
error = dofilewrite(&context, fp, uap->buf, uap->nbyte,
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);
- }
+ 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);
struct user_iovec *iovp;
guardid_t uguard;
struct guarded_fileproc *gfp;
- bool wrote_some = false;
AUDIT_ARG(fd, uap->fd);
if ((fp->f_flag & FWRITE) == 0) {
error = EBADF;
} else {
- error = wr_uio(p, fp, auio, retval);
- wrote_some = *retval > 0;
+ error = do_uiowrite(p, fp, auio, 0, retval);
}
- if (wrote_some) {
- fp_drop_written(p, uap->fd, fp);
- } else {
- fp_drop(p, uap->fd, fp, 0);
- }
+ fp_drop(p, uap->fd, fp, 0);
ExitThisRoutine:
if (auio != NULL) {
uio_free(auio);
}
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)
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)
{
error = EBADF;
break;
}
- struct fileglob *fg = fp->f_fglob;
+ struct fileglob *fg = fp->fp_glob;
if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE) {
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 (vgi->vgi_guard != vns->vns_guard) {
error = EPERM; /* guard mismatch */
} else if (vgi->vgi_attrs != vns->vns_attrs) {
- error = EACCES; /* attr mismatch */
+ /*
+ * 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);
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;
}
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)
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 (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);
* 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,
.mpo_vnode_check_open = vng_vnode_check_open,
.mpo_policy_syscall = vng_policy_syscall,
- .mpo_policy_init = vng_init,
};
static const char *vng_labelnames[] = {
.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)