]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_guarded.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_guarded.c
index dc29cb531eede2195b58b51fd4aec31488535139..8fc2889fd579ca7623c44c7e323c6476e84b3381 100644 (file)
 #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.
@@ -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;
 };
 
-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,13 +116,12 @@ 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;
 
@@ -127,13 +132,7 @@ 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
@@ -152,10 +151,6 @@ fp_lookup_guarded(proc_t p, int fd, guardid_t guard,
        }
        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 */
@@ -169,24 +164,20 @@ fp_lookup_guarded(proc_t p, int fd, guardid_t guard,
 /*
  * 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;
 }
@@ -197,7 +188,7 @@ 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);
+               panic("%s corrupt fp %p flags %x", __func__, fp, fp->fp_flags);
        }
 
        struct guarded_fileproc *gfp = FP_TO_GFP(fp);
@@ -211,7 +202,7 @@ 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);
+       thread_guard_violation(t, code, subcode, TRUE);
        return EPERM;
 }
 
@@ -413,7 +404,7 @@ guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval)
                return EINVAL;
        }
 
-       return kqueue_body(p, guarded_fileproc_alloc_init, &crarg, retval);
+       return kqueue_internal(p, guarded_fileproc_alloc_init, &crarg, retval);
 }
 
 /*
@@ -439,9 +430,8 @@ guarded_close_np(proc_t p, struct guarded_close_np_args *uap,
                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);
 }
 
 /*
@@ -579,11 +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) {
                                /*
@@ -611,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:
@@ -636,16 +621,16 @@ restart:
                        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;
@@ -672,11 +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;
@@ -688,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);
@@ -696,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;
@@ -737,7 +717,6 @@ guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t
        guardid_t uguard;
        struct fileproc *fp;
        struct guarded_fileproc *gfp;
-       bool wrote_some = false;
 
        AUDIT_ARG(fd, fd);
 
@@ -755,17 +734,14 @@ guarded_write_np(struct proc *p, struct guarded_write_np_args *uap, user_ssize_t
                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;
 }
 
@@ -784,7 +760,6 @@ guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize
        vnode_t vp  = (vnode_t)0;
        guardid_t uguard;
        struct guarded_fileproc *gfp;
-       bool wrote_some = false;
 
        AUDIT_ARG(fd, fd);
 
@@ -802,13 +777,13 @@ guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize
                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;
@@ -824,14 +799,9 @@ guarded_pwrite_np(struct proc *p, struct guarded_pwrite_np_args *uap, user_ssize
 
                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);
@@ -855,7 +825,6 @@ 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);
 
@@ -904,15 +873,10 @@ guarded_writev_np(struct proc *p, struct guarded_writev_np_args *uap, user_ssize
        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);
@@ -1042,8 +1006,8 @@ free_vgo(struct vng_owner *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)
@@ -1077,6 +1041,59 @@ 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)
 {
@@ -1101,7 +1118,7 @@ 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;
@@ -1122,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);
@@ -1165,7 +1182,16 @@ vnguard_sysc_setguard(proc_t p, const struct vnguard_set *vns)
                                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);
@@ -1205,6 +1231,19 @@ vng_policy_syscall(proc_t p, int cmd, user_addr_t arg)
                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;
        }
@@ -1281,6 +1320,11 @@ vng_reason_from_pathname(const char *path, uint32_t pathlen)
 
 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)
@@ -1364,10 +1408,14 @@ 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);
@@ -1379,13 +1427,14 @@ vng_guard_violation(const struct vng_info *vgi,
                        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);
@@ -1575,13 +1624,6 @@ vng_vnode_check_open(kauth_cred_t cred,
  * 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,
 
@@ -1594,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[] = {
@@ -1614,7 +1655,7 @@ 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)