]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/security/mac_veriexec/veriexec_metadata.c
Upgrade to version 3.1.4
[FreeBSD/FreeBSD.git] / sys / security / mac_veriexec / veriexec_metadata.c
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 /**
778  * @brief Intialize the meta-data store
779  */
780 void
781 mac_veriexec_metadata_init(void)
782 {
783
784         mtx_init(&ve_mutex, "veriexec lock", NULL, MTX_DEF);
785         LIST_INIT(&veriexec_dev_head);
786         LIST_INIT(&veriexec_file_dev_head);
787 }