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