xref: /netbsd/sys/kern/kern_veriexec.c (revision 416a8a0e)
1*416a8a0eSriastradh /*	$NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $	*/
28f9f36e4Smaxv 
38f9f36e4Smaxv /*-
48f9f36e4Smaxv  * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
58f9f36e4Smaxv  * Copyright (c) 2005, 2006 Brett Lymn <blymn@NetBSD.org>
68f9f36e4Smaxv  * All rights reserved.
78f9f36e4Smaxv  *
88f9f36e4Smaxv  * Redistribution and use in source and binary forms, with or without
98f9f36e4Smaxv  * modification, are permitted provided that the following conditions
108f9f36e4Smaxv  * are met:
118f9f36e4Smaxv  * 1. Redistributions of source code must retain the above copyright
128f9f36e4Smaxv  *    notice, this list of conditions and the following disclaimer.
138f9f36e4Smaxv  * 2. Redistributions in binary form must reproduce the above copyright
148f9f36e4Smaxv  *    notice, this list of conditions and the following disclaimer in the
158f9f36e4Smaxv  *    documentation and/or other materials provided with the distribution.
168f9f36e4Smaxv  * 3. The name of the authors may not be used to endorse or promote products
178f9f36e4Smaxv  *    derived from this software without specific prior written permission.
188f9f36e4Smaxv  *
198f9f36e4Smaxv  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
208f9f36e4Smaxv  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
218f9f36e4Smaxv  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
228f9f36e4Smaxv  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
238f9f36e4Smaxv  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
248f9f36e4Smaxv  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
258f9f36e4Smaxv  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
268f9f36e4Smaxv  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
278f9f36e4Smaxv  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
288f9f36e4Smaxv  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
298f9f36e4Smaxv  */
308f9f36e4Smaxv 
318f9f36e4Smaxv #include <sys/cdefs.h>
32*416a8a0eSriastradh __KERNEL_RCSID(0, "$NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $");
338f9f36e4Smaxv 
348f9f36e4Smaxv #include "opt_veriexec.h"
358f9f36e4Smaxv 
368f9f36e4Smaxv #include <sys/param.h>
378f9f36e4Smaxv #include <sys/mount.h>
388f9f36e4Smaxv #include <sys/kmem.h>
398f9f36e4Smaxv #include <sys/vnode.h>
408f9f36e4Smaxv #include <sys/namei.h>
418f9f36e4Smaxv #include <sys/once.h>
428f9f36e4Smaxv #include <sys/proc.h>
438f9f36e4Smaxv #include <sys/rwlock.h>
448f9f36e4Smaxv #include <sys/syslog.h>
458f9f36e4Smaxv #include <sys/sysctl.h>
468f9f36e4Smaxv #include <sys/inttypes.h>
478f9f36e4Smaxv #include <sys/verified_exec.h>
488f9f36e4Smaxv #include <sys/sha1.h>
498f9f36e4Smaxv #include <sys/sha2.h>
508f9f36e4Smaxv #include <sys/rmd160.h>
518f9f36e4Smaxv #include <sys/md5.h>
528f9f36e4Smaxv #include <sys/fileassoc.h>
538f9f36e4Smaxv #include <sys/kauth.h>
548f9f36e4Smaxv #include <sys/conf.h>
558f9f36e4Smaxv #include <miscfs/specfs/specdev.h>
568f9f36e4Smaxv #include <prop/proplib.h>
578f9f36e4Smaxv #include <sys/fcntl.h>
588f9f36e4Smaxv 
598f9f36e4Smaxv /* Readable values for veriexec_file_report(). */
608f9f36e4Smaxv #define	REPORT_ALWAYS		0x01	/* Always print */
618f9f36e4Smaxv #define	REPORT_VERBOSE		0x02	/* Print when verbose >= 1 */
628f9f36e4Smaxv #define	REPORT_DEBUG		0x04	/* Print when verbose >= 2 (debug) */
638f9f36e4Smaxv #define	REPORT_PANIC		0x08	/* Call panic() */
648f9f36e4Smaxv #define	REPORT_ALARM		0x10	/* Alarm - also print pid/uid/.. */
658f9f36e4Smaxv #define	REPORT_LOGMASK		(REPORT_ALWAYS|REPORT_VERBOSE|REPORT_DEBUG)
668f9f36e4Smaxv 
678f9f36e4Smaxv /* state of locking for veriexec_file_verify */
688f9f36e4Smaxv #define VERIEXEC_UNLOCKED	0x00	/* Nothing locked, callee does it */
698f9f36e4Smaxv #define VERIEXEC_LOCKED		0x01	/* Global op lock held */
708f9f36e4Smaxv 
71c35516e8Smaxv /* state of file locking for veriexec_file_verify */
72c35516e8Smaxv #define VERIEXEC_FILE_UNLOCKED	0x02	/* Nothing locked, callee does it */
73c35516e8Smaxv #define VERIEXEC_FILE_LOCKED	0x04	/* File locked */
748f9f36e4Smaxv 
758f9f36e4Smaxv #define VERIEXEC_RW_UPGRADE(lock)	while((rw_tryupgrade(lock)) == 0){};
768f9f36e4Smaxv 
778f9f36e4Smaxv struct veriexec_fpops {
788f9f36e4Smaxv 	const char *type;
798f9f36e4Smaxv 	size_t hash_len;
808f9f36e4Smaxv 	size_t context_size;
818f9f36e4Smaxv 	veriexec_fpop_init_t init;
828f9f36e4Smaxv 	veriexec_fpop_update_t update;
838f9f36e4Smaxv 	veriexec_fpop_final_t final;
848f9f36e4Smaxv 	LIST_ENTRY(veriexec_fpops) entries;
858f9f36e4Smaxv };
868f9f36e4Smaxv 
878f9f36e4Smaxv /* Veriexec per-file entry data. */
888f9f36e4Smaxv struct veriexec_file_entry {
898f9f36e4Smaxv 	krwlock_t lock;				/* r/w lock */
908f9f36e4Smaxv 	u_char *filename;			/* File name. */
918f9f36e4Smaxv 	u_char type;				/* Entry type. */
928f9f36e4Smaxv 	u_char status;				/* Evaluation status. */
938f9f36e4Smaxv 	u_char *fp;				/* Fingerprint. */
948f9f36e4Smaxv 	struct veriexec_fpops *ops;		/* Fingerprint ops vector*/
958f9f36e4Smaxv 	size_t filename_len;			/* Length of filename. */
968f9f36e4Smaxv };
978f9f36e4Smaxv 
988f9f36e4Smaxv /* Veriexec per-table data. */
998f9f36e4Smaxv struct veriexec_table_entry {
1008f9f36e4Smaxv 	uint64_t vte_count;			/* Number of Veriexec entries. */
1018f9f36e4Smaxv 	const struct sysctlnode *vte_node;
1028f9f36e4Smaxv };
1038f9f36e4Smaxv 
1048f9f36e4Smaxv static int veriexec_verbose;
1058f9f36e4Smaxv static int veriexec_strict;
1068f9f36e4Smaxv static int veriexec_bypass = 1;
1078f9f36e4Smaxv 
1088f9f36e4Smaxv static char *veriexec_fp_names = NULL;
1098f9f36e4Smaxv static size_t veriexec_name_max = 0;
1108f9f36e4Smaxv 
1118f9f36e4Smaxv static const struct sysctlnode *veriexec_count_node;
1128f9f36e4Smaxv 
1138f9f36e4Smaxv static fileassoc_t veriexec_hook;
1148f9f36e4Smaxv static specificdata_key_t veriexec_mountspecific_key;
1158f9f36e4Smaxv 
1168f9f36e4Smaxv static LIST_HEAD(, veriexec_fpops) veriexec_fpops_list =
1178f9f36e4Smaxv 	LIST_HEAD_INITIALIZER(veriexec_fpops_list);
1188f9f36e4Smaxv 
1198f9f36e4Smaxv static int veriexec_raw_cb(kauth_cred_t, kauth_action_t, void *,
1208f9f36e4Smaxv     void *, void *, void *, void *);
1218f9f36e4Smaxv static struct veriexec_fpops *veriexec_fpops_lookup(const char *);
1228f9f36e4Smaxv static void veriexec_file_free(struct veriexec_file_entry *);
1238f9f36e4Smaxv 
1248f9f36e4Smaxv static unsigned int veriexec_tablecount = 0;
1258f9f36e4Smaxv 
1268f9f36e4Smaxv /*
1278f9f36e4Smaxv  * Veriexec operations global lock - most ops hold this as a read
1288f9f36e4Smaxv  * lock, it is upgraded to a write lock when destroying veriexec file
1298f9f36e4Smaxv  * table entries.
1308f9f36e4Smaxv  */
1318f9f36e4Smaxv static krwlock_t veriexec_op_lock;
1328f9f36e4Smaxv 
1338f9f36e4Smaxv /*
1348f9f36e4Smaxv  * Sysctl helper routine for Veriexec.
1358f9f36e4Smaxv  */
1368f9f36e4Smaxv static int
sysctl_kern_veriexec_algorithms(SYSCTLFN_ARGS)1378f9f36e4Smaxv sysctl_kern_veriexec_algorithms(SYSCTLFN_ARGS)
1388f9f36e4Smaxv {
1398f9f36e4Smaxv 	size_t len;
1408f9f36e4Smaxv 	int error;
1418f9f36e4Smaxv 	const char *p;
1428f9f36e4Smaxv 
1438f9f36e4Smaxv 	if (newp != NULL)
1448f9f36e4Smaxv 		return EPERM;
1458f9f36e4Smaxv 
1468f9f36e4Smaxv 	if (namelen != 0)
1478f9f36e4Smaxv 		return EINVAL;
1488f9f36e4Smaxv 
1498f9f36e4Smaxv 	p = veriexec_fp_names == NULL ? "" : veriexec_fp_names;
1508f9f36e4Smaxv 
1518f9f36e4Smaxv 	len = strlen(p) + 1;
1528f9f36e4Smaxv 
1538f9f36e4Smaxv 	if (*oldlenp < len && oldp)
1548f9f36e4Smaxv 		return ENOMEM;
1558f9f36e4Smaxv 
1568f9f36e4Smaxv 	if (oldp && (error = copyout(p, oldp, len)) != 0)
1578f9f36e4Smaxv 		return error;
1588f9f36e4Smaxv 
1598f9f36e4Smaxv 	*oldlenp = len;
1608f9f36e4Smaxv 	return 0;
1618f9f36e4Smaxv }
1628f9f36e4Smaxv 
1638f9f36e4Smaxv static int
sysctl_kern_veriexec_strict(SYSCTLFN_ARGS)1648f9f36e4Smaxv sysctl_kern_veriexec_strict(SYSCTLFN_ARGS)
1658f9f36e4Smaxv {
1668f9f36e4Smaxv 	struct sysctlnode node;
1678f9f36e4Smaxv 	int error, newval;
1688f9f36e4Smaxv 
1698f9f36e4Smaxv 	node = *rnode;
1708f9f36e4Smaxv 	node.sysctl_data = &newval;
1718f9f36e4Smaxv 
1728f9f36e4Smaxv 	newval = veriexec_strict;
1738f9f36e4Smaxv 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
1748f9f36e4Smaxv 	if (error || newp == NULL)
1758f9f36e4Smaxv 		return error;
1768f9f36e4Smaxv 
1778f9f36e4Smaxv 	if (newval < veriexec_strict)
1788f9f36e4Smaxv 		return EPERM;
1798f9f36e4Smaxv 
1808f9f36e4Smaxv 	veriexec_strict = newval;
1818f9f36e4Smaxv 
1828f9f36e4Smaxv 	return 0;
1838f9f36e4Smaxv }
1848f9f36e4Smaxv 
1858f9f36e4Smaxv SYSCTL_SETUP(sysctl_kern_veriexec_setup, "sysctl kern.veriexec setup")
1868f9f36e4Smaxv {
1878f9f36e4Smaxv 	const struct sysctlnode *rnode = NULL;
1888f9f36e4Smaxv 
1898f9f36e4Smaxv 	sysctl_createv(clog, 0, NULL, &rnode,
1908f9f36e4Smaxv 		       CTLFLAG_PERMANENT,
1918f9f36e4Smaxv 		       CTLTYPE_NODE, "veriexec",
1928f9f36e4Smaxv 		       SYSCTL_DESCR("Veriexec"),
1938f9f36e4Smaxv 		       NULL, 0, NULL, 0,
1948f9f36e4Smaxv 		       CTL_KERN, CTL_CREATE, CTL_EOL);
1958f9f36e4Smaxv 
1968f9f36e4Smaxv 	sysctl_createv(clog, 0, &rnode, NULL,
1978f9f36e4Smaxv 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1988f9f36e4Smaxv 		       CTLTYPE_INT, "verbose",
1998f9f36e4Smaxv 		       SYSCTL_DESCR("Veriexec verbose level"),
2008f9f36e4Smaxv 		       NULL, 0, &veriexec_verbose, 0,
2018f9f36e4Smaxv 		       CTL_CREATE, CTL_EOL);
2028f9f36e4Smaxv 	sysctl_createv(clog, 0, &rnode, NULL,
2038f9f36e4Smaxv 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
2048f9f36e4Smaxv 		       CTLTYPE_INT, "strict",
2058f9f36e4Smaxv 		       SYSCTL_DESCR("Veriexec strict level"),
2068f9f36e4Smaxv 		       sysctl_kern_veriexec_strict, 0, NULL, 0,
2078f9f36e4Smaxv 		       CTL_CREATE, CTL_EOL);
2088f9f36e4Smaxv 	sysctl_createv(clog, 0, &rnode, NULL,
2098f9f36e4Smaxv 		       CTLFLAG_PERMANENT,
2108f9f36e4Smaxv 		       CTLTYPE_STRING, "algorithms",
2118f9f36e4Smaxv 		       SYSCTL_DESCR("Veriexec supported hashing "
2128f9f36e4Smaxv 				    "algorithms"),
2138f9f36e4Smaxv 		       sysctl_kern_veriexec_algorithms, 0, NULL, 0,
2148f9f36e4Smaxv 		       CTL_CREATE, CTL_EOL);
2158f9f36e4Smaxv 	sysctl_createv(clog, 0, &rnode, &veriexec_count_node,
2168f9f36e4Smaxv 		       CTLFLAG_PERMANENT,
2178f9f36e4Smaxv 		       CTLTYPE_NODE, "count",
2188f9f36e4Smaxv 		       SYSCTL_DESCR("Number of fingerprints on mount(s)"),
2198f9f36e4Smaxv 		       NULL, 0, NULL, 0,
2208f9f36e4Smaxv 		       CTL_CREATE, CTL_EOL);
2218f9f36e4Smaxv }
2228f9f36e4Smaxv 
2238f9f36e4Smaxv /*
22411acd843Smaxv  * Add ops to the fingerprint ops vector list.
2258f9f36e4Smaxv  */
2268f9f36e4Smaxv int
veriexec_fpops_add(const char * fp_type,size_t hash_len,size_t ctx_size,veriexec_fpop_init_t init,veriexec_fpop_update_t update,veriexec_fpop_final_t final)2278f9f36e4Smaxv veriexec_fpops_add(const char *fp_type, size_t hash_len, size_t ctx_size,
2288f9f36e4Smaxv     veriexec_fpop_init_t init, veriexec_fpop_update_t update,
2298f9f36e4Smaxv     veriexec_fpop_final_t final)
2308f9f36e4Smaxv {
2318f9f36e4Smaxv 	struct veriexec_fpops *ops;
2328f9f36e4Smaxv 
233*416a8a0eSriastradh 	KASSERT(init != NULL);
234*416a8a0eSriastradh 	KASSERT(update != NULL);
235*416a8a0eSriastradh 	KASSERT(final != NULL);
236*416a8a0eSriastradh 	KASSERT(hash_len != 0);
237*416a8a0eSriastradh 	KASSERT(ctx_size != 0);
23811acd843Smaxv 	KASSERT(fp_type != NULL);
2398f9f36e4Smaxv 
2408f9f36e4Smaxv 	if (veriexec_fpops_lookup(fp_type) != NULL)
2418f9f36e4Smaxv 		return (EEXIST);
2428f9f36e4Smaxv 
2438f9f36e4Smaxv 	ops = kmem_alloc(sizeof(*ops), KM_SLEEP);
2448f9f36e4Smaxv 	ops->type = fp_type;
2458f9f36e4Smaxv 	ops->hash_len = hash_len;
2468f9f36e4Smaxv 	ops->context_size = ctx_size;
2478f9f36e4Smaxv 	ops->init = init;
2488f9f36e4Smaxv 	ops->update = update;
2498f9f36e4Smaxv 	ops->final = final;
2508f9f36e4Smaxv 
2518f9f36e4Smaxv 	LIST_INSERT_HEAD(&veriexec_fpops_list, ops, entries);
2528f9f36e4Smaxv 
2538f9f36e4Smaxv 	/*
2548f9f36e4Smaxv 	 * If we don't have space for any names, allocate enough for six
2558f9f36e4Smaxv 	 * which should be sufficient. (it's also enough for all algorithms
2568f9f36e4Smaxv 	 * we can support at the moment)
2578f9f36e4Smaxv 	 */
2588f9f36e4Smaxv 	if (veriexec_fp_names == NULL) {
2598f9f36e4Smaxv 		veriexec_name_max = 64;
2608f9f36e4Smaxv 		veriexec_fp_names = kmem_zalloc(veriexec_name_max, KM_SLEEP);
2618f9f36e4Smaxv 	}
2628f9f36e4Smaxv 
2638f9f36e4Smaxv 	/*
2648f9f36e4Smaxv 	 * If we're running out of space for storing supported algorithms,
2658f9f36e4Smaxv 	 * extend the buffer with space for four names.
2668f9f36e4Smaxv 	 */
2678f9f36e4Smaxv 	while (veriexec_name_max - (strlen(veriexec_fp_names) + 1) <
2688f9f36e4Smaxv 	    strlen(fp_type)) {
2698f9f36e4Smaxv 		char *newp;
2708f9f36e4Smaxv 		unsigned int new_max;
2718f9f36e4Smaxv 
2728f9f36e4Smaxv 		/* Add space for four algorithm names. */
2738f9f36e4Smaxv 		new_max = veriexec_name_max + 64;
2748f9f36e4Smaxv 		newp = kmem_zalloc(new_max, KM_SLEEP);
2758f9f36e4Smaxv 		strlcpy(newp, veriexec_fp_names, new_max);
2768f9f36e4Smaxv 		kmem_free(veriexec_fp_names, veriexec_name_max);
2778f9f36e4Smaxv 		veriexec_fp_names = newp;
2788f9f36e4Smaxv 		veriexec_name_max = new_max;
2798f9f36e4Smaxv 	}
2808f9f36e4Smaxv 
2818f9f36e4Smaxv 	if (*veriexec_fp_names != '\0')
2828f9f36e4Smaxv 		strlcat(veriexec_fp_names, " ", veriexec_name_max);
2838f9f36e4Smaxv 
2848f9f36e4Smaxv 	strlcat(veriexec_fp_names, fp_type, veriexec_name_max);
2858f9f36e4Smaxv 
2868f9f36e4Smaxv 	return (0);
2878f9f36e4Smaxv }
2888f9f36e4Smaxv 
2898f9f36e4Smaxv static void
veriexec_mountspecific_dtor(void * v)2908f9f36e4Smaxv veriexec_mountspecific_dtor(void *v)
2918f9f36e4Smaxv {
2928f9f36e4Smaxv 	struct veriexec_table_entry *vte = v;
2938f9f36e4Smaxv 
2948f9f36e4Smaxv 	if (vte == NULL) {
2958f9f36e4Smaxv 		return;
2968f9f36e4Smaxv 	}
2978f9f36e4Smaxv 	sysctl_free(__UNCONST(vte->vte_node));
2988f9f36e4Smaxv 	veriexec_tablecount--;
2998f9f36e4Smaxv 	kmem_free(vte, sizeof(*vte));
3008f9f36e4Smaxv }
3018f9f36e4Smaxv 
3028f9f36e4Smaxv static int
veriexec_listener_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)3038f9f36e4Smaxv veriexec_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
3048f9f36e4Smaxv     void *arg0, void *arg1, void *arg2, void *arg3)
3058f9f36e4Smaxv {
3068f9f36e4Smaxv 	int result;
3078f9f36e4Smaxv 	enum kauth_system_req req;
3088f9f36e4Smaxv 
3098f9f36e4Smaxv 	if (action != KAUTH_SYSTEM_VERIEXEC)
3108f9f36e4Smaxv 		return KAUTH_RESULT_DEFER;
3118f9f36e4Smaxv 
3128f9f36e4Smaxv 	result = KAUTH_RESULT_DEFER;
3137cd8f2abSjoerg 	req = (enum kauth_system_req)(uintptr_t)arg0;
3148f9f36e4Smaxv 
3158f9f36e4Smaxv 	if (req == KAUTH_REQ_SYSTEM_VERIEXEC_MODIFY &&
3168f9f36e4Smaxv 	    veriexec_strict > VERIEXEC_LEARNING) {
3178f9f36e4Smaxv 		log(LOG_WARNING, "Veriexec: Strict mode, modifying "
3188f9f36e4Smaxv 		    "tables not permitted.\n");
3198f9f36e4Smaxv 
3208f9f36e4Smaxv 		result = KAUTH_RESULT_DENY;
3218f9f36e4Smaxv 	}
3228f9f36e4Smaxv 
3238f9f36e4Smaxv 	return result;
3248f9f36e4Smaxv }
3258f9f36e4Smaxv 
3268f9f36e4Smaxv /*
3278f9f36e4Smaxv  * Initialise Veriexec.
3288f9f36e4Smaxv  */
3298f9f36e4Smaxv void
veriexec_init(void)3308f9f36e4Smaxv veriexec_init(void)
3318f9f36e4Smaxv {
3328f9f36e4Smaxv 	int error;
3338f9f36e4Smaxv 
3348f9f36e4Smaxv 	/* Register a fileassoc for Veriexec. */
3358f9f36e4Smaxv 	error = fileassoc_register("veriexec",
3368f9f36e4Smaxv 	    (fileassoc_cleanup_cb_t)veriexec_file_free, &veriexec_hook);
3378f9f36e4Smaxv 	if (error)
3388f9f36e4Smaxv 		panic("Veriexec: Can't register fileassoc: error=%d", error);
3398f9f36e4Smaxv 
3408f9f36e4Smaxv 	/* Register listener to handle raw disk access. */
3418f9f36e4Smaxv 	if (kauth_listen_scope(KAUTH_SCOPE_DEVICE, veriexec_raw_cb, NULL) ==
3428f9f36e4Smaxv 	    NULL)
3438f9f36e4Smaxv 		panic("Veriexec: Can't listen on device scope");
3448f9f36e4Smaxv 
3458f9f36e4Smaxv 	error = mount_specific_key_create(&veriexec_mountspecific_key,
3468f9f36e4Smaxv 	    veriexec_mountspecific_dtor);
3478f9f36e4Smaxv 	if (error)
3488f9f36e4Smaxv 		panic("Veriexec: Can't create mountspecific key");
3498f9f36e4Smaxv 
3508f9f36e4Smaxv 	if (kauth_listen_scope(KAUTH_SCOPE_SYSTEM, veriexec_listener_cb,
3518f9f36e4Smaxv 	    NULL) == NULL)
3528f9f36e4Smaxv 		panic("Veriexec: Can't listen on system scope");
3538f9f36e4Smaxv 
3548f9f36e4Smaxv 	rw_init(&veriexec_op_lock);
3558f9f36e4Smaxv 
3568f9f36e4Smaxv #define	FPOPS_ADD(a, b, c, d, e, f)			\
3579ce7f4bbSchristos 	veriexec_fpops_add(a, b, c,			\
3589ce7f4bbSchristos 	    __FPTRCAST(veriexec_fpop_init_t, d),	\
3599ce7f4bbSchristos 	    __FPTRCAST(veriexec_fpop_update_t, e),	\
3609ce7f4bbSchristos 	    __FPTRCAST(veriexec_fpop_final_t, f))
3618f9f36e4Smaxv 
3628f9f36e4Smaxv #ifdef VERIFIED_EXEC_FP_SHA256
3638f9f36e4Smaxv 	FPOPS_ADD("SHA256", SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX),
3648f9f36e4Smaxv 	    SHA256_Init, SHA256_Update, SHA256_Final);
3658f9f36e4Smaxv #endif /* VERIFIED_EXEC_FP_SHA256 */
3668f9f36e4Smaxv 
3678f9f36e4Smaxv #ifdef VERIFIED_EXEC_FP_SHA384
3688f9f36e4Smaxv 	FPOPS_ADD("SHA384", SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX),
3698f9f36e4Smaxv 	    SHA384_Init, SHA384_Update, SHA384_Final);
3708f9f36e4Smaxv #endif /* VERIFIED_EXEC_FP_SHA384 */
3718f9f36e4Smaxv 
3728f9f36e4Smaxv #ifdef VERIFIED_EXEC_FP_SHA512
3738f9f36e4Smaxv 	FPOPS_ADD("SHA512", SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX),
3748f9f36e4Smaxv 	    SHA512_Init, SHA512_Update, SHA512_Final);
3758f9f36e4Smaxv #endif /* VERIFIED_EXEC_FP_SHA512 */
3768f9f36e4Smaxv 
3778f9f36e4Smaxv #undef FPOPS_ADD
3788f9f36e4Smaxv }
3798f9f36e4Smaxv 
3808f9f36e4Smaxv static struct veriexec_fpops *
veriexec_fpops_lookup(const char * name)3818f9f36e4Smaxv veriexec_fpops_lookup(const char *name)
3828f9f36e4Smaxv {
3838f9f36e4Smaxv 	struct veriexec_fpops *ops;
3848f9f36e4Smaxv 
3858f9f36e4Smaxv 	if (name == NULL)
3868f9f36e4Smaxv 		return (NULL);
3878f9f36e4Smaxv 
3888f9f36e4Smaxv 	LIST_FOREACH(ops, &veriexec_fpops_list, entries) {
3898f9f36e4Smaxv 		if (strcasecmp(name, ops->type) == 0)
3908f9f36e4Smaxv 			return (ops);
3918f9f36e4Smaxv 	}
3928f9f36e4Smaxv 
3938f9f36e4Smaxv 	return (NULL);
3948f9f36e4Smaxv }
3958f9f36e4Smaxv 
3968f9f36e4Smaxv /*
3978f9f36e4Smaxv  * Calculate fingerprint. Information on hash length and routines used is
3988f9f36e4Smaxv  * extracted from veriexec_hash_list according to the hash type.
3998f9f36e4Smaxv  *
4008f9f36e4Smaxv  * NOTE: vfe is assumed to be locked for writing on entry.
4018f9f36e4Smaxv  */
4028f9f36e4Smaxv static int
veriexec_fp_calc(struct lwp * l,struct vnode * vp,int file_lock_state,struct veriexec_file_entry * vfe,u_char * fp)403c35516e8Smaxv veriexec_fp_calc(struct lwp *l, struct vnode *vp, int file_lock_state,
4048f9f36e4Smaxv     struct veriexec_file_entry *vfe, u_char *fp)
4058f9f36e4Smaxv {
4068f9f36e4Smaxv 	struct vattr va;
40711acd843Smaxv 	void *ctx;
40811acd843Smaxv 	u_char *buf;
4098f9f36e4Smaxv 	off_t offset, len;
41011acd843Smaxv 	size_t resid;
41111acd843Smaxv 	int error;
4128f9f36e4Smaxv 
41337a04568Sriastradh 	KASSERT(file_lock_state != VERIEXEC_LOCKED);
41437a04568Sriastradh 	KASSERT(file_lock_state != VERIEXEC_UNLOCKED);
415c35516e8Smaxv 
416c35516e8Smaxv 	if (file_lock_state == VERIEXEC_FILE_UNLOCKED)
4178f9f36e4Smaxv 		vn_lock(vp, LK_SHARED | LK_RETRY);
4188f9f36e4Smaxv 	error = VOP_GETATTR(vp, &va, l->l_cred);
419c35516e8Smaxv 	if (file_lock_state == VERIEXEC_FILE_UNLOCKED)
4208f9f36e4Smaxv 		VOP_UNLOCK(vp);
4218f9f36e4Smaxv 	if (error)
4228f9f36e4Smaxv 		return (error);
4238f9f36e4Smaxv 
4248f9f36e4Smaxv 	ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP);
4258f9f36e4Smaxv 	buf = kmem_alloc(PAGE_SIZE, KM_SLEEP);
4268f9f36e4Smaxv 
4278f9f36e4Smaxv 	(vfe->ops->init)(ctx);
4288f9f36e4Smaxv 
4298f9f36e4Smaxv 	len = 0;
4308f9f36e4Smaxv 	error = 0;
4318f9f36e4Smaxv 	for (offset = 0; offset < va.va_size; offset += PAGE_SIZE) {
4328f9f36e4Smaxv 		len = ((va.va_size - offset) < PAGE_SIZE) ?
4338f9f36e4Smaxv 		    (va.va_size - offset) : PAGE_SIZE;
4348f9f36e4Smaxv 
4358f9f36e4Smaxv 		error = vn_rdwr(UIO_READ, vp, buf, len, offset,
4368f9f36e4Smaxv 				UIO_SYSSPACE,
437c35516e8Smaxv 				((file_lock_state == VERIEXEC_FILE_LOCKED)?
4388f9f36e4Smaxv 				 IO_NODELOCKED : 0),
4398f9f36e4Smaxv 				l->l_cred, &resid, NULL);
4408f9f36e4Smaxv 
4418f9f36e4Smaxv 		if (error) {
4428f9f36e4Smaxv 			goto bad;
4438f9f36e4Smaxv 		}
4448f9f36e4Smaxv 
4458f9f36e4Smaxv 		(vfe->ops->update)(ctx, buf, (unsigned int) len);
4468f9f36e4Smaxv 
4478f9f36e4Smaxv 		if (len != PAGE_SIZE)
4488f9f36e4Smaxv 			break;
4498f9f36e4Smaxv 	}
4508f9f36e4Smaxv 
4518f9f36e4Smaxv 	(vfe->ops->final)(fp, ctx);
4528f9f36e4Smaxv 
4538f9f36e4Smaxv bad:
4548f9f36e4Smaxv 	kmem_free(ctx, vfe->ops->context_size);
4558f9f36e4Smaxv 	kmem_free(buf, PAGE_SIZE);
4568f9f36e4Smaxv 
4578f9f36e4Smaxv 	return (error);
4588f9f36e4Smaxv }
4598f9f36e4Smaxv 
4608f9f36e4Smaxv /* Compare two fingerprints of the same type. */
4618f9f36e4Smaxv static int
veriexec_fp_cmp(struct veriexec_fpops * ops,u_char * fp1,u_char * fp2)4628f9f36e4Smaxv veriexec_fp_cmp(struct veriexec_fpops *ops, u_char *fp1, u_char *fp2)
4638f9f36e4Smaxv {
4648f9f36e4Smaxv 	if (veriexec_verbose >= 2) {
4658f9f36e4Smaxv 		int i;
4668f9f36e4Smaxv 
4678f9f36e4Smaxv 		printf("comparing hashes...\n");
4688f9f36e4Smaxv 		printf("fp1: ");
4698f9f36e4Smaxv 		for (i = 0; i < ops->hash_len; i++) {
4708f9f36e4Smaxv 			printf("%02x", fp1[i]);
4718f9f36e4Smaxv 		}
4728f9f36e4Smaxv 		printf("\nfp2: ");
4738f9f36e4Smaxv 		for (i = 0; i < ops->hash_len; i++) {
4748f9f36e4Smaxv 			printf("%02x", fp2[i]);
4758f9f36e4Smaxv 		}
4768f9f36e4Smaxv 		printf("\n");
4778f9f36e4Smaxv 	}
4788f9f36e4Smaxv 
4798f9f36e4Smaxv 	return (memcmp(fp1, fp2, ops->hash_len));
4808f9f36e4Smaxv }
4818f9f36e4Smaxv 
482bc430abcSmaxv static int
veriexec_fp_status(struct lwp * l,struct vnode * vp,int file_lock_state,struct veriexec_file_entry * vfe,u_char * status)483bc430abcSmaxv veriexec_fp_status(struct lwp *l, struct vnode *vp, int file_lock_state,
484bc430abcSmaxv     struct veriexec_file_entry *vfe, u_char *status)
485bc430abcSmaxv {
486bc430abcSmaxv 	size_t hash_len = vfe->ops->hash_len;
487bc430abcSmaxv 	u_char *digest;
48896293727Smaxv 	int error;
489bc430abcSmaxv 
490bc430abcSmaxv 	digest = kmem_zalloc(hash_len, KM_SLEEP);
491bc430abcSmaxv 
492bc430abcSmaxv 	error = veriexec_fp_calc(l, vp, file_lock_state, vfe, digest);
493bc430abcSmaxv 	if (error)
494bc430abcSmaxv 		goto out;
495bc430abcSmaxv 
496bc430abcSmaxv 	/* Compare fingerprint with loaded data. */
497bc430abcSmaxv 	if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0)
498bc430abcSmaxv 		*status = FINGERPRINT_VALID;
499bc430abcSmaxv 	else
500bc430abcSmaxv 		*status = FINGERPRINT_NOMATCH;
501bc430abcSmaxv 
502bc430abcSmaxv out:
503bc430abcSmaxv 	kmem_free(digest, hash_len);
504bc430abcSmaxv 	return error;
505bc430abcSmaxv }
506bc430abcSmaxv 
507bc430abcSmaxv 
5088f9f36e4Smaxv static struct veriexec_table_entry *
veriexec_table_lookup(struct mount * mp)5098f9f36e4Smaxv veriexec_table_lookup(struct mount *mp)
5108f9f36e4Smaxv {
5118f9f36e4Smaxv 	/* XXX: From raidframe init */
5128f9f36e4Smaxv 	if (mp == NULL)
5138f9f36e4Smaxv 		return NULL;
5148f9f36e4Smaxv 
5158f9f36e4Smaxv 	return mount_getspecific(mp, veriexec_mountspecific_key);
5168f9f36e4Smaxv }
5178f9f36e4Smaxv 
5188f9f36e4Smaxv static struct veriexec_file_entry *
veriexec_get(struct vnode * vp)5198f9f36e4Smaxv veriexec_get(struct vnode *vp)
5208f9f36e4Smaxv {
5218f9f36e4Smaxv 	return (fileassoc_lookup(vp, veriexec_hook));
5228f9f36e4Smaxv }
5238f9f36e4Smaxv 
5248f9f36e4Smaxv bool
veriexec_lookup(struct vnode * vp)5258f9f36e4Smaxv veriexec_lookup(struct vnode *vp)
5268f9f36e4Smaxv {
5278f9f36e4Smaxv 	return (veriexec_get(vp) == NULL ? false : true);
5288f9f36e4Smaxv }
5298f9f36e4Smaxv 
5308f9f36e4Smaxv /*
5318f9f36e4Smaxv  * Routine for maintaining mostly consistent message formats in Veriexec.
5328f9f36e4Smaxv  */
5338f9f36e4Smaxv static void
veriexec_file_report(struct veriexec_file_entry * vfe,const u_char * msg,const u_char * filename,struct lwp * l,int f)5348f9f36e4Smaxv veriexec_file_report(struct veriexec_file_entry *vfe, const u_char *msg,
5358f9f36e4Smaxv     const u_char *filename, struct lwp *l, int f)
5368f9f36e4Smaxv {
5378f9f36e4Smaxv 	if (vfe != NULL && vfe->filename != NULL)
5388f9f36e4Smaxv 		filename = vfe->filename;
5398f9f36e4Smaxv 	if (filename == NULL)
5408f9f36e4Smaxv 		return;
5418f9f36e4Smaxv 
5428f9f36e4Smaxv 	if (((f & REPORT_LOGMASK) >> 1) <= veriexec_verbose) {
5438f9f36e4Smaxv 		if (!(f & REPORT_ALARM) || (l == NULL))
5448f9f36e4Smaxv 			log(LOG_NOTICE, "Veriexec: %s [%s]\n", msg,
5458f9f36e4Smaxv 			    filename);
5468f9f36e4Smaxv 		else
5478f9f36e4Smaxv 			log(LOG_ALERT, "Veriexec: %s [%s, prog=%s pid=%u, "
5488f9f36e4Smaxv 			    "uid=%u, gid=%u]\n", msg, filename,
5498f9f36e4Smaxv 			    l->l_proc->p_comm, l->l_proc->p_pid,
5508f9f36e4Smaxv 			    kauth_cred_getuid(l->l_cred),
5518f9f36e4Smaxv 			    kauth_cred_getgid(l->l_cred));
5528f9f36e4Smaxv 	}
5538f9f36e4Smaxv 
5548f9f36e4Smaxv 	if (f & REPORT_PANIC)
5558f9f36e4Smaxv 		panic("Veriexec: Unrecoverable error.");
5568f9f36e4Smaxv }
5578f9f36e4Smaxv 
5588f9f36e4Smaxv /*
5598f9f36e4Smaxv  * Verify the fingerprint of the given file. If we're called directly from
5608f9f36e4Smaxv  * sys_execve(), 'flag' will be VERIEXEC_DIRECT. If we're called from
5618f9f36e4Smaxv  * exec_script(), 'flag' will be VERIEXEC_INDIRECT.  If we are called from
5628f9f36e4Smaxv  * vn_open(), 'flag' will be VERIEXEC_FILE.
5638f9f36e4Smaxv  *
564c35516e8Smaxv  * 'veriexec_op_lock' must be locked (and remains locked).
565c35516e8Smaxv  *
5668f9f36e4Smaxv  * NOTE: The veriexec file entry pointer (vfep) will be returned LOCKED
5678f9f36e4Smaxv  *       on no error.
5688f9f36e4Smaxv  */
5698f9f36e4Smaxv static int
veriexec_file_verify(struct lwp * l,struct vnode * vp,const u_char * name,int flag,int file_lock_state,struct veriexec_file_entry ** vfep)5708f9f36e4Smaxv veriexec_file_verify(struct lwp *l, struct vnode *vp, const u_char *name,
571c35516e8Smaxv     int flag, int file_lock_state, struct veriexec_file_entry **vfep)
5728f9f36e4Smaxv {
5738f9f36e4Smaxv 	struct veriexec_file_entry *vfe;
574bc430abcSmaxv 	int error = 0;
5758f9f36e4Smaxv 
576c35516e8Smaxv 	KASSERT(rw_lock_held(&veriexec_op_lock));
57737a04568Sriastradh 	KASSERT(file_lock_state != VERIEXEC_LOCKED);
57837a04568Sriastradh 	KASSERT(file_lock_state != VERIEXEC_UNLOCKED);
579c35516e8Smaxv 
5808f9f36e4Smaxv #define VFE_NEEDS_EVAL(vfe) ((vfe->status == FINGERPRINT_NOTEVAL) || \
5818f9f36e4Smaxv 			     (vfe->type & VERIEXEC_UNTRUSTED))
5828f9f36e4Smaxv 
5838f9f36e4Smaxv 	if (vfep != NULL)
5848f9f36e4Smaxv 		*vfep = NULL;
5858f9f36e4Smaxv 
5868f9f36e4Smaxv 	if (vp->v_type != VREG)
5878f9f36e4Smaxv 		return (0);
5888f9f36e4Smaxv 
5898f9f36e4Smaxv 	/* Lookup veriexec table entry, save pointer if requested. */
5908f9f36e4Smaxv 	vfe = veriexec_get(vp);
5918f9f36e4Smaxv 	if (vfep != NULL)
5928f9f36e4Smaxv 		*vfep = vfe;
5938f9f36e4Smaxv 
594bc430abcSmaxv 	/* No entry in the veriexec tables. */
595bc430abcSmaxv 	if (vfe == NULL) {
596bc430abcSmaxv 		veriexec_file_report(NULL, "No entry.", name,
597bc430abcSmaxv 		    l, REPORT_VERBOSE);
598bc430abcSmaxv 
599bc430abcSmaxv 		/*
600bc430abcSmaxv 		 * Lockdown mode: Deny access to non-monitored files.
601bc430abcSmaxv 		 * IPS mode: Deny execution of non-monitored files.
602bc430abcSmaxv 		 */
603bc430abcSmaxv 		if ((veriexec_strict >= VERIEXEC_LOCKDOWN) ||
604bc430abcSmaxv 		    ((veriexec_strict >= VERIEXEC_IPS) &&
605bc430abcSmaxv 		     (flag != VERIEXEC_FILE)))
606bc430abcSmaxv 			return (EPERM);
607bc430abcSmaxv 
608bc430abcSmaxv 		return (0);
609bc430abcSmaxv 	}
6108f9f36e4Smaxv 
6118f9f36e4Smaxv 	/*
6128f9f36e4Smaxv 	 * Grab the lock for the entry, if we need to do an evaluation
6138f9f36e4Smaxv 	 * then the lock is a write lock, after we have the write
6148f9f36e4Smaxv 	 * lock, check if we really need it - some other thread may
6158f9f36e4Smaxv 	 * have already done the work for us.
6168f9f36e4Smaxv 	 */
6178f9f36e4Smaxv 	if (VFE_NEEDS_EVAL(vfe)) {
6188f9f36e4Smaxv 		rw_enter(&vfe->lock, RW_WRITER);
6198f9f36e4Smaxv 		if (!VFE_NEEDS_EVAL(vfe))
6208f9f36e4Smaxv 			rw_downgrade(&vfe->lock);
6218f9f36e4Smaxv 	} else
6228f9f36e4Smaxv 		rw_enter(&vfe->lock, RW_READER);
6238f9f36e4Smaxv 
6248f9f36e4Smaxv 	/* Evaluate fingerprint if needed. */
6258f9f36e4Smaxv 	if (VFE_NEEDS_EVAL(vfe)) {
626bc430abcSmaxv 		u_char status;
6278f9f36e4Smaxv 
628bc430abcSmaxv 		error = veriexec_fp_status(l, vp, file_lock_state, vfe, &status);
6298f9f36e4Smaxv 		if (error) {
6308f9f36e4Smaxv 			veriexec_file_report(vfe, "Fingerprint calculation error.",
6318f9f36e4Smaxv 			    name, NULL, REPORT_ALWAYS);
6328f9f36e4Smaxv 			rw_exit(&vfe->lock);
6338f9f36e4Smaxv 			return (error);
6348f9f36e4Smaxv 		}
635bc430abcSmaxv 		vfe->status = status;
6368f9f36e4Smaxv 		rw_downgrade(&vfe->lock);
6378f9f36e4Smaxv 	}
6388f9f36e4Smaxv 
6398f9f36e4Smaxv 	if (!(vfe->type & flag)) {
6408f9f36e4Smaxv 		veriexec_file_report(vfe, "Incorrect access type.", name, l,
6418f9f36e4Smaxv 		    REPORT_ALWAYS|REPORT_ALARM);
6428f9f36e4Smaxv 
6438f9f36e4Smaxv 		/* IPS mode: Enforce access type. */
6448f9f36e4Smaxv 		if (veriexec_strict >= VERIEXEC_IPS) {
6458f9f36e4Smaxv 			rw_exit(&vfe->lock);
6468f9f36e4Smaxv 			return (EPERM);
6478f9f36e4Smaxv 		}
6488f9f36e4Smaxv 	}
6498f9f36e4Smaxv 
6508f9f36e4Smaxv 	switch (vfe->status) {
6518f9f36e4Smaxv 	case FINGERPRINT_NOTEVAL:
6528f9f36e4Smaxv 		/* Should not happen. */
6538f9f36e4Smaxv 		rw_exit(&vfe->lock);
6548f9f36e4Smaxv 		veriexec_file_report(vfe, "Not-evaluated status "
6558f9f36e4Smaxv 		    "post evaluation; inconsistency detected.", name,
6568f9f36e4Smaxv 		    NULL, REPORT_ALWAYS|REPORT_PANIC);
657c274e026Smrg 		__builtin_unreachable();
6588f9f36e4Smaxv 		/* NOTREACHED */
6598f9f36e4Smaxv 
6608f9f36e4Smaxv 	case FINGERPRINT_VALID:
6618f9f36e4Smaxv 		/* Valid fingerprint. */
6628f9f36e4Smaxv 		veriexec_file_report(vfe, "Match.", name, NULL,
6638f9f36e4Smaxv 		    REPORT_VERBOSE);
6648f9f36e4Smaxv 
6658f9f36e4Smaxv 		break;
6668f9f36e4Smaxv 
6678f9f36e4Smaxv 	case FINGERPRINT_NOMATCH:
6688f9f36e4Smaxv 		/* Fingerprint mismatch. */
6698f9f36e4Smaxv 		veriexec_file_report(vfe, "Mismatch.", name,
6708f9f36e4Smaxv 		    NULL, REPORT_ALWAYS|REPORT_ALARM);
6718f9f36e4Smaxv 
6728f9f36e4Smaxv 		/* IDS mode: Deny access on fingerprint mismatch. */
6738f9f36e4Smaxv 		if (veriexec_strict >= VERIEXEC_IDS) {
6748f9f36e4Smaxv 			rw_exit(&vfe->lock);
6758f9f36e4Smaxv 			error = EPERM;
6768f9f36e4Smaxv 		}
6778f9f36e4Smaxv 
6788f9f36e4Smaxv 		break;
6798f9f36e4Smaxv 
6808f9f36e4Smaxv 	default:
6818f9f36e4Smaxv 		/* Should never happen. */
6828f9f36e4Smaxv 		rw_exit(&vfe->lock);
6838f9f36e4Smaxv 		veriexec_file_report(vfe, "Invalid status "
6848f9f36e4Smaxv 		    "post evaluation.", name, NULL, REPORT_ALWAYS|REPORT_PANIC);
68593f9af89Smaxv 		/* NOTREACHED */
6868f9f36e4Smaxv 	}
6878f9f36e4Smaxv 
6888f9f36e4Smaxv 	return (error);
6898f9f36e4Smaxv }
6908f9f36e4Smaxv 
6918f9f36e4Smaxv int
veriexec_verify(struct lwp * l,struct vnode * vp,const u_char * name,int flag,bool * found)6928f9f36e4Smaxv veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
6938f9f36e4Smaxv     bool *found)
6948f9f36e4Smaxv {
6958f9f36e4Smaxv 	struct veriexec_file_entry *vfe;
6968f9f36e4Smaxv 	int r;
6978f9f36e4Smaxv 
6988f9f36e4Smaxv 	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
6998f9f36e4Smaxv 		return 0;
7008f9f36e4Smaxv 
701c35516e8Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
702c35516e8Smaxv 	r = veriexec_file_verify(l, vp, name, flag, VERIEXEC_FILE_UNLOCKED,
703c35516e8Smaxv 	    &vfe);
704c35516e8Smaxv 	rw_exit(&veriexec_op_lock);
7058f9f36e4Smaxv 
7068f9f36e4Smaxv 	if ((r  == 0) && (vfe != NULL))
7078f9f36e4Smaxv 		rw_exit(&vfe->lock);
7088f9f36e4Smaxv 
7098f9f36e4Smaxv 	if (found != NULL)
7108f9f36e4Smaxv 		*found = (vfe != NULL) ? true : false;
7118f9f36e4Smaxv 
7128f9f36e4Smaxv 	return (r);
7138f9f36e4Smaxv }
7148f9f36e4Smaxv 
7158f9f36e4Smaxv /*
7168f9f36e4Smaxv  * Veriexec remove policy code.
7178f9f36e4Smaxv  */
7188f9f36e4Smaxv int
veriexec_removechk(struct lwp * l,struct vnode * vp,const char * pathbuf)7198f9f36e4Smaxv veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
7208f9f36e4Smaxv {
7218f9f36e4Smaxv 	struct veriexec_file_entry *vfe;
7228f9f36e4Smaxv 	int error;
7238f9f36e4Smaxv 
7248f9f36e4Smaxv 	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
7258f9f36e4Smaxv 		return 0;
7268f9f36e4Smaxv 
7278f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
7288f9f36e4Smaxv 	vfe = veriexec_get(vp);
7298f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
7308f9f36e4Smaxv 
7318f9f36e4Smaxv 	if (vfe == NULL) {
7328f9f36e4Smaxv 		/* Lockdown mode: Deny access to non-monitored files. */
7338f9f36e4Smaxv 		if (veriexec_strict >= VERIEXEC_LOCKDOWN)
7348f9f36e4Smaxv 			return (EPERM);
7358f9f36e4Smaxv 
7368f9f36e4Smaxv 		return (0);
7378f9f36e4Smaxv 	}
7388f9f36e4Smaxv 
7398f9f36e4Smaxv 	veriexec_file_report(vfe, "Remove request.", pathbuf, l,
7408f9f36e4Smaxv 	    REPORT_ALWAYS|REPORT_ALARM);
7418f9f36e4Smaxv 
7428f9f36e4Smaxv 	/* IDS mode: Deny removal of monitored files. */
7438f9f36e4Smaxv 	if (veriexec_strict >= VERIEXEC_IDS)
7448f9f36e4Smaxv 		error = EPERM;
7458f9f36e4Smaxv 	else
7468f9f36e4Smaxv 		error = veriexec_file_delete(l, vp);
7478f9f36e4Smaxv 
7488f9f36e4Smaxv 	return error;
7498f9f36e4Smaxv }
7508f9f36e4Smaxv 
7518f9f36e4Smaxv /*
7528f9f36e4Smaxv  * Veriexec rename policy.
7538f9f36e4Smaxv  *
7548f9f36e4Smaxv  * XXX: Once there's a way to hook after a successful rename, it would be
7558f9f36e4Smaxv  * XXX: nice to update vfe->filename to the new name if it's not NULL and
7568f9f36e4Smaxv  * XXX: the new name is absolute (ie., starts with a slash).
7578f9f36e4Smaxv  */
7588f9f36e4Smaxv int
veriexec_renamechk(struct lwp * l,struct vnode * fromvp,const char * fromname,struct vnode * tovp,const char * toname)7598f9f36e4Smaxv veriexec_renamechk(struct lwp *l, struct vnode *fromvp, const char *fromname,
7608f9f36e4Smaxv     struct vnode *tovp, const char *toname)
7618f9f36e4Smaxv {
7627e91eb6dSmaxv 	struct veriexec_file_entry *fvfe = NULL, *tvfe = NULL;
7638f9f36e4Smaxv 
7648f9f36e4Smaxv 	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
7658f9f36e4Smaxv 		return 0;
7668f9f36e4Smaxv 
7678f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
7688f9f36e4Smaxv 
7698f9f36e4Smaxv 	if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
7708f9f36e4Smaxv 		log(LOG_ALERT, "Veriexec: Preventing rename of `%s' to "
7718f9f36e4Smaxv 		    "`%s', uid=%u, pid=%u: Lockdown mode.\n", fromname, toname,
7728f9f36e4Smaxv 		    kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
7738f9f36e4Smaxv 		rw_exit(&veriexec_op_lock);
7748f9f36e4Smaxv 		return (EPERM);
7758f9f36e4Smaxv 	}
7768f9f36e4Smaxv 
7777e91eb6dSmaxv 	fvfe = veriexec_get(fromvp);
7788f9f36e4Smaxv 	if (tovp != NULL)
7798f9f36e4Smaxv 		tvfe = veriexec_get(tovp);
7808f9f36e4Smaxv 
7817e91eb6dSmaxv 	if ((fvfe == NULL) && (tvfe == NULL)) {
7827e91eb6dSmaxv 		/* None of them is monitored */
7837e91eb6dSmaxv 		rw_exit(&veriexec_op_lock);
7847e91eb6dSmaxv 		return 0;
7857e91eb6dSmaxv 	}
7867e91eb6dSmaxv 
7878f9f36e4Smaxv 	if (veriexec_strict >= VERIEXEC_IPS) {
7888f9f36e4Smaxv 		log(LOG_ALERT, "Veriexec: Preventing rename of `%s' "
7898f9f36e4Smaxv 		    "to `%s', uid=%u, pid=%u: IPS mode, %s "
7908f9f36e4Smaxv 		    "monitored.\n", fromname, toname,
7918f9f36e4Smaxv 		    kauth_cred_geteuid(l->l_cred),
7927e91eb6dSmaxv 		    l->l_proc->p_pid, (fvfe != NULL && tvfe != NULL) ?
7938f9f36e4Smaxv 		    "files" : "file");
7948f9f36e4Smaxv 		rw_exit(&veriexec_op_lock);
7958f9f36e4Smaxv 		return (EPERM);
7968f9f36e4Smaxv 	}
7978f9f36e4Smaxv 
7987e91eb6dSmaxv 	if (fvfe != NULL) {
7998f9f36e4Smaxv 		/*
8008f9f36e4Smaxv 		 * Monitored file is renamed; filename no longer relevant.
8017e91eb6dSmaxv 		 */
8027e91eb6dSmaxv 
8037e91eb6dSmaxv 		/*
8048f9f36e4Smaxv 		 * XXX: We could keep the buffer, and when (and if) updating the
8058f9f36e4Smaxv 		 * XXX: filename post-rename, re-allocate it only if it's not
8068f9f36e4Smaxv 		 * XXX: big enough for the new filename.
8078f9f36e4Smaxv 		 */
8087e91eb6dSmaxv 
8097e91eb6dSmaxv 		/* XXX: Get write lock on fvfe here? */
8108f9f36e4Smaxv 
8118f9f36e4Smaxv 		VERIEXEC_RW_UPGRADE(&veriexec_op_lock);
8128f9f36e4Smaxv 		/* once we have the op lock in write mode
8138f9f36e4Smaxv 		 * there should be no locks on any file
8148f9f36e4Smaxv 		 * entries so we can destroy the object.
8158f9f36e4Smaxv 		 */
8168f9f36e4Smaxv 
8177e91eb6dSmaxv 		if (fvfe->filename_len > 0)
8187e91eb6dSmaxv 			kmem_free(fvfe->filename, fvfe->filename_len);
8198f9f36e4Smaxv 
8207e91eb6dSmaxv 		fvfe->filename = NULL;
8217e91eb6dSmaxv 		fvfe->filename_len = 0;
8228f9f36e4Smaxv 
8238f9f36e4Smaxv 		rw_downgrade(&veriexec_op_lock);
8248f9f36e4Smaxv 	}
8258f9f36e4Smaxv 
8268f9f36e4Smaxv 	log(LOG_NOTICE, "Veriexec: %s file `%s' renamed to "
8277e91eb6dSmaxv 	    "%s file `%s', uid=%u, pid=%u.\n", (fvfe != NULL) ?
8288f9f36e4Smaxv 	    "Monitored" : "Non-monitored", fromname, (tvfe != NULL) ?
8298f9f36e4Smaxv 	    "monitored" : "non-monitored", toname,
8308f9f36e4Smaxv 	    kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
8318f9f36e4Smaxv 
8328f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
8338f9f36e4Smaxv 
8347e91eb6dSmaxv 	if (tvfe != NULL) {
8358f9f36e4Smaxv 		/*
8368f9f36e4Smaxv 		 * Monitored file is overwritten. Remove the entry.
8378f9f36e4Smaxv 		 */
8388f9f36e4Smaxv 		(void)veriexec_file_delete(l, tovp);
8397e91eb6dSmaxv 	}
8408f9f36e4Smaxv 
8418f9f36e4Smaxv 	return (0);
8428f9f36e4Smaxv }
8438f9f36e4Smaxv 
8448f9f36e4Smaxv static void
veriexec_file_free(struct veriexec_file_entry * vfe)8458f9f36e4Smaxv veriexec_file_free(struct veriexec_file_entry *vfe)
8468f9f36e4Smaxv {
8478f9f36e4Smaxv 	if (vfe != NULL) {
8488f9f36e4Smaxv 		if (vfe->fp != NULL)
8498f9f36e4Smaxv 			kmem_free(vfe->fp, vfe->ops->hash_len);
8508f9f36e4Smaxv 		if (vfe->filename != NULL)
8518f9f36e4Smaxv 			kmem_free(vfe->filename, vfe->filename_len);
8528f9f36e4Smaxv 		rw_destroy(&vfe->lock);
8538f9f36e4Smaxv 		kmem_free(vfe, sizeof(*vfe));
8548f9f36e4Smaxv 	}
8558f9f36e4Smaxv }
8568f9f36e4Smaxv 
8578f9f36e4Smaxv static void
veriexec_file_purge(struct veriexec_file_entry * vfe,int have_lock)8588f9f36e4Smaxv veriexec_file_purge(struct veriexec_file_entry *vfe, int have_lock)
8598f9f36e4Smaxv {
8608f9f36e4Smaxv 	if (vfe == NULL)
8618f9f36e4Smaxv 		return;
8628f9f36e4Smaxv 
8638f9f36e4Smaxv 	if (have_lock == VERIEXEC_UNLOCKED)
8648f9f36e4Smaxv 		rw_enter(&vfe->lock, RW_WRITER);
8658f9f36e4Smaxv 	else
8668f9f36e4Smaxv 		VERIEXEC_RW_UPGRADE(&vfe->lock);
8678f9f36e4Smaxv 
8688f9f36e4Smaxv 	vfe->status = FINGERPRINT_NOTEVAL;
8698f9f36e4Smaxv 	if (have_lock == VERIEXEC_UNLOCKED)
8708f9f36e4Smaxv 		rw_exit(&vfe->lock);
8718f9f36e4Smaxv 	else
8728f9f36e4Smaxv 		rw_downgrade(&vfe->lock);
8738f9f36e4Smaxv }
8748f9f36e4Smaxv 
8758f9f36e4Smaxv static void
veriexec_file_purge_cb(struct veriexec_file_entry * vfe,void * cookie)8768f9f36e4Smaxv veriexec_file_purge_cb(struct veriexec_file_entry *vfe, void *cookie)
8778f9f36e4Smaxv {
8788f9f36e4Smaxv 	veriexec_file_purge(vfe, VERIEXEC_UNLOCKED);
8798f9f36e4Smaxv }
8808f9f36e4Smaxv 
8818f9f36e4Smaxv /*
8828f9f36e4Smaxv  * Invalidate a Veriexec file entry.
8838f9f36e4Smaxv  * XXX: This should be updated when per-page fingerprints are added.
8848f9f36e4Smaxv  */
8858f9f36e4Smaxv void
veriexec_purge(struct vnode * vp)8868f9f36e4Smaxv veriexec_purge(struct vnode *vp)
8878f9f36e4Smaxv {
8888f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
8898f9f36e4Smaxv 	veriexec_file_purge(veriexec_get(vp), VERIEXEC_UNLOCKED);
8908f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
8918f9f36e4Smaxv }
8928f9f36e4Smaxv 
8938f9f36e4Smaxv /*
8948f9f36e4Smaxv  * Enforce raw disk access policy.
8958f9f36e4Smaxv  *
8968f9f36e4Smaxv  * IDS mode: Invalidate fingerprints on a mount if it's opened for writing.
8978f9f36e4Smaxv  * IPS mode: Don't allow raw writing to disks we monitor.
8988f9f36e4Smaxv  * Lockdown mode: Don't allow raw writing to all disks.
8998f9f36e4Smaxv  *
9008f9f36e4Smaxv  * XXX: This is bogus. There's an obvious race condition between the time
9018f9f36e4Smaxv  * XXX: the disk is open for writing, in which an attacker can access a
9028f9f36e4Smaxv  * XXX: monitored file to get its signature cached again, and when the raw
9038f9f36e4Smaxv  * XXX: file is overwritten on disk.
9048f9f36e4Smaxv  * XXX:
9058f9f36e4Smaxv  * XXX: To solve this, we need something like the following:
9068f9f36e4Smaxv  * XXX:		open raw disk:
9078f9f36e4Smaxv  * XXX:		  - raise refcount,
9088f9f36e4Smaxv  * XXX:		  - invalidate fingerprints,
9098f9f36e4Smaxv  * XXX:		  - mark all entries for that disk with "no cache" flag
9108f9f36e4Smaxv  * XXX:
9118f9f36e4Smaxv  * XXX:		veriexec_verify:
9128f9f36e4Smaxv  * XXX:		  - if "no cache", don't cache evaluation result
9138f9f36e4Smaxv  * XXX:
9148f9f36e4Smaxv  * XXX:		close raw disk:
9158f9f36e4Smaxv  * XXX:		  - lower refcount,
9168f9f36e4Smaxv  * XXX:		  - if refcount == 0, remove "no cache" flag from all entries
9178f9f36e4Smaxv  */
9188f9f36e4Smaxv static int
veriexec_raw_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)9198f9f36e4Smaxv veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
9208f9f36e4Smaxv     void *arg0, void *arg1, void *arg2, void *arg3)
9218f9f36e4Smaxv {
9228f9f36e4Smaxv 	int result;
9238f9f36e4Smaxv 	enum kauth_device_req req;
9248f9f36e4Smaxv 	struct veriexec_table_entry *vte;
9258f9f36e4Smaxv 
9268f9f36e4Smaxv 	result = KAUTH_RESULT_DENY;
9277cd8f2abSjoerg 	req = (enum kauth_device_req)(uintptr_t)arg0;
9288f9f36e4Smaxv 
9298f9f36e4Smaxv 	switch (action) {
9308f9f36e4Smaxv 	case KAUTH_DEVICE_RAWIO_SPEC: {
9318f9f36e4Smaxv 		struct vnode *vp, *bvp;
9328f9f36e4Smaxv 		int error;
9338f9f36e4Smaxv 
9348f9f36e4Smaxv 		if (req == KAUTH_REQ_DEVICE_RAWIO_SPEC_READ) {
9358f9f36e4Smaxv 			result = KAUTH_RESULT_DEFER;
9368f9f36e4Smaxv 			break;
9378f9f36e4Smaxv 		}
9388f9f36e4Smaxv 
9398f9f36e4Smaxv 		vp = arg1;
9408f9f36e4Smaxv 		KASSERT(vp != NULL);
9418f9f36e4Smaxv 
9428f9f36e4Smaxv 		/* Handle /dev/mem and /dev/kmem. */
9438f9f36e4Smaxv 		if (iskmemvp(vp)) {
9448f9f36e4Smaxv 			if (veriexec_strict < VERIEXEC_IPS)
9458f9f36e4Smaxv 				result = KAUTH_RESULT_DEFER;
9468f9f36e4Smaxv 
9478f9f36e4Smaxv 			break;
9488f9f36e4Smaxv 		}
9498f9f36e4Smaxv 
9508f9f36e4Smaxv 		error = rawdev_mounted(vp, &bvp);
9518f9f36e4Smaxv 		if (error == EINVAL) {
9528f9f36e4Smaxv 			result = KAUTH_RESULT_DEFER;
9538f9f36e4Smaxv 			break;
9548f9f36e4Smaxv 		}
9558f9f36e4Smaxv 
9568f9f36e4Smaxv 		/*
9578f9f36e4Smaxv 		 * XXX: See vfs_mountedon() comment in rawdev_mounted().
9588f9f36e4Smaxv 		 */
9598f9f36e4Smaxv 		vte = veriexec_table_lookup(bvp->v_mount);
9608f9f36e4Smaxv 		if (vte == NULL) {
9618f9f36e4Smaxv 			result = KAUTH_RESULT_DEFER;
9628f9f36e4Smaxv 			break;
9638f9f36e4Smaxv 		}
9648f9f36e4Smaxv 
9658f9f36e4Smaxv 		switch (veriexec_strict) {
9668f9f36e4Smaxv 		case VERIEXEC_LEARNING:
9678f9f36e4Smaxv 		case VERIEXEC_IDS:
9688f9f36e4Smaxv 			result = KAUTH_RESULT_DEFER;
9698f9f36e4Smaxv 
9708f9f36e4Smaxv 			rw_enter(&veriexec_op_lock, RW_WRITER);
9718f9f36e4Smaxv 			fileassoc_table_run(bvp->v_mount, veriexec_hook,
9728f9f36e4Smaxv 			    (fileassoc_cb_t)veriexec_file_purge_cb, NULL);
9738f9f36e4Smaxv 			rw_exit(&veriexec_op_lock);
9748f9f36e4Smaxv 
9758f9f36e4Smaxv 			break;
9768f9f36e4Smaxv 		case VERIEXEC_IPS:
9778f9f36e4Smaxv 			result = KAUTH_RESULT_DENY;
9788f9f36e4Smaxv 			break;
9798f9f36e4Smaxv 		case VERIEXEC_LOCKDOWN:
9808f9f36e4Smaxv 			result = KAUTH_RESULT_DENY;
9818f9f36e4Smaxv 			break;
9828f9f36e4Smaxv 		}
9838f9f36e4Smaxv 
9848f9f36e4Smaxv 		break;
9858f9f36e4Smaxv 		}
9868f9f36e4Smaxv 
9878f9f36e4Smaxv 	case KAUTH_DEVICE_RAWIO_PASSTHRU:
9888f9f36e4Smaxv 		/* XXX What can we do here? */
9898f9f36e4Smaxv 		if (veriexec_strict < VERIEXEC_IPS)
9908f9f36e4Smaxv 			result = KAUTH_RESULT_DEFER;
9918f9f36e4Smaxv 
9928f9f36e4Smaxv 		break;
9938f9f36e4Smaxv 
9948f9f36e4Smaxv 	default:
9958f9f36e4Smaxv 		result = KAUTH_RESULT_DEFER;
9968f9f36e4Smaxv 		break;
9978f9f36e4Smaxv 	}
9988f9f36e4Smaxv 
9998f9f36e4Smaxv 	return (result);
10008f9f36e4Smaxv }
10018f9f36e4Smaxv 
10028f9f36e4Smaxv /*
10038f9f36e4Smaxv  * Create a new Veriexec table.
10048f9f36e4Smaxv  */
10058f9f36e4Smaxv static struct veriexec_table_entry *
veriexec_table_add(struct lwp * l,struct mount * mp)10068f9f36e4Smaxv veriexec_table_add(struct lwp *l, struct mount *mp)
10078f9f36e4Smaxv {
10088f9f36e4Smaxv 	struct veriexec_table_entry *vte;
10098f9f36e4Smaxv 	u_char buf[16];
10108f9f36e4Smaxv 
10118f9f36e4Smaxv 	vte = kmem_zalloc(sizeof(*vte), KM_SLEEP);
10128f9f36e4Smaxv 	mount_setspecific(mp, veriexec_mountspecific_key, vte);
10138f9f36e4Smaxv 
10148f9f36e4Smaxv 	snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++);
10158f9f36e4Smaxv 	sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
10168f9f36e4Smaxv 		       0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL,
10178f9f36e4Smaxv 		       0, CTL_CREATE, CTL_EOL);
10188f9f36e4Smaxv 
10198f9f36e4Smaxv 	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
10208f9f36e4Smaxv 		       CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
10218f9f36e4Smaxv 		       NULL, NULL, 0, mp->mnt_stat.f_mntonname,
10228f9f36e4Smaxv 		       0, CTL_CREATE, CTL_EOL);
10238f9f36e4Smaxv 	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
10248f9f36e4Smaxv 		       CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
10258f9f36e4Smaxv 		       NULL, NULL, 0, mp->mnt_stat.f_fstypename,
10268f9f36e4Smaxv 		       0, CTL_CREATE, CTL_EOL);
10278f9f36e4Smaxv 	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
10288f9f36e4Smaxv 		       CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
10298f9f36e4Smaxv 		       NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
10308f9f36e4Smaxv 
10318f9f36e4Smaxv 	return (vte);
10328f9f36e4Smaxv }
10338f9f36e4Smaxv 
10348f9f36e4Smaxv /*
10358f9f36e4Smaxv  * Add a file to be monitored by Veriexec.
10368f9f36e4Smaxv  *
103784de7c75Salnsn  * Expected elements in dict:
103884de7c75Salnsn  *     file, fp, fp-type, entry-type, keep-filename, eval-on-load.
10398f9f36e4Smaxv  */
10408f9f36e4Smaxv int
veriexec_file_add(struct lwp * l,prop_dictionary_t dict)10418f9f36e4Smaxv veriexec_file_add(struct lwp *l, prop_dictionary_t dict)
10428f9f36e4Smaxv {
10438f9f36e4Smaxv 	struct veriexec_table_entry *vte;
10448baf0885Smaxv 	struct veriexec_file_entry *vfe = NULL;
10454e08125eSpgoyette 	struct veriexec_file_entry *ovfe;
10468f9f36e4Smaxv 	struct vnode *vp;
10478f9f36e4Smaxv 	const char *file, *fp_type;
10488f9f36e4Smaxv 	int error;
10494e08125eSpgoyette 	bool ignore_dup = false;
10508f9f36e4Smaxv 
105186a11f6aSthorpej 	if (!prop_dictionary_get_string(dict, "file", &file))
10528f9f36e4Smaxv 		return (EINVAL);
10538f9f36e4Smaxv 
10548f9f36e4Smaxv 	error = namei_simple_kernel(file, NSM_FOLLOW_NOEMULROOT, &vp);
10558f9f36e4Smaxv 	if (error)
10568f9f36e4Smaxv 		return (error);
10578f9f36e4Smaxv 
10588f9f36e4Smaxv 	/* Add only regular files. */
10598f9f36e4Smaxv 	if (vp->v_type != VREG) {
10608f9f36e4Smaxv 		log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n",
10618f9f36e4Smaxv 		    file);
10628f9f36e4Smaxv 		error = EBADF;
10638f9f36e4Smaxv 		goto out;
10648f9f36e4Smaxv 	}
10658f9f36e4Smaxv 
10668f9f36e4Smaxv 	vfe = kmem_zalloc(sizeof(*vfe), KM_SLEEP);
10678f9f36e4Smaxv 	rw_init(&vfe->lock);
10688f9f36e4Smaxv 
10698f9f36e4Smaxv 	/* Lookup fingerprint hashing algorithm. */
107052fd37d2Sthorpej 	fp_type = prop_string_value(prop_dictionary_get(dict, "fp-type"));
10718f9f36e4Smaxv 	if ((vfe->ops = veriexec_fpops_lookup(fp_type)) == NULL) {
10728f9f36e4Smaxv 		log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
10738f9f36e4Smaxv 		    "`%s' for file `%s'.\n", fp_type, file);
10748f9f36e4Smaxv 		error = EOPNOTSUPP;
1075eb2cf93dSpgoyette 		goto out;
10768f9f36e4Smaxv 	}
10778f9f36e4Smaxv 
10788f9f36e4Smaxv 	if (prop_data_size(prop_dictionary_get(dict, "fp")) !=
10798f9f36e4Smaxv 	    vfe->ops->hash_len) {
10808f9f36e4Smaxv 		log(LOG_ERR, "Veriexec: Bad fingerprint length for `%s'.\n",
10818f9f36e4Smaxv 		    file);
10828f9f36e4Smaxv 		error = EINVAL;
1083eb2cf93dSpgoyette 		goto out;
10848f9f36e4Smaxv 	}
10858f9f36e4Smaxv 
10868f9f36e4Smaxv 	vfe->fp = kmem_alloc(vfe->ops->hash_len, KM_SLEEP);
108752fd37d2Sthorpej 	memcpy(vfe->fp, prop_data_value(prop_dictionary_get(dict, "fp")),
10888f9f36e4Smaxv 	    vfe->ops->hash_len);
10898f9f36e4Smaxv 
10908f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_WRITER);
10918f9f36e4Smaxv 
10928f9f36e4Smaxv 	/* Continue entry initialization. */
10938f9f36e4Smaxv 	if (prop_dictionary_get_uint8(dict, "entry-type", &vfe->type) == FALSE)
10948f9f36e4Smaxv 		vfe->type = 0;
10958f9f36e4Smaxv 	else {
10968f9f36e4Smaxv 		uint8_t extra_flags;
10978f9f36e4Smaxv 
10988f9f36e4Smaxv 		extra_flags = vfe->type & ~(VERIEXEC_DIRECT |
10998f9f36e4Smaxv 		    VERIEXEC_INDIRECT | VERIEXEC_FILE | VERIEXEC_UNTRUSTED);
11008f9f36e4Smaxv 		if (extra_flags) {
11018f9f36e4Smaxv 			log(LOG_NOTICE, "Veriexec: Contaminated flags `0x%x' "
11028f9f36e4Smaxv 			    "for `%s', skipping.\n", extra_flags, file);
11038f9f36e4Smaxv 			error = EINVAL;
11048f9f36e4Smaxv 			goto unlock_out;
11058f9f36e4Smaxv 		}
11068f9f36e4Smaxv 	}
11078f9f36e4Smaxv 	if (!(vfe->type & (VERIEXEC_DIRECT | VERIEXEC_INDIRECT |
11088f9f36e4Smaxv 	    VERIEXEC_FILE)))
11098f9f36e4Smaxv 		vfe->type |= VERIEXEC_DIRECT;
11108f9f36e4Smaxv 
11118f9f36e4Smaxv 	vfe->status = FINGERPRINT_NOTEVAL;
11128f9f36e4Smaxv 	if (prop_bool_true(prop_dictionary_get(dict, "keep-filename"))) {
11135ec80d6cSchristos 		vfe->filename = kmem_strdupsize(file, &vfe->filename_len,
11145ec80d6cSchristos 		    KM_SLEEP);
11158f9f36e4Smaxv 	} else
11168f9f36e4Smaxv 		vfe->filename = NULL;
11178f9f36e4Smaxv 
11188f9f36e4Smaxv 	if (prop_bool_true(prop_dictionary_get(dict, "eval-on-load")) ||
11198f9f36e4Smaxv 	    (vfe->type & VERIEXEC_UNTRUSTED)) {
1120bc430abcSmaxv 		u_char status;
11218f9f36e4Smaxv 
1122bc430abcSmaxv 		error = veriexec_fp_status(l, vp, VERIEXEC_FILE_UNLOCKED,
1123bc430abcSmaxv 		    vfe, &status);
1124bc430abcSmaxv 		if (error)
11258f9f36e4Smaxv 			goto unlock_out;
1126bc430abcSmaxv 		vfe->status = status;
11278f9f36e4Smaxv 	}
11288f9f36e4Smaxv 
11294e08125eSpgoyette 	/*
11304e08125eSpgoyette 	 * If we already have an entry for this file, and it matches
11314e08125eSpgoyette 	 * the new entry exactly (except for the filename, which may
11324e08125eSpgoyette 	 * hard-linked!), we just ignore the new entry.  If the new
11334e08125eSpgoyette 	 * entry differs, report the error.
11344e08125eSpgoyette 	 */
11354e08125eSpgoyette 	if ((ovfe = veriexec_get(vp)) != NULL) {
11364e08125eSpgoyette 		error = EEXIST;
11374e08125eSpgoyette 		if (vfe->type == ovfe->type &&
11384e08125eSpgoyette 		    vfe->status == ovfe->status &&
11394e08125eSpgoyette 		    vfe->ops == ovfe->ops &&
11404e08125eSpgoyette 		    memcmp(vfe->fp, ovfe->fp, vfe->ops->hash_len) == 0)
11414e08125eSpgoyette 			ignore_dup = true;
11424e08125eSpgoyette 		goto unlock_out;
11434e08125eSpgoyette 	}
11444e08125eSpgoyette 
11458f9f36e4Smaxv 	vte = veriexec_table_lookup(vp->v_mount);
11468f9f36e4Smaxv 	if (vte == NULL)
11478f9f36e4Smaxv 		vte = veriexec_table_add(l, vp->v_mount);
11488f9f36e4Smaxv 
11498f9f36e4Smaxv 	/* XXX if we bail below this, we might want to gc newly created vtes. */
11508f9f36e4Smaxv 
11518f9f36e4Smaxv 	error = fileassoc_add(vp, veriexec_hook, vfe);
11528f9f36e4Smaxv 	if (error)
11538f9f36e4Smaxv 		goto unlock_out;
11548f9f36e4Smaxv 
11558f9f36e4Smaxv 	vte->vte_count++;
11568f9f36e4Smaxv 
11578f9f36e4Smaxv 	veriexec_file_report(NULL, "New entry.", file, NULL, REPORT_DEBUG);
11588f9f36e4Smaxv 	veriexec_bypass = 0;
11598f9f36e4Smaxv 
11608f9f36e4Smaxv   unlock_out:
11618f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
11628f9f36e4Smaxv 
11638f9f36e4Smaxv   out:
11648f9f36e4Smaxv 	vrele(vp);
11658f9f36e4Smaxv 	if (error)
11668f9f36e4Smaxv 		veriexec_file_free(vfe);
11678f9f36e4Smaxv 
11684e08125eSpgoyette 	if (ignore_dup && error == EEXIST)
11694e08125eSpgoyette 		error = 0;
11704e08125eSpgoyette 
11718f9f36e4Smaxv 	return (error);
11728f9f36e4Smaxv }
11738f9f36e4Smaxv 
11748f9f36e4Smaxv int
veriexec_table_delete(struct lwp * l,struct mount * mp)11757e91eb6dSmaxv veriexec_table_delete(struct lwp *l, struct mount *mp)
11767e91eb6dSmaxv {
11778f9f36e4Smaxv 	struct veriexec_table_entry *vte;
11788f9f36e4Smaxv 
11798f9f36e4Smaxv 	vte = veriexec_table_lookup(mp);
11808f9f36e4Smaxv 	if (vte == NULL)
11818f9f36e4Smaxv 		return (ENOENT);
11828f9f36e4Smaxv 
11838f9f36e4Smaxv 	veriexec_mountspecific_dtor(vte);
11848f9f36e4Smaxv 	mount_setspecific(mp, veriexec_mountspecific_key, NULL);
11858f9f36e4Smaxv 
11868f9f36e4Smaxv 	return (fileassoc_table_clear(mp, veriexec_hook));
11878f9f36e4Smaxv }
11888f9f36e4Smaxv 
11898f9f36e4Smaxv int
veriexec_file_delete(struct lwp * l,struct vnode * vp)11907e91eb6dSmaxv veriexec_file_delete(struct lwp *l, struct vnode *vp)
11917e91eb6dSmaxv {
11928f9f36e4Smaxv 	struct veriexec_table_entry *vte;
11938f9f36e4Smaxv 	int error;
11948f9f36e4Smaxv 
11958f9f36e4Smaxv 	vte = veriexec_table_lookup(vp->v_mount);
11968f9f36e4Smaxv 	if (vte == NULL)
11978f9f36e4Smaxv 		return (ENOENT);
11988f9f36e4Smaxv 
11998f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_WRITER);
12008f9f36e4Smaxv 	error = fileassoc_clear(vp, veriexec_hook);
12018f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
12027e91eb6dSmaxv 	if (!error) {
12037e91eb6dSmaxv 		KASSERT(vte->vte_count > 0);
12048f9f36e4Smaxv 		vte->vte_count--;
12057e91eb6dSmaxv 	}
12068f9f36e4Smaxv 
12078f9f36e4Smaxv 	return (error);
12088f9f36e4Smaxv }
12098f9f36e4Smaxv 
12108f9f36e4Smaxv /*
12118f9f36e4Smaxv  * Convert Veriexec entry data to a dictionary readable by userland tools.
12128f9f36e4Smaxv  */
12138f9f36e4Smaxv static void
veriexec_file_convert(struct veriexec_file_entry * vfe,prop_dictionary_t rdict)12148f9f36e4Smaxv veriexec_file_convert(struct veriexec_file_entry *vfe, prop_dictionary_t rdict)
12158f9f36e4Smaxv {
12168f9f36e4Smaxv 	if (vfe->filename)
12178f9f36e4Smaxv 		prop_dictionary_set(rdict, "file",
121871496056Sthorpej 		    prop_string_create_copy(vfe->filename));
12198f9f36e4Smaxv 	prop_dictionary_set_uint8(rdict, "entry-type", vfe->type);
12208f9f36e4Smaxv 	prop_dictionary_set_uint8(rdict, "status", vfe->status);
12218f9f36e4Smaxv 	prop_dictionary_set(rdict, "fp-type",
122271496056Sthorpej 	    prop_string_create_copy(vfe->ops->type));
12238f9f36e4Smaxv 	prop_dictionary_set(rdict, "fp",
122452fd37d2Sthorpej 	    prop_data_create_copy(vfe->fp, vfe->ops->hash_len));
12258f9f36e4Smaxv }
12268f9f36e4Smaxv 
12278f9f36e4Smaxv int
veriexec_convert(struct vnode * vp,prop_dictionary_t rdict)12288f9f36e4Smaxv veriexec_convert(struct vnode *vp, prop_dictionary_t rdict)
12298f9f36e4Smaxv {
12308f9f36e4Smaxv 	struct veriexec_file_entry *vfe;
12318f9f36e4Smaxv 
12328f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
12338f9f36e4Smaxv 
12348f9f36e4Smaxv 	vfe = veriexec_get(vp);
12358f9f36e4Smaxv 	if (vfe == NULL) {
12368f9f36e4Smaxv 		rw_exit(&veriexec_op_lock);
12378f9f36e4Smaxv 		return (ENOENT);
12388f9f36e4Smaxv 	}
12398f9f36e4Smaxv 
12408f9f36e4Smaxv 	rw_enter(&vfe->lock, RW_READER);
12418f9f36e4Smaxv 	veriexec_file_convert(vfe, rdict);
12428f9f36e4Smaxv 	rw_exit(&vfe->lock);
124393f9af89Smaxv 
12448f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
12458f9f36e4Smaxv 	return (0);
12468f9f36e4Smaxv }
12478f9f36e4Smaxv 
12488f9f36e4Smaxv int
veriexec_unmountchk(struct mount * mp)12498f9f36e4Smaxv veriexec_unmountchk(struct mount *mp)
12508f9f36e4Smaxv {
12518f9f36e4Smaxv 	int error;
12528f9f36e4Smaxv 
12538f9f36e4Smaxv 	if ((veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
12548f9f36e4Smaxv 	    || doing_shutdown)
12558f9f36e4Smaxv 		return (0);
12568f9f36e4Smaxv 
12578f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
12588f9f36e4Smaxv 
12598f9f36e4Smaxv 	switch (veriexec_strict) {
12608f9f36e4Smaxv 	case VERIEXEC_LEARNING:
12618f9f36e4Smaxv 		error = 0;
12628f9f36e4Smaxv 		break;
12638f9f36e4Smaxv 
12648f9f36e4Smaxv 	case VERIEXEC_IDS:
12658f9f36e4Smaxv 		if (veriexec_table_lookup(mp) != NULL) {
12668f9f36e4Smaxv 			log(LOG_INFO, "Veriexec: IDS mode, allowing unmount "
12678f9f36e4Smaxv 			    "of \"%s\".\n", mp->mnt_stat.f_mntonname);
12688f9f36e4Smaxv 		}
12698f9f36e4Smaxv 
12708f9f36e4Smaxv 		error = 0;
12718f9f36e4Smaxv 		break;
12728f9f36e4Smaxv 
12738f9f36e4Smaxv 	case VERIEXEC_IPS: {
12748f9f36e4Smaxv 		struct veriexec_table_entry *vte;
12758f9f36e4Smaxv 
12768f9f36e4Smaxv 		vte = veriexec_table_lookup(mp);
12778f9f36e4Smaxv 		if ((vte != NULL) && (vte->vte_count > 0)) {
12788f9f36e4Smaxv 			log(LOG_ALERT, "Veriexec: IPS mode, preventing"
12798f9f36e4Smaxv 			    " unmount of \"%s\" with monitored files.\n",
12808f9f36e4Smaxv 			    mp->mnt_stat.f_mntonname);
12818f9f36e4Smaxv 
12828f9f36e4Smaxv 			error = EPERM;
12838f9f36e4Smaxv 		} else
12848f9f36e4Smaxv 			error = 0;
12858f9f36e4Smaxv 		break;
12868f9f36e4Smaxv 		}
12878f9f36e4Smaxv 
12888f9f36e4Smaxv 	case VERIEXEC_LOCKDOWN:
12898f9f36e4Smaxv 	default:
12908f9f36e4Smaxv 		log(LOG_ALERT, "Veriexec: Lockdown mode, preventing unmount "
12918f9f36e4Smaxv 		    "of \"%s\".\n", mp->mnt_stat.f_mntonname);
12928f9f36e4Smaxv 		error = EPERM;
12938f9f36e4Smaxv 		break;
12948f9f36e4Smaxv 	}
12958f9f36e4Smaxv 
12968f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
12978f9f36e4Smaxv 	return (error);
12988f9f36e4Smaxv }
12998f9f36e4Smaxv 
13008f9f36e4Smaxv int
veriexec_openchk(struct lwp * l,struct vnode * vp,const char * path,int fmode)13018f9f36e4Smaxv veriexec_openchk(struct lwp *l, struct vnode *vp, const char *path, int fmode)
13028f9f36e4Smaxv {
13038f9f36e4Smaxv 	struct veriexec_file_entry *vfe = NULL;
13048f9f36e4Smaxv 	int error = 0;
13058f9f36e4Smaxv 
13068f9f36e4Smaxv 	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
13078f9f36e4Smaxv 		return 0;
13088f9f36e4Smaxv 
13098f9f36e4Smaxv 	if (vp == NULL) {
13108f9f36e4Smaxv 		/* If no creation requested, let this fail normally. */
13118f9f36e4Smaxv 		if (!(fmode & O_CREAT))
13128f9f36e4Smaxv 			goto out;
13138f9f36e4Smaxv 
13148f9f36e4Smaxv 		/* Lockdown mode: Prevent creation of new files. */
13158f9f36e4Smaxv 		if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
13168f9f36e4Smaxv 			log(LOG_ALERT, "Veriexec: Preventing new file "
13178f9f36e4Smaxv 			    "creation in `%s'.\n", path);
13188f9f36e4Smaxv 			error = EPERM;
13198f9f36e4Smaxv 		}
13208f9f36e4Smaxv 
13218f9f36e4Smaxv 		goto out;
13228f9f36e4Smaxv 	}
13238f9f36e4Smaxv 
13248f9f36e4Smaxv 	rw_enter(&veriexec_op_lock, RW_READER);
13258f9f36e4Smaxv 	error = veriexec_file_verify(l, vp, path, VERIEXEC_FILE,
1326c35516e8Smaxv 				     VERIEXEC_FILE_LOCKED, &vfe);
13278f9f36e4Smaxv 
13288f9f36e4Smaxv 	if (error) {
13298f9f36e4Smaxv 		rw_exit(&veriexec_op_lock);
13308f9f36e4Smaxv 		goto out;
13318f9f36e4Smaxv 	}
13328f9f36e4Smaxv 
13338f9f36e4Smaxv 	if ((vfe != NULL) && ((fmode & FWRITE) || (fmode & O_TRUNC))) {
13348f9f36e4Smaxv 		veriexec_file_report(vfe, "Write access request.", path, l,
13358f9f36e4Smaxv 		    REPORT_ALWAYS | REPORT_ALARM);
13368f9f36e4Smaxv 
13378f9f36e4Smaxv 		/* IPS mode: Deny write access to monitored files. */
13388f9f36e4Smaxv 		if (veriexec_strict >= VERIEXEC_IPS)
13398f9f36e4Smaxv 			error = EPERM;
13408f9f36e4Smaxv 		else
13418f9f36e4Smaxv 			veriexec_file_purge(vfe, VERIEXEC_LOCKED);
13428f9f36e4Smaxv 	}
13438f9f36e4Smaxv 
13448f9f36e4Smaxv 	if (vfe != NULL)
13458f9f36e4Smaxv 		rw_exit(&vfe->lock);
13468f9f36e4Smaxv 
13478f9f36e4Smaxv 	rw_exit(&veriexec_op_lock);
13488f9f36e4Smaxv  out:
13498f9f36e4Smaxv 	return (error);
13508f9f36e4Smaxv }
13518f9f36e4Smaxv 
13528f9f36e4Smaxv static void
veriexec_file_dump(struct veriexec_file_entry * vfe,prop_array_t entries)13538f9f36e4Smaxv veriexec_file_dump(struct veriexec_file_entry *vfe, prop_array_t entries)
13548f9f36e4Smaxv {
13558f9f36e4Smaxv 	prop_dictionary_t entry;
13568f9f36e4Smaxv 
13578f9f36e4Smaxv 	/* If we don't have a filename, this is meaningless. */
13588f9f36e4Smaxv 	if (vfe->filename == NULL)
13598f9f36e4Smaxv 		return;
13608f9f36e4Smaxv 
13618f9f36e4Smaxv 	entry = prop_dictionary_create();
13628f9f36e4Smaxv 
13638f9f36e4Smaxv 	veriexec_file_convert(vfe, entry);
13648f9f36e4Smaxv 
13658f9f36e4Smaxv 	prop_array_add(entries, entry);
13668f9f36e4Smaxv }
13678f9f36e4Smaxv 
13688f9f36e4Smaxv int
veriexec_dump(struct lwp * l,prop_array_t rarray)13698f9f36e4Smaxv veriexec_dump(struct lwp *l, prop_array_t rarray)
13708f9f36e4Smaxv {
1371f2953622Shannken 	mount_iterator_t *iter;
1372f2953622Shannken 	struct mount *mp;
13738f9f36e4Smaxv 
1374f2953622Shannken 	mountlist_iterator_init(&iter);
1375f2953622Shannken 	while ((mp = mountlist_iterator_next(iter)) != NULL) {
13768f9f36e4Smaxv 		fileassoc_table_run(mp, veriexec_hook,
13778f9f36e4Smaxv 		    (fileassoc_cb_t)veriexec_file_dump, rarray);
13788f9f36e4Smaxv 	}
1379f2953622Shannken 	mountlist_iterator_destroy(iter);
13808f9f36e4Smaxv 
13818f9f36e4Smaxv 	return (0);
13828f9f36e4Smaxv }
13838f9f36e4Smaxv 
13848f9f36e4Smaxv int
veriexec_flush(struct lwp * l)13858f9f36e4Smaxv veriexec_flush(struct lwp *l)
13868f9f36e4Smaxv {
1387f2953622Shannken 	mount_iterator_t *iter;
1388f2953622Shannken 	struct mount *mp;
13898f9f36e4Smaxv 	int error = 0;
13908f9f36e4Smaxv 
1391f2953622Shannken 	mountlist_iterator_init(&iter);
1392f2953622Shannken 	while ((mp = mountlist_iterator_next(iter)) != NULL) {
13938f9f36e4Smaxv 		int lerror;
13948f9f36e4Smaxv 
13958f9f36e4Smaxv 		lerror = veriexec_table_delete(l, mp);
13968f9f36e4Smaxv 		if (lerror && lerror != ENOENT)
13978f9f36e4Smaxv 			error = lerror;
13988f9f36e4Smaxv 	}
1399f2953622Shannken 	mountlist_iterator_destroy(iter);
14008f9f36e4Smaxv 
14018f9f36e4Smaxv 	return (error);
14028f9f36e4Smaxv }
1403