xref: /freebsd/sys/security/mac_veriexec/veriexec_metadata.c (revision 6eabf4ce35ec1c79ab9a9dbe70ac934d62d2cd5d)
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  */
55 struct 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 
63 typedef LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_devhead_t;
64 
65 /**
66  * @brief Mutex to protect the meta-data store lists
67  */
68 struct 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  */
75 veriexec_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  */
83 veriexec_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  */
103 static struct mac_veriexec_file_info *
get_veriexec_file(struct veriexec_devhead * head,dev_t fsid,long fileid,unsigned long gen,int * found_dev)104 get_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  */
165 static void
mac_veriexec_print_db_dev_list(struct sbuf * sbp,struct veriexec_dev_list * lp)166 mac_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  */
186 static void
mac_veriexec_print_db_head(struct sbuf * sbp,struct veriexec_devhead * head)187 mac_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  */
204 void
mac_veriexec_metadata_print_db(struct sbuf * sbp)205 mac_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  */
232 int
mac_veriexec_metadata_has_file(dev_t fsid,long fileid,unsigned long gen)233 mac_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  */
253 static int
free_veriexec_dev(dev_t fsid,struct veriexec_devhead * head)254 free_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  */
304 static struct veriexec_dev_list *
find_veriexec_dev(dev_t fsid,struct veriexec_devhead * head)305 find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
306 {
307 	struct veriexec_dev_list *lp;
308 	struct veriexec_dev_list *np = NULL;
309 
310 search:
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  */
372 static size_t
mac_veriexec_init_label(char ** labelp,size_t labellen,char * src,size_t srclen)373 mac_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  */
404 int
mac_veriexec_metadata_unmounted(dev_t fsid,struct thread * td)405 mac_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  */
439 int
mac_veriexec_metadata_get_file_flags(dev_t fsid,long fileid,unsigned long gen,int * flags,int check_files)440 mac_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  */
467 int
mac_veriexec_metadata_get_executable_flags(struct ucred * cred,struct proc * p,int * flags,int check_files)468 mac_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  */
504 int
mac_veriexec_metadata_fetch_fingerprint_status(struct vnode * vp,struct vattr * vap,struct thread * td,int check_files)505 mac_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  */
599 const char *
mac_veriexec_metadata_get_file_label(dev_t fsid,long fileid,unsigned long gen,int check_files)600 mac_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  */
634 int
mac_veriexec_metadata_add_file(int file_dev,dev_t fsid,long fileid,unsigned long gen,unsigned char fingerprint[MAXFINGERPRINTLEN],char * label,size_t labellen,int flags,const char * fp_type,int override)635 mac_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 
661 search:
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  */
775 int
mac_veriexec_metadata_get_file_info(dev_t fsid,long fileid,unsigned long gen,int * found_dev,struct mac_veriexec_file_info ** ipp,int check_files)776 mac_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  */
810 void
mac_veriexec_metadata_init(void)811 mac_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