1/*
2 * $FreeBSD$
3 *
4 * Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
5 * All rights reserved.
6 *
7 * Originally derived from:
8 *	$NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33
34#include "opt_mac.h"
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/exec.h>
39#include <sys/lock.h>
40#include <sys/malloc.h>
41#include <sys/mutex.h>
42#include <sys/proc.h>
43#include <sys/sbuf.h>
44#include <sys/vnode.h>
45
46#include "mac_veriexec.h"
47#include "mac_veriexec_internal.h"
48
49/**
50 * @brief per-device meta-data storage
51 */
52struct veriexec_dev_list {
53	dev_t fsid;	/**< file system identifier of the mount point */
54	LIST_HEAD(filehead, mac_veriexec_file_info) file_head;
55	    /**< list of per-file meta-data information */
56	LIST_ENTRY(veriexec_dev_list) entries;
57	    /**< next entries in the device list */
58};
59
60typedef LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_devhead_t;
61
62/**
63 * @brief Mutex to protect the meta-data store lists
64 */
65struct mtx ve_mutex;
66
67/**
68 * @brief Executables meta-data storage
69 *
70 * This is used to store the fingerprints for potentially-executable files.
71 */
72veriexec_devhead_t veriexec_dev_head;
73
74/**
75 * @brief Plain file meta-data storage
76 *
77 * This is used for files that are not allowed to be executed, but should
78 * have fingerprint validation available.
79 */
80veriexec_devhead_t veriexec_file_dev_head;
81
82/**
83 * @internal
84 * @brief Search the @p head meta-data list for the specified file identifier
85 *     @p fileid in the file system identified by @p fsid
86 *
87 * If meta-data exists for file system identified by @p fsid, it has a
88 * fingerprint list, and @p found_dev is not @c NULL then store true in the
89 * location pointed to by @p found_dev
90 *
91 * @param head		meta-data list to search
92 * @param fsid		file system identifier to look for
93 * @param fileid	file to look for
94 * @param gen		generation of file
95 * @param found_dev	indicator that an entry for the file system was found
96 *
97 * @return A pointer to the meta-data inforation if meta-data exists for
98 *     the specified file identifier, otherwise @c NULL
99 */
100static struct mac_veriexec_file_info *
101get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid,
102    unsigned long gen, int *found_dev)
103{
104	struct veriexec_dev_list *lp;
105	struct mac_veriexec_file_info *ip, *tip;
106
107	ip = NULL;
108
109	/* Initialize the value found_dev, if non-NULL */
110	if (found_dev != NULL)
111		*found_dev = 0;
112
113	VERIEXEC_DEBUG(3, ("searching for file %ju.%lu on device %ju,"
114	    " files=%d\n", (uintmax_t)fileid, gen, (uintmax_t)fsid,
115	    (head == &veriexec_file_dev_head)));
116
117	/* Get a lock to access the list */
118	mtx_lock(&ve_mutex);
119
120	/* First, look for the file system */
121	for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries))
122		if (lp->fsid == fsid)
123			break;
124
125	/* We found the file system in the list */
126	if (lp != NULL) {
127		VERIEXEC_DEBUG(3, ("found matching dev number %ju\n",
128		    (uintmax_t)lp->fsid));
129
130		/* If found_dev is non-NULL, store true there */
131		if (found_dev != NULL)
132			*found_dev = 1;
133
134		/* Next, look for the meta-data information for the file */
135		LIST_FOREACH_SAFE(ip, &(lp->file_head), entries, tip) {
136			if (ip->fileid == fileid) {
137				if (ip->gen == gen)
138					break;
139				/* we need to garbage collect */
140				LIST_REMOVE(ip, entries);
141				free(ip, M_VERIEXEC);
142			}
143		}
144	}
145
146	/* Release the lock we obtained earlier */
147	mtx_unlock(&ve_mutex);
148
149	/* Return the meta-data information we found, if anything */
150	return (ip);
151}
152
153/**
154 * @internal
155 * @brief Search the meta-data store for information on the specified file.
156 *
157 * @param fsid		file system identifier to look for
158 * @param fileid	file to look for
159 * @param gen		generation of file
160 * @param found_dev	indicator that an entry for the file system was found
161 * @param check_files	if 1, check the files list first, otherwise check the
162 * 			exectuables list first
163 *
164 * @return A pointer to the meta-data inforation if meta-data exists for
165 *     the specified file identifier, otherwise @c NULL
166 */
167static struct mac_veriexec_file_info *
168find_veriexec_file(dev_t fsid, long fileid, unsigned long gen, int *found_dev,
169    int check_files)
170{
171	struct veriexec_devhead *search[3];
172	struct mac_veriexec_file_info *ip;
173	int x;
174
175	/* Determine the order of the lists to search */
176	if (check_files) {
177		search[0] = &veriexec_file_dev_head;
178		search[1] = &veriexec_dev_head;
179	} else {
180		search[0] = &veriexec_dev_head;
181		search[1] = &veriexec_file_dev_head;
182	}
183	search[2] = NULL;
184
185	VERIEXEC_DEBUG(3, ("%s: searching for dev %ju, file %lu\n",
186	    __func__, (uintmax_t)fsid, fileid));
187
188	/* Search for the specified file */
189	for (ip = NULL, x = 0; ip == NULL && search[x]; x++)
190		ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev);
191
192	return (ip);
193}
194
195/**
196 * @internal
197 * @brief Display the fingerprint for each entry in the device list
198 *
199 * @param sbp		sbuf to write output to
200 * @param lp		pointer to device list
201 */
202static void
203mac_veriexec_print_db_dev_list(struct sbuf *sbp, struct veriexec_dev_list *lp)
204{
205	struct mac_veriexec_file_info *ip;
206
207#define FPB(i) (ip->fingerprint[i])
208	for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
209	    ip = LIST_NEXT(ip, entries))
210		sbuf_printf(sbp, "  %ld: %u %ld [%02x %02x %02x %02x %02x "
211		    "%02x %02x %02x...]\n", ip->fileid, ip->flags, ip->gen,
212		    FPB(0), FPB(1), FPB(2), FPB(3), FPB(4), FPB(5), FPB(6),
213		    FPB(7));
214}
215
216/**
217 * @internal
218 * @brief Display the device list
219 *
220 * @param sbp		sbuf to write output to
221 * @param head		pointer to head of the device list
222 */
223static void
224mac_veriexec_print_db_head(struct sbuf *sbp, struct veriexec_devhead *head)
225{
226	struct veriexec_dev_list *lp;
227
228	for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) {
229		sbuf_printf(sbp, " FS id: %ju\n", (uintmax_t)lp->fsid);
230		mac_veriexec_print_db_dev_list(sbp, lp);
231	}
232
233}
234
235/**
236 * @internal
237 * @brief Generate human-readable output for the current fingerprint database
238 *
239 * @param sbp	sbuf to write output to
240 */
241void
242mac_veriexec_metadata_print_db(struct sbuf *sbp)
243{
244	struct {
245		struct veriexec_devhead *h;
246		const char *name;
247	} fpdbs[] = {
248		{ &veriexec_file_dev_head, "regular files" },
249		{ &veriexec_dev_head, "executable files" },
250	};
251	int i;
252
253	mtx_lock(&ve_mutex);
254	for (i = 0; i < sizeof(fpdbs)/sizeof(fpdbs[0]); i++) {
255		sbuf_printf(sbp, "%s fingerprint db:\n", fpdbs[i].name);
256		mac_veriexec_print_db_head(sbp, fpdbs[i].h);
257	}
258	mtx_unlock(&ve_mutex);
259}
260/**
261 * @brief Determine if the meta-data store has an entry for the specified file.
262 *
263 * @param fsid		file system identifier to look for
264 * @param fileid	file to look for
265 * @param gen		generation of file
266 *
267 * @return 1 if there is an entry in the meta-data store, 0 otherwise.
268 */
269int
270mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen)
271{
272
273	return (find_veriexec_file(fsid, fileid, gen, NULL,
274	    VERIEXEC_FILES_FIRST) != NULL);
275}
276
277/**
278 * @brief Search the list of devices looking for the one given, in order to
279 *     release the resources used by it.
280 *
281 * If found, free all file entries for it, and remove it from the list.
282 *
283 * @note Called with @a ve_mutex held
284 *
285 * @param fsid		file system identifier to look for
286 * @param head		meta-data list to search
287 *
288 * @return 0 if the device entry was freed, otherwise an error code
289 */
290static int
291free_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
292{
293	struct veriexec_dev_list *lp;
294	struct mac_veriexec_file_info *ip, *nip;
295
296	/* Look for the file system */
297	for (lp = LIST_FIRST(head); lp != NULL;
298	     lp = LIST_NEXT(lp, entries))
299		if (lp->fsid == fsid) break;
300
301	/* If lp is NULL, we did not find it */
302	if (lp == NULL)
303		return ENOENT;
304
305	/* Unhook lp, before we free it and its content */
306	LIST_REMOVE(lp, entries);
307
308	/* Release the lock */
309	mtx_unlock(&ve_mutex);
310
311	/* Free the file entries in the list */
312	for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = nip) {
313		nip = LIST_NEXT(ip, entries);
314		LIST_REMOVE(ip, entries);
315		free(ip, M_VERIEXEC);
316	}
317
318	/* Free the meta-data entry for the device */
319	free(lp, M_VERIEXEC);
320
321	/* Re-acquire the lock */
322	mtx_lock(&ve_mutex);
323	return 0;
324}
325
326/**
327 * @brief Search the list of devices looking for the one given.
328 *
329 * If it is not in the list then add it.
330 *
331 * @note Called with @a ve_mutex held
332 *
333 * @param fsid		file system identifier to look for
334 * @param head		meta-data list to search
335 *
336 * @return A pointer to the meta-data entry for the device, if found or added,
337 *     otherwise @c NULL
338 */
339static struct veriexec_dev_list *
340find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
341{
342	struct veriexec_dev_list *lp;
343	struct veriexec_dev_list *np = NULL;
344
345search:
346	/* Look for the file system */
347	for (lp = LIST_FIRST(head); lp != NULL;
348	     lp = LIST_NEXT(lp, entries))
349		if (lp->fsid == fsid) break;
350
351	if (lp == NULL) {
352		if (np == NULL) {
353			/*
354			 * If pointer is null then entry not there,
355			 * add a new one, first try to malloc while
356			 * we hold mutex - should work most of the time.
357			 */
358			np = malloc(sizeof(struct veriexec_dev_list),
359			    M_VERIEXEC, M_NOWAIT);
360			if (np == NULL) {
361				/*
362				 * So much for that plan, dop the mutex
363				 * and repeat...
364				 */
365				mtx_unlock(&ve_mutex);
366				np = malloc(sizeof(struct veriexec_dev_list),
367				    M_VERIEXEC, M_WAITOK);
368				mtx_lock(&ve_mutex);
369				/*
370				 * Repeat the seach, in case someone
371				 * added this while we slept.
372				 */
373				goto search;
374			}
375		}
376		if (np) {
377			/* Add the entry to the list */
378			lp = np;
379			LIST_INIT(&(lp->file_head));
380			lp->fsid = fsid;
381			LIST_INSERT_HEAD(head, lp, entries);
382		}
383	} else if (np) {
384		/*
385		 * Someone else did it while we slept.
386		 */
387		mtx_unlock(&ve_mutex);
388		free(np, M_VERIEXEC);
389		mtx_lock(&ve_mutex);
390	}
391
392	return (lp);
393}
394
395/**
396 * @brief When a device is unmounted, we want to toss the signatures recorded
397 *     against it.
398 *
399 * We are being called from unmount() with the root vnode just before it is
400 * freed.
401 *
402 * @param fsid		file system identifier to look for
403 * @param td		calling thread
404 *
405 * @return 0 on success, otherwise an error code.
406 */
407int
408mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td)
409{
410    int error;
411
412    /*
413     * The device can have entries on both lists.
414     */
415    mtx_lock(&ve_mutex);
416    error = free_veriexec_dev(fsid, &veriexec_dev_head);
417    if (error && error != ENOENT) {
418	    mtx_unlock(&ve_mutex);
419	    return error;
420    }
421    error = free_veriexec_dev(fsid, &veriexec_file_dev_head);
422    mtx_unlock(&ve_mutex);
423    if (error && error != ENOENT) {
424	    return error;
425    }
426    return 0;
427}
428
429/**
430 * @brief Return the flags assigned to the file identified by file system
431 * 	  identifier @p fsid and file identifier @p fileid.
432 *
433 * @param fsid		file system identifier
434 * @param fileid	file identifier within the file system
435 * @param gen		generation of file
436 * @param flags		pointer to location to store the flags
437 * @param check_files	if 1, check the files list first, otherwise check the
438 * 			exectuables list first
439 *
440 * @return 0 on success, otherwise an error code.
441 */
442int
443mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen,
444    int *flags, int check_files)
445{
446	struct mac_veriexec_file_info *ip;
447	int found_dev;
448
449	ip = find_veriexec_file(fsid, fileid, gen, &found_dev, check_files);
450	if (ip == NULL)
451		return (ENOENT);
452
453	*flags = ip->flags;
454	return (0);
455}
456
457/**
458 * @brief get the files for the specified process
459 *
460 * @param cred		credentials to use
461 * @param p		process to get the flags for
462 * @param flags		where to store the flags
463 * @param check_files	if 1, check the files list first, otherwise check the
464 * 			exectuables list first
465 *
466 * @return 0 if the process has an entry in the meta-data store, otherwise an
467 *     error code
468 */
469int
470mac_veriexec_metadata_get_executable_flags(struct ucred *cred, struct proc *p,
471    int *flags, int check_files)
472{
473	struct vnode *proc_vn;
474	struct vattr vap;
475	int error;
476
477	/* Get the text vnode for the process */
478	proc_vn = p->p_textvp;
479	if (proc_vn == NULL)
480		return EINVAL;
481
482	/* Get vnode attributes */
483	error = VOP_GETATTR(proc_vn, &vap, cred);
484	if (error)
485		return error;
486
487	error = mac_veriexec_metadata_get_file_flags(vap.va_fsid,
488	    vap.va_fileid, vap.va_gen, flags,
489	    (check_files == VERIEXEC_FILES_FIRST));
490
491	return (error);
492}
493
494/**
495 * @brief Ensure the fingerprint status for the vnode @p vp is assigned to its
496 *     MAC label.
497 *
498 * @param vp		vnode to check
499 * @param vap		vnode attributes to use
500 * @param td		calling thread
501 * @param check_files	if 1, check the files list first, otherwise check the
502 * 			exectuables list first
503 *
504 * @return 0 on success, otherwise an error code.
505 */
506int
507mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
508    struct vattr *vap, struct thread *td, int check_files)
509{
510	unsigned char digest[MAXFINGERPRINTLEN];
511	struct mac_veriexec_file_info *ip;
512	int error, found_dev;
513	fingerprint_status_t status;
514
515	error = 0;
516	ip = NULL;
517
518	status = mac_veriexec_get_fingerprint_status(vp);
519	if (status == FINGERPRINT_INVALID || status == FINGERPRINT_NODEV) {
520		found_dev = 0;
521		ip = find_veriexec_file(vap->va_fsid, vap->va_fileid,
522		    vap->va_gen, &found_dev, check_files);
523		if (ip == NULL) {
524			status = (found_dev) ? FINGERPRINT_NOENTRY :
525			    FINGERPRINT_NODEV;
526			VERIEXEC_DEBUG(3,
527			    ("fingerprint status is %d for dev %ju, file "
528			    "%ju.%lu\n", status, (uintmax_t)vap->va_fsid,
529			    (uintmax_t)vap->va_fileid, vap->va_gen));
530		} else {
531			/*
532			 * evaluate and compare fingerprint
533			 */
534			error = mac_veriexec_fingerprint_check_vnode(vp, ip,
535			    td, vap->va_size, digest);
536			switch (error) {
537			case 0:
538				/* Process flags */
539				if ((ip->flags & VERIEXEC_INDIRECT))
540					status = FINGERPRINT_INDIRECT;
541				else if ((ip->flags & VERIEXEC_FILE))
542					status = FINGERPRINT_FILE;
543				else
544					status = FINGERPRINT_VALID;
545				VERIEXEC_DEBUG(2,
546				    ("%sfingerprint matches for dev %ju, file "
547				    "%ju.%lu\n",
548				     (status == FINGERPRINT_INDIRECT) ?
549				     "indirect " :
550				     (status == FINGERPRINT_FILE) ?
551				     "file " : "", (uintmax_t)vap->va_fsid,
552				     (uintmax_t)vap->va_fileid, vap->va_gen));
553				break;
554
555			case EAUTH:
556#ifdef VERIFIED_EXEC_DEBUG_VERBOSE
557				{
558					char have[MAXFINGERPRINTLEN * 2 + 1];
559					char want[MAXFINGERPRINTLEN * 2 + 1];
560					int i, len;
561
562					len = ip->ops->digest_len;
563					for (i = 0; i < len; i++) {
564						sprintf(&want[i * 2], "%02x",
565						    ip->fingerprint[i]);
566						sprintf(&have[i * 2], "%02x",
567						    digest[i]);
568					}
569					log(LOG_ERR, MAC_VERIEXEC_FULLNAME
570					    ": fingerprint for dev %ju, file "
571					    "%ju.%lu %s != %s\n",
572					    (uintmax_t)vap->va_fsid,
573					    (uintmax_t)vap->va_fileid,
574					    vap->va_gen,
575					    have, want);
576				}
577#endif
578				status = FINGERPRINT_NOMATCH;
579				break;
580			default:
581				VERIEXEC_DEBUG(2,
582				    ("fingerprint status error %d\n", error));
583				break;
584			}
585		}
586		mac_veriexec_set_fingerprint_status(vp, status);
587	}
588	return (error);
589}
590
591/**
592 * Add a file and its fingerprint to the list of files attached
593 * to the device @p fsid.
594 *
595 * Only add the entry if it is not already on the list.
596 *
597 * @note Called with @a ve_mutex held
598 *
599 * @param file_dev	if 1, the entry should be added on the file list,
600 * 			otherwise it should be added on the executable list
601 * @param fsid		file system identifier of device
602 * @param fileid	file to add
603 * @param gen		generation of file
604 * @param fingerprint	fingerprint to add to the store
605 * @param flags		flags to set in the store
606 * @param fp_type	digest type
607 * @param override	if 1, override any values already stored
608 *
609 * @return 0 on success, otherwise an error code.
610 */
611int
612mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
613    unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN],
614    int flags, const char *fp_type, int override)
615{
616	struct mac_veriexec_fpops *fpops;
617	struct veriexec_dev_list *lp;
618	struct veriexec_devhead *head;
619	struct mac_veriexec_file_info *ip;
620	struct mac_veriexec_file_info *np = NULL;
621
622	/* Look up the device entry */
623	if (file_dev)
624		head = &veriexec_file_dev_head;
625	else
626		head = &veriexec_dev_head;
627	lp = find_veriexec_dev(fsid, head);
628
629	/* Look up the fingerprint operations for the digest type */
630	fpops = mac_veriexec_fingerprint_lookup_ops(fp_type);
631	if (fpops == NULL)
632		return (EOPNOTSUPP);
633
634search:
635	for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
636	     ip = LIST_NEXT(ip, entries)) {
637		  /* check for a dupe file in the list, skip if an entry
638		   * exists for this file except for when the flags contains
639		   * VERIEXEC_INDIRECT, always set the flags when it is so
640		   * we don't get a hole caused by conflicting flags on
641		   * hardlinked files.  XXX maybe we should validate
642		   * fingerprint is same and complain if it is not...
643		   */
644		if (ip->fileid == fileid && ip->gen == gen) {
645			if (override) {
646				/*
647				 * for a signed load we allow overrides,
648				 * otherwise fingerpints needed for pkg loads
649				 * can fail (the files are on temp device).
650				 */
651				ip->flags = flags;
652				ip->ops = fpops;
653				memcpy(ip->fingerprint, fingerprint,
654				    fpops->digest_len);
655			} else if ((flags & (VERIEXEC_INDIRECT|VERIEXEC_FILE)))
656				ip->flags |= flags;
657
658			if (np) {
659				/* unlikely but... we don't need it now. */
660				mtx_unlock(&ve_mutex);
661				free(np, M_VERIEXEC);
662				mtx_lock(&ve_mutex);
663			}
664			return (0);
665		}
666	}
667
668	/*
669	 * We may have been past here before...
670	 */
671	if (np == NULL) {
672		/*
673		 * We first try with mutex held and nowait.
674		 */
675		np = malloc(sizeof(struct mac_veriexec_file_info), M_VERIEXEC,
676		    M_NOWAIT);
677		if (np == NULL) {
678			/*
679			 * It was worth a try, now
680			 * drop mutex while we malloc.
681			 */
682			mtx_unlock(&ve_mutex);
683			np = malloc(sizeof(struct mac_veriexec_file_info),
684			    M_VERIEXEC, M_WAITOK);
685			mtx_lock(&ve_mutex);
686			/*
687			 * We now have to repeat our search!
688			 */
689			goto search;
690		}
691	}
692
693	/* Set up the meta-data entry */
694	ip = np;
695	ip->flags = flags;
696	ip->ops = fpops;
697	ip->fileid = fileid;
698	ip->gen = gen;
699	memcpy(ip->fingerprint, fingerprint, fpops->digest_len);
700
701	VERIEXEC_DEBUG(3, ("add file %ju.%lu (files=%d)\n",
702	    (uintmax_t)ip->fileid,
703	    ip->gen, file_dev));
704
705	/* Add the entry to the list */
706	LIST_INSERT_HEAD(&(lp->file_head), ip, entries);
707#ifdef DEBUG_VERIEXEC_FINGERPRINT
708	{
709		off_t offset;
710
711		printf("Stored %s fingerprint:\n", fp_type);
712		for (offset = 0; offset < fpops->digest_len; offset++)
713			printf("%02x", fingerprint[offset]);
714		printf("\n");
715	}
716#endif
717	return (0);
718}
719
720/**
721 * @brief Intialize the meta-data store
722 */
723void
724mac_veriexec_metadata_init(void)
725{
726
727	mtx_init(&ve_mutex, "veriexec lock", NULL, MTX_DEF);
728	LIST_INIT(&veriexec_dev_head);
729	LIST_INIT(&veriexec_file_dev_head);
730}
731