1/*	$NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $	*/
2
3/*-
4 * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
5 * Copyright (c) 2005, 2006 Brett Lymn <blymn@NetBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the authors may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $");
33
34#include "opt_veriexec.h"
35
36#include <sys/param.h>
37#include <sys/mount.h>
38#include <sys/kmem.h>
39#include <sys/vnode.h>
40#include <sys/namei.h>
41#include <sys/once.h>
42#include <sys/proc.h>
43#include <sys/rwlock.h>
44#include <sys/syslog.h>
45#include <sys/sysctl.h>
46#include <sys/inttypes.h>
47#include <sys/verified_exec.h>
48#include <sys/sha1.h>
49#include <sys/sha2.h>
50#include <sys/rmd160.h>
51#include <sys/md5.h>
52#include <sys/fileassoc.h>
53#include <sys/kauth.h>
54#include <sys/conf.h>
55#include <miscfs/specfs/specdev.h>
56#include <prop/proplib.h>
57#include <sys/fcntl.h>
58
59/* Readable values for veriexec_file_report(). */
60#define	REPORT_ALWAYS		0x01	/* Always print */
61#define	REPORT_VERBOSE		0x02	/* Print when verbose >= 1 */
62#define	REPORT_DEBUG		0x04	/* Print when verbose >= 2 (debug) */
63#define	REPORT_PANIC		0x08	/* Call panic() */
64#define	REPORT_ALARM		0x10	/* Alarm - also print pid/uid/.. */
65#define	REPORT_LOGMASK		(REPORT_ALWAYS|REPORT_VERBOSE|REPORT_DEBUG)
66
67/* state of locking for veriexec_file_verify */
68#define VERIEXEC_UNLOCKED	0x00	/* Nothing locked, callee does it */
69#define VERIEXEC_LOCKED		0x01	/* Global op lock held */
70
71/* state of file locking for veriexec_file_verify */
72#define VERIEXEC_FILE_UNLOCKED	0x02	/* Nothing locked, callee does it */
73#define VERIEXEC_FILE_LOCKED	0x04	/* File locked */
74
75#define VERIEXEC_RW_UPGRADE(lock)	while((rw_tryupgrade(lock)) == 0){};
76
77struct veriexec_fpops {
78	const char *type;
79	size_t hash_len;
80	size_t context_size;
81	veriexec_fpop_init_t init;
82	veriexec_fpop_update_t update;
83	veriexec_fpop_final_t final;
84	LIST_ENTRY(veriexec_fpops) entries;
85};
86
87/* Veriexec per-file entry data. */
88struct veriexec_file_entry {
89	krwlock_t lock;				/* r/w lock */
90	u_char *filename;			/* File name. */
91	u_char type;				/* Entry type. */
92	u_char status;				/* Evaluation status. */
93	u_char *fp;				/* Fingerprint. */
94	struct veriexec_fpops *ops;		/* Fingerprint ops vector*/
95	size_t filename_len;			/* Length of filename. */
96};
97
98/* Veriexec per-table data. */
99struct veriexec_table_entry {
100	uint64_t vte_count;			/* Number of Veriexec entries. */
101	const struct sysctlnode *vte_node;
102};
103
104static int veriexec_verbose;
105static int veriexec_strict;
106static int veriexec_bypass = 1;
107
108static char *veriexec_fp_names = NULL;
109static size_t veriexec_name_max = 0;
110
111static const struct sysctlnode *veriexec_count_node;
112
113static fileassoc_t veriexec_hook;
114static specificdata_key_t veriexec_mountspecific_key;
115
116static LIST_HEAD(, veriexec_fpops) veriexec_fpops_list =
117	LIST_HEAD_INITIALIZER(veriexec_fpops_list);
118
119static int veriexec_raw_cb(kauth_cred_t, kauth_action_t, void *,
120    void *, void *, void *, void *);
121static struct veriexec_fpops *veriexec_fpops_lookup(const char *);
122static void veriexec_file_free(struct veriexec_file_entry *);
123
124static unsigned int veriexec_tablecount = 0;
125
126/*
127 * Veriexec operations global lock - most ops hold this as a read
128 * lock, it is upgraded to a write lock when destroying veriexec file
129 * table entries.
130 */
131static krwlock_t veriexec_op_lock;
132
133/*
134 * Sysctl helper routine for Veriexec.
135 */
136static int
137sysctl_kern_veriexec_algorithms(SYSCTLFN_ARGS)
138{
139	size_t len;
140	int error;
141	const char *p;
142
143	if (newp != NULL)
144		return EPERM;
145
146	if (namelen != 0)
147		return EINVAL;
148
149	p = veriexec_fp_names == NULL ? "" : veriexec_fp_names;
150
151	len = strlen(p) + 1;
152
153	if (*oldlenp < len && oldp)
154		return ENOMEM;
155
156	if (oldp && (error = copyout(p, oldp, len)) != 0)
157		return error;
158
159	*oldlenp = len;
160	return 0;
161}
162
163static int
164sysctl_kern_veriexec_strict(SYSCTLFN_ARGS)
165{
166	struct sysctlnode node;
167	int error, newval;
168
169	node = *rnode;
170	node.sysctl_data = &newval;
171
172	newval = veriexec_strict;
173	error = sysctl_lookup(SYSCTLFN_CALL(&node));
174	if (error || newp == NULL)
175		return error;
176
177	if (newval < veriexec_strict)
178		return EPERM;
179
180	veriexec_strict = newval;
181
182	return 0;
183}
184
185SYSCTL_SETUP(sysctl_kern_veriexec_setup, "sysctl kern.veriexec setup")
186{
187	const struct sysctlnode *rnode = NULL;
188
189	sysctl_createv(clog, 0, NULL, &rnode,
190		       CTLFLAG_PERMANENT,
191		       CTLTYPE_NODE, "veriexec",
192		       SYSCTL_DESCR("Veriexec"),
193		       NULL, 0, NULL, 0,
194		       CTL_KERN, CTL_CREATE, CTL_EOL);
195
196	sysctl_createv(clog, 0, &rnode, NULL,
197		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
198		       CTLTYPE_INT, "verbose",
199		       SYSCTL_DESCR("Veriexec verbose level"),
200		       NULL, 0, &veriexec_verbose, 0,
201		       CTL_CREATE, CTL_EOL);
202	sysctl_createv(clog, 0, &rnode, NULL,
203		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
204		       CTLTYPE_INT, "strict",
205		       SYSCTL_DESCR("Veriexec strict level"),
206		       sysctl_kern_veriexec_strict, 0, NULL, 0,
207		       CTL_CREATE, CTL_EOL);
208	sysctl_createv(clog, 0, &rnode, NULL,
209		       CTLFLAG_PERMANENT,
210		       CTLTYPE_STRING, "algorithms",
211		       SYSCTL_DESCR("Veriexec supported hashing "
212				    "algorithms"),
213		       sysctl_kern_veriexec_algorithms, 0, NULL, 0,
214		       CTL_CREATE, CTL_EOL);
215	sysctl_createv(clog, 0, &rnode, &veriexec_count_node,
216		       CTLFLAG_PERMANENT,
217		       CTLTYPE_NODE, "count",
218		       SYSCTL_DESCR("Number of fingerprints on mount(s)"),
219		       NULL, 0, NULL, 0,
220		       CTL_CREATE, CTL_EOL);
221}
222
223/*
224 * Add ops to the fingerprint ops vector list.
225 */
226int
227veriexec_fpops_add(const char *fp_type, size_t hash_len, size_t ctx_size,
228    veriexec_fpop_init_t init, veriexec_fpop_update_t update,
229    veriexec_fpop_final_t final)
230{
231	struct veriexec_fpops *ops;
232
233	KASSERT(init != NULL);
234	KASSERT(update != NULL);
235	KASSERT(final != NULL);
236	KASSERT(hash_len != 0);
237	KASSERT(ctx_size != 0);
238	KASSERT(fp_type != NULL);
239
240	if (veriexec_fpops_lookup(fp_type) != NULL)
241		return (EEXIST);
242
243	ops = kmem_alloc(sizeof(*ops), KM_SLEEP);
244	ops->type = fp_type;
245	ops->hash_len = hash_len;
246	ops->context_size = ctx_size;
247	ops->init = init;
248	ops->update = update;
249	ops->final = final;
250
251	LIST_INSERT_HEAD(&veriexec_fpops_list, ops, entries);
252
253	/*
254	 * If we don't have space for any names, allocate enough for six
255	 * which should be sufficient. (it's also enough for all algorithms
256	 * we can support at the moment)
257	 */
258	if (veriexec_fp_names == NULL) {
259		veriexec_name_max = 64;
260		veriexec_fp_names = kmem_zalloc(veriexec_name_max, KM_SLEEP);
261	}
262
263	/*
264	 * If we're running out of space for storing supported algorithms,
265	 * extend the buffer with space for four names.
266	 */
267	while (veriexec_name_max - (strlen(veriexec_fp_names) + 1) <
268	    strlen(fp_type)) {
269		char *newp;
270		unsigned int new_max;
271
272		/* Add space for four algorithm names. */
273		new_max = veriexec_name_max + 64;
274		newp = kmem_zalloc(new_max, KM_SLEEP);
275		strlcpy(newp, veriexec_fp_names, new_max);
276		kmem_free(veriexec_fp_names, veriexec_name_max);
277		veriexec_fp_names = newp;
278		veriexec_name_max = new_max;
279	}
280
281	if (*veriexec_fp_names != '\0')
282		strlcat(veriexec_fp_names, " ", veriexec_name_max);
283
284	strlcat(veriexec_fp_names, fp_type, veriexec_name_max);
285
286	return (0);
287}
288
289static void
290veriexec_mountspecific_dtor(void *v)
291{
292	struct veriexec_table_entry *vte = v;
293
294	if (vte == NULL) {
295		return;
296	}
297	sysctl_free(__UNCONST(vte->vte_node));
298	veriexec_tablecount--;
299	kmem_free(vte, sizeof(*vte));
300}
301
302static int
303veriexec_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
304    void *arg0, void *arg1, void *arg2, void *arg3)
305{
306	int result;
307	enum kauth_system_req req;
308
309	if (action != KAUTH_SYSTEM_VERIEXEC)
310		return KAUTH_RESULT_DEFER;
311
312	result = KAUTH_RESULT_DEFER;
313	req = (enum kauth_system_req)(uintptr_t)arg0;
314
315	if (req == KAUTH_REQ_SYSTEM_VERIEXEC_MODIFY &&
316	    veriexec_strict > VERIEXEC_LEARNING) {
317		log(LOG_WARNING, "Veriexec: Strict mode, modifying "
318		    "tables not permitted.\n");
319
320		result = KAUTH_RESULT_DENY;
321	}
322
323	return result;
324}
325
326/*
327 * Initialise Veriexec.
328 */
329void
330veriexec_init(void)
331{
332	int error;
333
334	/* Register a fileassoc for Veriexec. */
335	error = fileassoc_register("veriexec",
336	    (fileassoc_cleanup_cb_t)veriexec_file_free, &veriexec_hook);
337	if (error)
338		panic("Veriexec: Can't register fileassoc: error=%d", error);
339
340	/* Register listener to handle raw disk access. */
341	if (kauth_listen_scope(KAUTH_SCOPE_DEVICE, veriexec_raw_cb, NULL) ==
342	    NULL)
343		panic("Veriexec: Can't listen on device scope");
344
345	error = mount_specific_key_create(&veriexec_mountspecific_key,
346	    veriexec_mountspecific_dtor);
347	if (error)
348		panic("Veriexec: Can't create mountspecific key");
349
350	if (kauth_listen_scope(KAUTH_SCOPE_SYSTEM, veriexec_listener_cb,
351	    NULL) == NULL)
352		panic("Veriexec: Can't listen on system scope");
353
354	rw_init(&veriexec_op_lock);
355
356#define	FPOPS_ADD(a, b, c, d, e, f)			\
357	veriexec_fpops_add(a, b, c,			\
358	    __FPTRCAST(veriexec_fpop_init_t, d),	\
359	    __FPTRCAST(veriexec_fpop_update_t, e),	\
360	    __FPTRCAST(veriexec_fpop_final_t, f))
361
362#ifdef VERIFIED_EXEC_FP_SHA256
363	FPOPS_ADD("SHA256", SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX),
364	    SHA256_Init, SHA256_Update, SHA256_Final);
365#endif /* VERIFIED_EXEC_FP_SHA256 */
366
367#ifdef VERIFIED_EXEC_FP_SHA384
368	FPOPS_ADD("SHA384", SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX),
369	    SHA384_Init, SHA384_Update, SHA384_Final);
370#endif /* VERIFIED_EXEC_FP_SHA384 */
371
372#ifdef VERIFIED_EXEC_FP_SHA512
373	FPOPS_ADD("SHA512", SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX),
374	    SHA512_Init, SHA512_Update, SHA512_Final);
375#endif /* VERIFIED_EXEC_FP_SHA512 */
376
377#undef FPOPS_ADD
378}
379
380static struct veriexec_fpops *
381veriexec_fpops_lookup(const char *name)
382{
383	struct veriexec_fpops *ops;
384
385	if (name == NULL)
386		return (NULL);
387
388	LIST_FOREACH(ops, &veriexec_fpops_list, entries) {
389		if (strcasecmp(name, ops->type) == 0)
390			return (ops);
391	}
392
393	return (NULL);
394}
395
396/*
397 * Calculate fingerprint. Information on hash length and routines used is
398 * extracted from veriexec_hash_list according to the hash type.
399 *
400 * NOTE: vfe is assumed to be locked for writing on entry.
401 */
402static int
403veriexec_fp_calc(struct lwp *l, struct vnode *vp, int file_lock_state,
404    struct veriexec_file_entry *vfe, u_char *fp)
405{
406	struct vattr va;
407	void *ctx;
408	u_char *buf;
409	off_t offset, len;
410	size_t resid;
411	int error;
412
413	KASSERT(file_lock_state != VERIEXEC_LOCKED);
414	KASSERT(file_lock_state != VERIEXEC_UNLOCKED);
415
416	if (file_lock_state == VERIEXEC_FILE_UNLOCKED)
417		vn_lock(vp, LK_SHARED | LK_RETRY);
418	error = VOP_GETATTR(vp, &va, l->l_cred);
419	if (file_lock_state == VERIEXEC_FILE_UNLOCKED)
420		VOP_UNLOCK(vp);
421	if (error)
422		return (error);
423
424	ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP);
425	buf = kmem_alloc(PAGE_SIZE, KM_SLEEP);
426
427	(vfe->ops->init)(ctx);
428
429	len = 0;
430	error = 0;
431	for (offset = 0; offset < va.va_size; offset += PAGE_SIZE) {
432		len = ((va.va_size - offset) < PAGE_SIZE) ?
433		    (va.va_size - offset) : PAGE_SIZE;
434
435		error = vn_rdwr(UIO_READ, vp, buf, len, offset,
436				UIO_SYSSPACE,
437				((file_lock_state == VERIEXEC_FILE_LOCKED)?
438				 IO_NODELOCKED : 0),
439				l->l_cred, &resid, NULL);
440
441		if (error) {
442			goto bad;
443		}
444
445		(vfe->ops->update)(ctx, buf, (unsigned int) len);
446
447		if (len != PAGE_SIZE)
448			break;
449	}
450
451	(vfe->ops->final)(fp, ctx);
452
453bad:
454	kmem_free(ctx, vfe->ops->context_size);
455	kmem_free(buf, PAGE_SIZE);
456
457	return (error);
458}
459
460/* Compare two fingerprints of the same type. */
461static int
462veriexec_fp_cmp(struct veriexec_fpops *ops, u_char *fp1, u_char *fp2)
463{
464	if (veriexec_verbose >= 2) {
465		int i;
466
467		printf("comparing hashes...\n");
468		printf("fp1: ");
469		for (i = 0; i < ops->hash_len; i++) {
470			printf("%02x", fp1[i]);
471		}
472		printf("\nfp2: ");
473		for (i = 0; i < ops->hash_len; i++) {
474			printf("%02x", fp2[i]);
475		}
476		printf("\n");
477	}
478
479	return (memcmp(fp1, fp2, ops->hash_len));
480}
481
482static int
483veriexec_fp_status(struct lwp *l, struct vnode *vp, int file_lock_state,
484    struct veriexec_file_entry *vfe, u_char *status)
485{
486	size_t hash_len = vfe->ops->hash_len;
487	u_char *digest;
488	int error;
489
490	digest = kmem_zalloc(hash_len, KM_SLEEP);
491
492	error = veriexec_fp_calc(l, vp, file_lock_state, vfe, digest);
493	if (error)
494		goto out;
495
496	/* Compare fingerprint with loaded data. */
497	if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0)
498		*status = FINGERPRINT_VALID;
499	else
500		*status = FINGERPRINT_NOMATCH;
501
502out:
503	kmem_free(digest, hash_len);
504	return error;
505}
506
507
508static struct veriexec_table_entry *
509veriexec_table_lookup(struct mount *mp)
510{
511	/* XXX: From raidframe init */
512	if (mp == NULL)
513		return NULL;
514
515	return mount_getspecific(mp, veriexec_mountspecific_key);
516}
517
518static struct veriexec_file_entry *
519veriexec_get(struct vnode *vp)
520{
521	return (fileassoc_lookup(vp, veriexec_hook));
522}
523
524bool
525veriexec_lookup(struct vnode *vp)
526{
527	return (veriexec_get(vp) == NULL ? false : true);
528}
529
530/*
531 * Routine for maintaining mostly consistent message formats in Veriexec.
532 */
533static void
534veriexec_file_report(struct veriexec_file_entry *vfe, const u_char *msg,
535    const u_char *filename, struct lwp *l, int f)
536{
537	if (vfe != NULL && vfe->filename != NULL)
538		filename = vfe->filename;
539	if (filename == NULL)
540		return;
541
542	if (((f & REPORT_LOGMASK) >> 1) <= veriexec_verbose) {
543		if (!(f & REPORT_ALARM) || (l == NULL))
544			log(LOG_NOTICE, "Veriexec: %s [%s]\n", msg,
545			    filename);
546		else
547			log(LOG_ALERT, "Veriexec: %s [%s, prog=%s pid=%u, "
548			    "uid=%u, gid=%u]\n", msg, filename,
549			    l->l_proc->p_comm, l->l_proc->p_pid,
550			    kauth_cred_getuid(l->l_cred),
551			    kauth_cred_getgid(l->l_cred));
552	}
553
554	if (f & REPORT_PANIC)
555		panic("Veriexec: Unrecoverable error.");
556}
557
558/*
559 * Verify the fingerprint of the given file. If we're called directly from
560 * sys_execve(), 'flag' will be VERIEXEC_DIRECT. If we're called from
561 * exec_script(), 'flag' will be VERIEXEC_INDIRECT.  If we are called from
562 * vn_open(), 'flag' will be VERIEXEC_FILE.
563 *
564 * 'veriexec_op_lock' must be locked (and remains locked).
565 *
566 * NOTE: The veriexec file entry pointer (vfep) will be returned LOCKED
567 *       on no error.
568 */
569static int
570veriexec_file_verify(struct lwp *l, struct vnode *vp, const u_char *name,
571    int flag, int file_lock_state, struct veriexec_file_entry **vfep)
572{
573	struct veriexec_file_entry *vfe;
574	int error = 0;
575
576	KASSERT(rw_lock_held(&veriexec_op_lock));
577	KASSERT(file_lock_state != VERIEXEC_LOCKED);
578	KASSERT(file_lock_state != VERIEXEC_UNLOCKED);
579
580#define VFE_NEEDS_EVAL(vfe) ((vfe->status == FINGERPRINT_NOTEVAL) || \
581			     (vfe->type & VERIEXEC_UNTRUSTED))
582
583	if (vfep != NULL)
584		*vfep = NULL;
585
586	if (vp->v_type != VREG)
587		return (0);
588
589	/* Lookup veriexec table entry, save pointer if requested. */
590	vfe = veriexec_get(vp);
591	if (vfep != NULL)
592		*vfep = vfe;
593
594	/* No entry in the veriexec tables. */
595	if (vfe == NULL) {
596		veriexec_file_report(NULL, "No entry.", name,
597		    l, REPORT_VERBOSE);
598
599		/*
600		 * Lockdown mode: Deny access to non-monitored files.
601		 * IPS mode: Deny execution of non-monitored files.
602		 */
603		if ((veriexec_strict >= VERIEXEC_LOCKDOWN) ||
604		    ((veriexec_strict >= VERIEXEC_IPS) &&
605		     (flag != VERIEXEC_FILE)))
606			return (EPERM);
607
608		return (0);
609	}
610
611	/*
612	 * Grab the lock for the entry, if we need to do an evaluation
613	 * then the lock is a write lock, after we have the write
614	 * lock, check if we really need it - some other thread may
615	 * have already done the work for us.
616	 */
617	if (VFE_NEEDS_EVAL(vfe)) {
618		rw_enter(&vfe->lock, RW_WRITER);
619		if (!VFE_NEEDS_EVAL(vfe))
620			rw_downgrade(&vfe->lock);
621	} else
622		rw_enter(&vfe->lock, RW_READER);
623
624	/* Evaluate fingerprint if needed. */
625	if (VFE_NEEDS_EVAL(vfe)) {
626		u_char status;
627
628		error = veriexec_fp_status(l, vp, file_lock_state, vfe, &status);
629		if (error) {
630			veriexec_file_report(vfe, "Fingerprint calculation error.",
631			    name, NULL, REPORT_ALWAYS);
632			rw_exit(&vfe->lock);
633			return (error);
634		}
635		vfe->status = status;
636		rw_downgrade(&vfe->lock);
637	}
638
639	if (!(vfe->type & flag)) {
640		veriexec_file_report(vfe, "Incorrect access type.", name, l,
641		    REPORT_ALWAYS|REPORT_ALARM);
642
643		/* IPS mode: Enforce access type. */
644		if (veriexec_strict >= VERIEXEC_IPS) {
645			rw_exit(&vfe->lock);
646			return (EPERM);
647		}
648	}
649
650	switch (vfe->status) {
651	case FINGERPRINT_NOTEVAL:
652		/* Should not happen. */
653		rw_exit(&vfe->lock);
654		veriexec_file_report(vfe, "Not-evaluated status "
655		    "post evaluation; inconsistency detected.", name,
656		    NULL, REPORT_ALWAYS|REPORT_PANIC);
657		__builtin_unreachable();
658		/* NOTREACHED */
659
660	case FINGERPRINT_VALID:
661		/* Valid fingerprint. */
662		veriexec_file_report(vfe, "Match.", name, NULL,
663		    REPORT_VERBOSE);
664
665		break;
666
667	case FINGERPRINT_NOMATCH:
668		/* Fingerprint mismatch. */
669		veriexec_file_report(vfe, "Mismatch.", name,
670		    NULL, REPORT_ALWAYS|REPORT_ALARM);
671
672		/* IDS mode: Deny access on fingerprint mismatch. */
673		if (veriexec_strict >= VERIEXEC_IDS) {
674			rw_exit(&vfe->lock);
675			error = EPERM;
676		}
677
678		break;
679
680	default:
681		/* Should never happen. */
682		rw_exit(&vfe->lock);
683		veriexec_file_report(vfe, "Invalid status "
684		    "post evaluation.", name, NULL, REPORT_ALWAYS|REPORT_PANIC);
685		/* NOTREACHED */
686	}
687
688	return (error);
689}
690
691int
692veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
693    bool *found)
694{
695	struct veriexec_file_entry *vfe;
696	int r;
697
698	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
699		return 0;
700
701	rw_enter(&veriexec_op_lock, RW_READER);
702	r = veriexec_file_verify(l, vp, name, flag, VERIEXEC_FILE_UNLOCKED,
703	    &vfe);
704	rw_exit(&veriexec_op_lock);
705
706	if ((r  == 0) && (vfe != NULL))
707		rw_exit(&vfe->lock);
708
709	if (found != NULL)
710		*found = (vfe != NULL) ? true : false;
711
712	return (r);
713}
714
715/*
716 * Veriexec remove policy code.
717 */
718int
719veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
720{
721	struct veriexec_file_entry *vfe;
722	int error;
723
724	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
725		return 0;
726
727	rw_enter(&veriexec_op_lock, RW_READER);
728	vfe = veriexec_get(vp);
729	rw_exit(&veriexec_op_lock);
730
731	if (vfe == NULL) {
732		/* Lockdown mode: Deny access to non-monitored files. */
733		if (veriexec_strict >= VERIEXEC_LOCKDOWN)
734			return (EPERM);
735
736		return (0);
737	}
738
739	veriexec_file_report(vfe, "Remove request.", pathbuf, l,
740	    REPORT_ALWAYS|REPORT_ALARM);
741
742	/* IDS mode: Deny removal of monitored files. */
743	if (veriexec_strict >= VERIEXEC_IDS)
744		error = EPERM;
745	else
746		error = veriexec_file_delete(l, vp);
747
748	return error;
749}
750
751/*
752 * Veriexec rename policy.
753 *
754 * XXX: Once there's a way to hook after a successful rename, it would be
755 * XXX: nice to update vfe->filename to the new name if it's not NULL and
756 * XXX: the new name is absolute (ie., starts with a slash).
757 */
758int
759veriexec_renamechk(struct lwp *l, struct vnode *fromvp, const char *fromname,
760    struct vnode *tovp, const char *toname)
761{
762	struct veriexec_file_entry *fvfe = NULL, *tvfe = NULL;
763
764	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
765		return 0;
766
767	rw_enter(&veriexec_op_lock, RW_READER);
768
769	if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
770		log(LOG_ALERT, "Veriexec: Preventing rename of `%s' to "
771		    "`%s', uid=%u, pid=%u: Lockdown mode.\n", fromname, toname,
772		    kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
773		rw_exit(&veriexec_op_lock);
774		return (EPERM);
775	}
776
777	fvfe = veriexec_get(fromvp);
778	if (tovp != NULL)
779		tvfe = veriexec_get(tovp);
780
781	if ((fvfe == NULL) && (tvfe == NULL)) {
782		/* None of them is monitored */
783		rw_exit(&veriexec_op_lock);
784		return 0;
785	}
786
787	if (veriexec_strict >= VERIEXEC_IPS) {
788		log(LOG_ALERT, "Veriexec: Preventing rename of `%s' "
789		    "to `%s', uid=%u, pid=%u: IPS mode, %s "
790		    "monitored.\n", fromname, toname,
791		    kauth_cred_geteuid(l->l_cred),
792		    l->l_proc->p_pid, (fvfe != NULL && tvfe != NULL) ?
793		    "files" : "file");
794		rw_exit(&veriexec_op_lock);
795		return (EPERM);
796	}
797
798	if (fvfe != NULL) {
799		/*
800		 * Monitored file is renamed; filename no longer relevant.
801		 */
802
803		/*
804		 * XXX: We could keep the buffer, and when (and if) updating the
805		 * XXX: filename post-rename, re-allocate it only if it's not
806		 * XXX: big enough for the new filename.
807		 */
808
809		/* XXX: Get write lock on fvfe here? */
810
811		VERIEXEC_RW_UPGRADE(&veriexec_op_lock);
812		/* once we have the op lock in write mode
813		 * there should be no locks on any file
814		 * entries so we can destroy the object.
815		 */
816
817		if (fvfe->filename_len > 0)
818			kmem_free(fvfe->filename, fvfe->filename_len);
819
820		fvfe->filename = NULL;
821		fvfe->filename_len = 0;
822
823		rw_downgrade(&veriexec_op_lock);
824	}
825
826	log(LOG_NOTICE, "Veriexec: %s file `%s' renamed to "
827	    "%s file `%s', uid=%u, pid=%u.\n", (fvfe != NULL) ?
828	    "Monitored" : "Non-monitored", fromname, (tvfe != NULL) ?
829	    "monitored" : "non-monitored", toname,
830	    kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
831
832	rw_exit(&veriexec_op_lock);
833
834	if (tvfe != NULL) {
835		/*
836		 * Monitored file is overwritten. Remove the entry.
837		 */
838		(void)veriexec_file_delete(l, tovp);
839	}
840
841	return (0);
842}
843
844static void
845veriexec_file_free(struct veriexec_file_entry *vfe)
846{
847	if (vfe != NULL) {
848		if (vfe->fp != NULL)
849			kmem_free(vfe->fp, vfe->ops->hash_len);
850		if (vfe->filename != NULL)
851			kmem_free(vfe->filename, vfe->filename_len);
852		rw_destroy(&vfe->lock);
853		kmem_free(vfe, sizeof(*vfe));
854	}
855}
856
857static void
858veriexec_file_purge(struct veriexec_file_entry *vfe, int have_lock)
859{
860	if (vfe == NULL)
861		return;
862
863	if (have_lock == VERIEXEC_UNLOCKED)
864		rw_enter(&vfe->lock, RW_WRITER);
865	else
866		VERIEXEC_RW_UPGRADE(&vfe->lock);
867
868	vfe->status = FINGERPRINT_NOTEVAL;
869	if (have_lock == VERIEXEC_UNLOCKED)
870		rw_exit(&vfe->lock);
871	else
872		rw_downgrade(&vfe->lock);
873}
874
875static void
876veriexec_file_purge_cb(struct veriexec_file_entry *vfe, void *cookie)
877{
878	veriexec_file_purge(vfe, VERIEXEC_UNLOCKED);
879}
880
881/*
882 * Invalidate a Veriexec file entry.
883 * XXX: This should be updated when per-page fingerprints are added.
884 */
885void
886veriexec_purge(struct vnode *vp)
887{
888	rw_enter(&veriexec_op_lock, RW_READER);
889	veriexec_file_purge(veriexec_get(vp), VERIEXEC_UNLOCKED);
890	rw_exit(&veriexec_op_lock);
891}
892
893/*
894 * Enforce raw disk access policy.
895 *
896 * IDS mode: Invalidate fingerprints on a mount if it's opened for writing.
897 * IPS mode: Don't allow raw writing to disks we monitor.
898 * Lockdown mode: Don't allow raw writing to all disks.
899 *
900 * XXX: This is bogus. There's an obvious race condition between the time
901 * XXX: the disk is open for writing, in which an attacker can access a
902 * XXX: monitored file to get its signature cached again, and when the raw
903 * XXX: file is overwritten on disk.
904 * XXX:
905 * XXX: To solve this, we need something like the following:
906 * XXX:		open raw disk:
907 * XXX:		  - raise refcount,
908 * XXX:		  - invalidate fingerprints,
909 * XXX:		  - mark all entries for that disk with "no cache" flag
910 * XXX:
911 * XXX:		veriexec_verify:
912 * XXX:		  - if "no cache", don't cache evaluation result
913 * XXX:
914 * XXX:		close raw disk:
915 * XXX:		  - lower refcount,
916 * XXX:		  - if refcount == 0, remove "no cache" flag from all entries
917 */
918static int
919veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
920    void *arg0, void *arg1, void *arg2, void *arg3)
921{
922	int result;
923	enum kauth_device_req req;
924	struct veriexec_table_entry *vte;
925
926	result = KAUTH_RESULT_DENY;
927	req = (enum kauth_device_req)(uintptr_t)arg0;
928
929	switch (action) {
930	case KAUTH_DEVICE_RAWIO_SPEC: {
931		struct vnode *vp, *bvp;
932		int error;
933
934		if (req == KAUTH_REQ_DEVICE_RAWIO_SPEC_READ) {
935			result = KAUTH_RESULT_DEFER;
936			break;
937		}
938
939		vp = arg1;
940		KASSERT(vp != NULL);
941
942		/* Handle /dev/mem and /dev/kmem. */
943		if (iskmemvp(vp)) {
944			if (veriexec_strict < VERIEXEC_IPS)
945				result = KAUTH_RESULT_DEFER;
946
947			break;
948		}
949
950		error = rawdev_mounted(vp, &bvp);
951		if (error == EINVAL) {
952			result = KAUTH_RESULT_DEFER;
953			break;
954		}
955
956		/*
957		 * XXX: See vfs_mountedon() comment in rawdev_mounted().
958		 */
959		vte = veriexec_table_lookup(bvp->v_mount);
960		if (vte == NULL) {
961			result = KAUTH_RESULT_DEFER;
962			break;
963		}
964
965		switch (veriexec_strict) {
966		case VERIEXEC_LEARNING:
967		case VERIEXEC_IDS:
968			result = KAUTH_RESULT_DEFER;
969
970			rw_enter(&veriexec_op_lock, RW_WRITER);
971			fileassoc_table_run(bvp->v_mount, veriexec_hook,
972			    (fileassoc_cb_t)veriexec_file_purge_cb, NULL);
973			rw_exit(&veriexec_op_lock);
974
975			break;
976		case VERIEXEC_IPS:
977			result = KAUTH_RESULT_DENY;
978			break;
979		case VERIEXEC_LOCKDOWN:
980			result = KAUTH_RESULT_DENY;
981			break;
982		}
983
984		break;
985		}
986
987	case KAUTH_DEVICE_RAWIO_PASSTHRU:
988		/* XXX What can we do here? */
989		if (veriexec_strict < VERIEXEC_IPS)
990			result = KAUTH_RESULT_DEFER;
991
992		break;
993
994	default:
995		result = KAUTH_RESULT_DEFER;
996		break;
997	}
998
999	return (result);
1000}
1001
1002/*
1003 * Create a new Veriexec table.
1004 */
1005static struct veriexec_table_entry *
1006veriexec_table_add(struct lwp *l, struct mount *mp)
1007{
1008	struct veriexec_table_entry *vte;
1009	u_char buf[16];
1010
1011	vte = kmem_zalloc(sizeof(*vte), KM_SLEEP);
1012	mount_setspecific(mp, veriexec_mountspecific_key, vte);
1013
1014	snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++);
1015	sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
1016		       0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL,
1017		       0, CTL_CREATE, CTL_EOL);
1018
1019	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1020		       CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
1021		       NULL, NULL, 0, mp->mnt_stat.f_mntonname,
1022		       0, CTL_CREATE, CTL_EOL);
1023	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1024		       CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
1025		       NULL, NULL, 0, mp->mnt_stat.f_fstypename,
1026		       0, CTL_CREATE, CTL_EOL);
1027	sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1028		       CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
1029		       NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
1030
1031	return (vte);
1032}
1033
1034/*
1035 * Add a file to be monitored by Veriexec.
1036 *
1037 * Expected elements in dict:
1038 *     file, fp, fp-type, entry-type, keep-filename, eval-on-load.
1039 */
1040int
1041veriexec_file_add(struct lwp *l, prop_dictionary_t dict)
1042{
1043	struct veriexec_table_entry *vte;
1044	struct veriexec_file_entry *vfe = NULL;
1045	struct veriexec_file_entry *ovfe;
1046	struct vnode *vp;
1047	const char *file, *fp_type;
1048	int error;
1049	bool ignore_dup = false;
1050
1051	if (!prop_dictionary_get_string(dict, "file", &file))
1052		return (EINVAL);
1053
1054	error = namei_simple_kernel(file, NSM_FOLLOW_NOEMULROOT, &vp);
1055	if (error)
1056		return (error);
1057
1058	/* Add only regular files. */
1059	if (vp->v_type != VREG) {
1060		log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n",
1061		    file);
1062		error = EBADF;
1063		goto out;
1064	}
1065
1066	vfe = kmem_zalloc(sizeof(*vfe), KM_SLEEP);
1067	rw_init(&vfe->lock);
1068
1069	/* Lookup fingerprint hashing algorithm. */
1070	fp_type = prop_string_value(prop_dictionary_get(dict, "fp-type"));
1071	if ((vfe->ops = veriexec_fpops_lookup(fp_type)) == NULL) {
1072		log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
1073		    "`%s' for file `%s'.\n", fp_type, file);
1074		error = EOPNOTSUPP;
1075		goto out;
1076	}
1077
1078	if (prop_data_size(prop_dictionary_get(dict, "fp")) !=
1079	    vfe->ops->hash_len) {
1080		log(LOG_ERR, "Veriexec: Bad fingerprint length for `%s'.\n",
1081		    file);
1082		error = EINVAL;
1083		goto out;
1084	}
1085
1086	vfe->fp = kmem_alloc(vfe->ops->hash_len, KM_SLEEP);
1087	memcpy(vfe->fp, prop_data_value(prop_dictionary_get(dict, "fp")),
1088	    vfe->ops->hash_len);
1089
1090	rw_enter(&veriexec_op_lock, RW_WRITER);
1091
1092	/* Continue entry initialization. */
1093	if (prop_dictionary_get_uint8(dict, "entry-type", &vfe->type) == FALSE)
1094		vfe->type = 0;
1095	else {
1096		uint8_t extra_flags;
1097
1098		extra_flags = vfe->type & ~(VERIEXEC_DIRECT |
1099		    VERIEXEC_INDIRECT | VERIEXEC_FILE | VERIEXEC_UNTRUSTED);
1100		if (extra_flags) {
1101			log(LOG_NOTICE, "Veriexec: Contaminated flags `0x%x' "
1102			    "for `%s', skipping.\n", extra_flags, file);
1103			error = EINVAL;
1104			goto unlock_out;
1105		}
1106	}
1107	if (!(vfe->type & (VERIEXEC_DIRECT | VERIEXEC_INDIRECT |
1108	    VERIEXEC_FILE)))
1109		vfe->type |= VERIEXEC_DIRECT;
1110
1111	vfe->status = FINGERPRINT_NOTEVAL;
1112	if (prop_bool_true(prop_dictionary_get(dict, "keep-filename"))) {
1113		vfe->filename = kmem_strdupsize(file, &vfe->filename_len,
1114		    KM_SLEEP);
1115	} else
1116		vfe->filename = NULL;
1117
1118	if (prop_bool_true(prop_dictionary_get(dict, "eval-on-load")) ||
1119	    (vfe->type & VERIEXEC_UNTRUSTED)) {
1120		u_char status;
1121
1122		error = veriexec_fp_status(l, vp, VERIEXEC_FILE_UNLOCKED,
1123		    vfe, &status);
1124		if (error)
1125			goto unlock_out;
1126		vfe->status = status;
1127	}
1128
1129	/*
1130	 * If we already have an entry for this file, and it matches
1131	 * the new entry exactly (except for the filename, which may
1132	 * hard-linked!), we just ignore the new entry.  If the new
1133	 * entry differs, report the error.
1134	 */
1135	if ((ovfe = veriexec_get(vp)) != NULL) {
1136		error = EEXIST;
1137		if (vfe->type == ovfe->type &&
1138		    vfe->status == ovfe->status &&
1139		    vfe->ops == ovfe->ops &&
1140		    memcmp(vfe->fp, ovfe->fp, vfe->ops->hash_len) == 0)
1141			ignore_dup = true;
1142		goto unlock_out;
1143	}
1144
1145	vte = veriexec_table_lookup(vp->v_mount);
1146	if (vte == NULL)
1147		vte = veriexec_table_add(l, vp->v_mount);
1148
1149	/* XXX if we bail below this, we might want to gc newly created vtes. */
1150
1151	error = fileassoc_add(vp, veriexec_hook, vfe);
1152	if (error)
1153		goto unlock_out;
1154
1155	vte->vte_count++;
1156
1157	veriexec_file_report(NULL, "New entry.", file, NULL, REPORT_DEBUG);
1158	veriexec_bypass = 0;
1159
1160  unlock_out:
1161	rw_exit(&veriexec_op_lock);
1162
1163  out:
1164	vrele(vp);
1165	if (error)
1166		veriexec_file_free(vfe);
1167
1168	if (ignore_dup && error == EEXIST)
1169		error = 0;
1170
1171	return (error);
1172}
1173
1174int
1175veriexec_table_delete(struct lwp *l, struct mount *mp)
1176{
1177	struct veriexec_table_entry *vte;
1178
1179	vte = veriexec_table_lookup(mp);
1180	if (vte == NULL)
1181		return (ENOENT);
1182
1183	veriexec_mountspecific_dtor(vte);
1184	mount_setspecific(mp, veriexec_mountspecific_key, NULL);
1185
1186	return (fileassoc_table_clear(mp, veriexec_hook));
1187}
1188
1189int
1190veriexec_file_delete(struct lwp *l, struct vnode *vp)
1191{
1192	struct veriexec_table_entry *vte;
1193	int error;
1194
1195	vte = veriexec_table_lookup(vp->v_mount);
1196	if (vte == NULL)
1197		return (ENOENT);
1198
1199	rw_enter(&veriexec_op_lock, RW_WRITER);
1200	error = fileassoc_clear(vp, veriexec_hook);
1201	rw_exit(&veriexec_op_lock);
1202	if (!error) {
1203		KASSERT(vte->vte_count > 0);
1204		vte->vte_count--;
1205	}
1206
1207	return (error);
1208}
1209
1210/*
1211 * Convert Veriexec entry data to a dictionary readable by userland tools.
1212 */
1213static void
1214veriexec_file_convert(struct veriexec_file_entry *vfe, prop_dictionary_t rdict)
1215{
1216	if (vfe->filename)
1217		prop_dictionary_set(rdict, "file",
1218		    prop_string_create_copy(vfe->filename));
1219	prop_dictionary_set_uint8(rdict, "entry-type", vfe->type);
1220	prop_dictionary_set_uint8(rdict, "status", vfe->status);
1221	prop_dictionary_set(rdict, "fp-type",
1222	    prop_string_create_copy(vfe->ops->type));
1223	prop_dictionary_set(rdict, "fp",
1224	    prop_data_create_copy(vfe->fp, vfe->ops->hash_len));
1225}
1226
1227int
1228veriexec_convert(struct vnode *vp, prop_dictionary_t rdict)
1229{
1230	struct veriexec_file_entry *vfe;
1231
1232	rw_enter(&veriexec_op_lock, RW_READER);
1233
1234	vfe = veriexec_get(vp);
1235	if (vfe == NULL) {
1236		rw_exit(&veriexec_op_lock);
1237		return (ENOENT);
1238	}
1239
1240	rw_enter(&vfe->lock, RW_READER);
1241	veriexec_file_convert(vfe, rdict);
1242	rw_exit(&vfe->lock);
1243
1244	rw_exit(&veriexec_op_lock);
1245	return (0);
1246}
1247
1248int
1249veriexec_unmountchk(struct mount *mp)
1250{
1251	int error;
1252
1253	if ((veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
1254	    || doing_shutdown)
1255		return (0);
1256
1257	rw_enter(&veriexec_op_lock, RW_READER);
1258
1259	switch (veriexec_strict) {
1260	case VERIEXEC_LEARNING:
1261		error = 0;
1262		break;
1263
1264	case VERIEXEC_IDS:
1265		if (veriexec_table_lookup(mp) != NULL) {
1266			log(LOG_INFO, "Veriexec: IDS mode, allowing unmount "
1267			    "of \"%s\".\n", mp->mnt_stat.f_mntonname);
1268		}
1269
1270		error = 0;
1271		break;
1272
1273	case VERIEXEC_IPS: {
1274		struct veriexec_table_entry *vte;
1275
1276		vte = veriexec_table_lookup(mp);
1277		if ((vte != NULL) && (vte->vte_count > 0)) {
1278			log(LOG_ALERT, "Veriexec: IPS mode, preventing"
1279			    " unmount of \"%s\" with monitored files.\n",
1280			    mp->mnt_stat.f_mntonname);
1281
1282			error = EPERM;
1283		} else
1284			error = 0;
1285		break;
1286		}
1287
1288	case VERIEXEC_LOCKDOWN:
1289	default:
1290		log(LOG_ALERT, "Veriexec: Lockdown mode, preventing unmount "
1291		    "of \"%s\".\n", mp->mnt_stat.f_mntonname);
1292		error = EPERM;
1293		break;
1294	}
1295
1296	rw_exit(&veriexec_op_lock);
1297	return (error);
1298}
1299
1300int
1301veriexec_openchk(struct lwp *l, struct vnode *vp, const char *path, int fmode)
1302{
1303	struct veriexec_file_entry *vfe = NULL;
1304	int error = 0;
1305
1306	if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
1307		return 0;
1308
1309	if (vp == NULL) {
1310		/* If no creation requested, let this fail normally. */
1311		if (!(fmode & O_CREAT))
1312			goto out;
1313
1314		/* Lockdown mode: Prevent creation of new files. */
1315		if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
1316			log(LOG_ALERT, "Veriexec: Preventing new file "
1317			    "creation in `%s'.\n", path);
1318			error = EPERM;
1319		}
1320
1321		goto out;
1322	}
1323
1324	rw_enter(&veriexec_op_lock, RW_READER);
1325	error = veriexec_file_verify(l, vp, path, VERIEXEC_FILE,
1326				     VERIEXEC_FILE_LOCKED, &vfe);
1327
1328	if (error) {
1329		rw_exit(&veriexec_op_lock);
1330		goto out;
1331	}
1332
1333	if ((vfe != NULL) && ((fmode & FWRITE) || (fmode & O_TRUNC))) {
1334		veriexec_file_report(vfe, "Write access request.", path, l,
1335		    REPORT_ALWAYS | REPORT_ALARM);
1336
1337		/* IPS mode: Deny write access to monitored files. */
1338		if (veriexec_strict >= VERIEXEC_IPS)
1339			error = EPERM;
1340		else
1341			veriexec_file_purge(vfe, VERIEXEC_LOCKED);
1342	}
1343
1344	if (vfe != NULL)
1345		rw_exit(&vfe->lock);
1346
1347	rw_exit(&veriexec_op_lock);
1348 out:
1349	return (error);
1350}
1351
1352static void
1353veriexec_file_dump(struct veriexec_file_entry *vfe, prop_array_t entries)
1354{
1355	prop_dictionary_t entry;
1356
1357	/* If we don't have a filename, this is meaningless. */
1358	if (vfe->filename == NULL)
1359		return;
1360
1361	entry = prop_dictionary_create();
1362
1363	veriexec_file_convert(vfe, entry);
1364
1365	prop_array_add(entries, entry);
1366}
1367
1368int
1369veriexec_dump(struct lwp *l, prop_array_t rarray)
1370{
1371	mount_iterator_t *iter;
1372	struct mount *mp;
1373
1374	mountlist_iterator_init(&iter);
1375	while ((mp = mountlist_iterator_next(iter)) != NULL) {
1376		fileassoc_table_run(mp, veriexec_hook,
1377		    (fileassoc_cb_t)veriexec_file_dump, rarray);
1378	}
1379	mountlist_iterator_destroy(iter);
1380
1381	return (0);
1382}
1383
1384int
1385veriexec_flush(struct lwp *l)
1386{
1387	mount_iterator_t *iter;
1388	struct mount *mp;
1389	int error = 0;
1390
1391	mountlist_iterator_init(&iter);
1392	while ((mp = mountlist_iterator_next(iter)) != NULL) {
1393		int lerror;
1394
1395		lerror = veriexec_table_delete(l, mp);
1396		if (lerror && lerror != ENOENT)
1397			error = lerror;
1398	}
1399	mountlist_iterator_destroy(iter);
1400
1401	return (error);
1402}
1403