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