root/sys/security/mac_veriexec/veriexec_metadata.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc.
 * All rights reserved.
 *
 * Originally derived from:
 *      $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>

#include "opt_mac.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/exec.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sbuf.h>
#ifdef MAC_VERIEXEC_DEBUG
#include <sys/syslog.h>
#endif
#include <sys/vnode.h>

#include "mac_veriexec.h"
#include "mac_veriexec_internal.h"

/**
 * @brief per-device meta-data storage
 */
struct veriexec_dev_list {
        dev_t fsid;     /**< file system identifier of the mount point */
        LIST_HEAD(filehead, mac_veriexec_file_info) file_head;
            /**< list of per-file meta-data information */
        LIST_ENTRY(veriexec_dev_list) entries;
            /**< next entries in the device list */
};

typedef LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_devhead_t;

/**
 * @brief Mutex to protect the meta-data store lists
 */
struct mtx ve_mutex;

/**
 * @brief Executables meta-data storage
 *
 * This is used to store the fingerprints for potentially-executable files.
 */
veriexec_devhead_t veriexec_dev_head;

/**
 * @brief Plain file meta-data storage
 *
 * This is used for files that are not allowed to be executed, but should
 * have fingerprint validation available.
 */
veriexec_devhead_t veriexec_file_dev_head;

/**
 * @internal
 * @brief Search the @p head meta-data list for the specified file identifier
 *     @p fileid in the file system identified by @p fsid
 *
 * If meta-data exists for file system identified by @p fsid, it has a
 * fingerprint list, and @p found_dev is not @c NULL then store true in the
 * location pointed to by @p found_dev
 *
 * @param head          meta-data list to search
 * @param fsid          file system identifier to look for
 * @param fileid        file to look for
 * @param gen           generation of file
 * @param found_dev     indicator that an entry for the file system was found
 *
 * @return A pointer to the meta-data inforation if meta-data exists for
 *     the specified file identifier, otherwise @c NULL
 */
static struct mac_veriexec_file_info *
get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid,
    unsigned long gen, int *found_dev)
{
        struct veriexec_dev_list *lp;
        struct mac_veriexec_file_info *ip, *tip;

        ip = NULL;

        /* Initialize the value found_dev, if non-NULL */
        if (found_dev != NULL)
                *found_dev = 0;

        VERIEXEC_DEBUG(3, ("searching for file %ju.%lu on device %ju,"
            " files=%d\n", (uintmax_t)fileid, gen, (uintmax_t)fsid,
            (head == &veriexec_file_dev_head)));

        /* Get a lock to access the list */
        mtx_lock(&ve_mutex);

        /* First, look for the file system */
        for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries))
                if (lp->fsid == fsid)
                        break;

        /* We found the file system in the list */
        if (lp != NULL) {
                VERIEXEC_DEBUG(3, ("found matching dev number %ju\n",
                    (uintmax_t)lp->fsid));

                /* If found_dev is non-NULL, store true there */
                if (found_dev != NULL)
                        *found_dev = 1;

                /* Next, look for the meta-data information for the file */
                LIST_FOREACH_SAFE(ip, &(lp->file_head), entries, tip) {
                        if (ip->fileid == fileid) {
                                if (ip->gen == gen)
                                        break;
                                /* we need to garbage collect */
                                LIST_REMOVE(ip, entries);
                                if (ip->label)
                                        free(ip->label, M_VERIEXEC);
                                free(ip, M_VERIEXEC);
                        }
                }
        }

        /* Release the lock we obtained earlier */
        mtx_unlock(&ve_mutex);

        /* Return the meta-data information we found, if anything */
        return (ip);
}

/**
 * @internal
 * @brief Display the fingerprint for each entry in the device list
 *
 * @param sbp           sbuf to write output to
 * @param lp            pointer to device list
 */
static void
mac_veriexec_print_db_dev_list(struct sbuf *sbp, struct veriexec_dev_list *lp)
{
        struct mac_veriexec_file_info *ip;

#define FPB(i) (ip->fingerprint[i])
        for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
            ip = LIST_NEXT(ip, entries))
                sbuf_printf(sbp, "  %ld: %u %ld [%02x %02x %02x %02x %02x "
                    "%02x %02x %02x...]\n", ip->fileid, ip->flags, ip->gen,
                    FPB(0), FPB(1), FPB(2), FPB(3), FPB(4), FPB(5), FPB(6),
                    FPB(7));
}

/**
 * @internal
 * @brief Display the device list
 *
 * @param sbp           sbuf to write output to
 * @param head          pointer to head of the device list
 */
static void
mac_veriexec_print_db_head(struct sbuf *sbp, struct veriexec_devhead *head)
{
        struct veriexec_dev_list *lp;

        for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) {
                sbuf_printf(sbp, " FS id: %ju\n", (uintmax_t)lp->fsid);
                mac_veriexec_print_db_dev_list(sbp, lp);
        }

}

/**
 * @internal
 * @brief Generate human-readable output for the current fingerprint database
 *
 * @param sbp   sbuf to write output to
 */
void
mac_veriexec_metadata_print_db(struct sbuf *sbp)
{
        struct {
                struct veriexec_devhead *h;
                const char *name;
        } fpdbs[] = {
                { &veriexec_file_dev_head, "regular files" },
                { &veriexec_dev_head, "executable files" },
        };
        int i;

        mtx_lock(&ve_mutex);
        for (i = 0; i < sizeof(fpdbs)/sizeof(fpdbs[0]); i++) {
                sbuf_printf(sbp, "%s fingerprint db:\n", fpdbs[i].name);
                mac_veriexec_print_db_head(sbp, fpdbs[i].h);
        }
        mtx_unlock(&ve_mutex);
}
/**
 * @brief Determine if the meta-data store has an entry for the specified file.
 *
 * @param fsid          file system identifier to look for
 * @param fileid        file to look for
 * @param gen           generation of file
 *
 * @return 1 if there is an entry in the meta-data store, 0 otherwise.
 */
int
mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen)
{

        return (mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
            NULL, VERIEXEC_FILES_FIRST) == 0);
}

/**
 * @brief Search the list of devices looking for the one given, in order to
 *     release the resources used by it.
 *
 * If found, free all file entries for it, and remove it from the list.
 *
 * @note Called with @a ve_mutex held
 *
 * @param fsid          file system identifier to look for
 * @param head          meta-data list to search
 *
 * @return 0 if the device entry was freed, otherwise an error code
 */
static int
free_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
{
        struct veriexec_dev_list *lp;
        struct mac_veriexec_file_info *ip, *nip;

        /* Look for the file system */
        for (lp = LIST_FIRST(head); lp != NULL;
             lp = LIST_NEXT(lp, entries))
                if (lp->fsid == fsid) break;

        /* If lp is NULL, we did not find it */
        if (lp == NULL)
                return ENOENT;

        /* Unhook lp, before we free it and its content */
        LIST_REMOVE(lp, entries);

        /* Release the lock */
        mtx_unlock(&ve_mutex);

        /* Free the file entries in the list */
        for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = nip) {
                nip = LIST_NEXT(ip, entries);
                LIST_REMOVE(ip, entries);
                if (ip->label)
                        free(ip->label, M_VERIEXEC);
                free(ip, M_VERIEXEC);
        }

        /* Free the meta-data entry for the device */
        free(lp, M_VERIEXEC);

        /* Re-acquire the lock */
        mtx_lock(&ve_mutex);
        return 0;
}

/**
 * @brief Search the list of devices looking for the one given.
 *
 * If it is not in the list then add it.
 *
 * @note Called with @a ve_mutex held
 *
 * @param fsid          file system identifier to look for
 * @param head          meta-data list to search
 *
 * @return A pointer to the meta-data entry for the device, if found or added,
 *     otherwise @c NULL
 */
static struct veriexec_dev_list *
find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
{
        struct veriexec_dev_list *lp;
        struct veriexec_dev_list *np = NULL;

search:
        /* Look for the file system */
        for (lp = LIST_FIRST(head); lp != NULL;
             lp = LIST_NEXT(lp, entries))
                if (lp->fsid == fsid) break;

        if (lp == NULL) {
                if (np == NULL) {
                        /*
                         * If pointer is null then entry not there,
                         * add a new one, first try to malloc while
                         * we hold mutex - should work most of the time.
                         */
                        np = malloc(sizeof(struct veriexec_dev_list),
                            M_VERIEXEC, M_NOWAIT);
                        if (np == NULL) {
                                /*
                                 * So much for that plan, dop the mutex
                                 * and repeat...
                                 */
                                mtx_unlock(&ve_mutex);
                                np = malloc(sizeof(struct veriexec_dev_list),
                                    M_VERIEXEC, M_WAITOK);
                                mtx_lock(&ve_mutex);
                                /*
                                 * Repeat the search, in case someone
                                 * added this while we slept.
                                 */
                                goto search;
                        }
                }
                if (np) {
                        /* Add the entry to the list */
                        lp = np;
                        LIST_INIT(&(lp->file_head));
                        lp->fsid = fsid;
                        LIST_INSERT_HEAD(head, lp, entries);
                }
        } else if (np) {
                /*
                 * Someone else did it while we slept.
                 */
                mtx_unlock(&ve_mutex);
                free(np, M_VERIEXEC);
                mtx_lock(&ve_mutex);
        }

        return (lp);
}

/**
 * @internal
 * @brief Allocate and initialize label record with the provided data.
 *
 * @param labelp        Location to store the initialized label
 * @param src           Pointer to label string to copy
 * @param srclen        Length of label string to copy
 *
 * @return Length of resulting label
 *
 * @note Called with ve_mutex locked.
 */
static size_t
mac_veriexec_init_label(char **labelp, size_t labellen, char *src,
    size_t srclen)
{
        char *label;

        label = *labelp;
        if (labellen < srclen) {
                mtx_unlock(&ve_mutex);
                if (label != NULL)
                        free(label, M_VERIEXEC);
                label = malloc(srclen, M_VERIEXEC, M_WAITOK);
                mtx_lock(&ve_mutex);
                labellen = srclen;
                *labelp = label;
        }
        memcpy(label, src, srclen);
        return labellen;
}

/**
 * @brief When a device is unmounted, we want to toss the signatures recorded
 *     against it.
 *
 * We are being called from unmount() with the root vnode just before it is
 * freed.
 *
 * @param fsid          file system identifier to look for
 * @param td            calling thread
 *
 * @return 0 on success, otherwise an error code.
 */
int
mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td)
{
    int error;

    /*
     * The device can have entries on both lists.
     */
    mtx_lock(&ve_mutex);
    error = free_veriexec_dev(fsid, &veriexec_dev_head);
    if (error && error != ENOENT) {
            mtx_unlock(&ve_mutex);
            return error;
    }
    error = free_veriexec_dev(fsid, &veriexec_file_dev_head);
    mtx_unlock(&ve_mutex);
    if (error && error != ENOENT) {
            return error;
    }
    return 0;
}

/**
 * @brief Return the flags assigned to the file identified by file system
 *        identifier @p fsid and file identifier @p fileid.
 *
 * @param fsid          file system identifier
 * @param fileid        file identifier within the file system
 * @param gen           generation of file
 * @param flags         pointer to location to store the flags
 * @param check_files   if 1, check the files list first, otherwise check the
 *                      exectuables list first
 *
 * @return 0 on success, otherwise an error code.
 */
int
mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen,
    int *flags, int check_files)
{
        struct mac_veriexec_file_info *ip;
        int error;

        error = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
            &ip, check_files);
        if (error != 0)
                return (error);

        *flags = ip->flags;
        return (0);
}

/**
 * @brief get the files for the specified process
 *
 * @param cred          credentials to use
 * @param p             process to get the flags for
 * @param flags         where to store the flags
 * @param check_files   if 1, check the files list first, otherwise check the
 *                      exectuables list first
 *
 * @return 0 if the process has an entry in the meta-data store, otherwise an
 *     error code
 */
int
mac_veriexec_metadata_get_executable_flags(struct ucred *cred, struct proc *p,
    int *flags, int check_files)
{
        struct vnode *proc_vn;
        struct vattr vap;
        int error;

        /* Get the text vnode for the process */
        proc_vn = p->p_textvp;
        if (proc_vn == NULL)
                return EINVAL;

        /* Get vnode attributes */
        error = VOP_GETATTR(proc_vn, &vap, cred);
        if (error)
                return error;

        error = mac_veriexec_metadata_get_file_flags(vap.va_fsid,
            vap.va_fileid, vap.va_gen, flags,
            (check_files == VERIEXEC_FILES_FIRST));

        return (error);
}

/**
 * @brief Ensure the fingerprint status for the vnode @p vp is assigned to its
 *     MAC label.
 *
 * @param vp            vnode to check
 * @param vap           vnode attributes to use
 * @param td            calling thread
 * @param check_files   if 1, check the files list first, otherwise check the
 *                      exectuables list first
 *
 * @return 0 on success, otherwise an error code.
 */
int
mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
    struct vattr *vap, struct thread *td, int check_files)
{
        unsigned char digest[MAXFINGERPRINTLEN];
        struct mac_veriexec_file_info *ip;
        int error, found_dev;
        fingerprint_status_t status;

        error = 0;
        ip = NULL;

        status = mac_veriexec_get_fingerprint_status(vp);
        if (status == FINGERPRINT_INVALID || status == FINGERPRINT_NODEV) {
                found_dev = 0;
                if (mac_veriexec_metadata_get_file_info(vap->va_fsid,
                    vap->va_fileid, vap->va_gen, &found_dev, &ip,
                    check_files) != 0) {
                        status = (found_dev) ? FINGERPRINT_NOENTRY :
                            FINGERPRINT_NODEV;
                        VERIEXEC_DEBUG(3,
                            ("fingerprint status is %d for dev %ju, file "
                            "%ju.%lu\n", status, (uintmax_t)vap->va_fsid,
                            (uintmax_t)vap->va_fileid, vap->va_gen));
                } else {
                        /*
                         * evaluate and compare fingerprint
                         */
                        error = mac_veriexec_fingerprint_check_vnode(vp, ip,
                            td, vap->va_size, digest);
                        switch (error) {
                        case 0:
                                /* Process flags */
                                if ((ip->flags & VERIEXEC_INDIRECT))
                                        status = FINGERPRINT_INDIRECT;
                                else if ((ip->flags & VERIEXEC_FILE))
                                        status = FINGERPRINT_FILE;
                                else
                                        status = FINGERPRINT_VALID;
                                VERIEXEC_DEBUG(2,
                                    ("%sfingerprint matches for dev %ju, file "
                                    "%ju.%lu\n",
                                     (status == FINGERPRINT_INDIRECT) ?
                                     "indirect " :
                                     (status == FINGERPRINT_FILE) ?
                                     "file " : "", (uintmax_t)vap->va_fsid,
                                     (uintmax_t)vap->va_fileid, vap->va_gen));
                                break;

                        case EAUTH:
#ifdef MAC_VERIEXEC_DEBUG
                                {
                                        char have[MAXFINGERPRINTLEN * 2 + 1];
                                        char want[MAXFINGERPRINTLEN * 2 + 1];
                                        int i, len;

                                        len = ip->ops->digest_len;
                                        for (i = 0; i < len; i++) {
                                                sprintf(&want[i * 2], "%02x",
                                                    ip->fingerprint[i]);
                                                sprintf(&have[i * 2], "%02x",
                                                    digest[i]);
                                        }
                                        log(LOG_ERR, MAC_VERIEXEC_FULLNAME
                                            ": fingerprint for dev %ju, file "
                                            "%ju.%lu %s != %s\n",
                                            (uintmax_t)vap->va_fsid,
                                            (uintmax_t)vap->va_fileid,
                                            vap->va_gen,
                                            have, want);
                                }
#endif
                                status = FINGERPRINT_NOMATCH;
                                break;
                        default:
                                VERIEXEC_DEBUG(2,
                                    ("fingerprint status error %d\n", error));
                                break;
                        }
                }
                mac_veriexec_set_fingerprint_status(vp, status);
        }
        return (error);
}

/**
 * Return label if we have one
 *
 * @param fsid         file system identifier to look for
 * @param fileid       file to look for
 * @param gen          generation of file
 * @param check_files  look at non-executable files?
 *
 * @return A pointer to the label or @c NULL
 */
const char *
mac_veriexec_metadata_get_file_label(dev_t fsid, long fileid,
    unsigned long gen, int check_files)
{
        struct mac_veriexec_file_info *ip;
        int error;

        error = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
            &ip, check_files);
        if (error)
                return (NULL);

        return ((ip->flags & VERIEXEC_LABEL) != 0 ? ip->label : NULL);
}

/**
 * Add a file and its fingerprint to the list of files attached
 * to the device @p fsid.
 *
 * Only add the entry if it is not already on the list.
 *
 * @note Called with @a ve_mutex held
 *
 * @param file_dev      if 1, the entry should be added on the file list,
 *                      otherwise it should be added on the executable list
 * @param fsid          file system identifier of device
 * @param fileid        file to add
 * @param gen           generation of file
 * @param fingerprint   fingerprint to add to the store
 * @param flags         flags to set in the store
 * @param fp_type       digest type
 * @param override      if 1, override any values already stored
 *
 * @return 0 on success, otherwise an error code.
 */
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)
{
        struct mac_veriexec_fpops *fpops;
        struct veriexec_dev_list *lp;
        struct veriexec_devhead *head;
        struct mac_veriexec_file_info *ip;
        struct mac_veriexec_file_info *np = NULL;

        /* Label and labellen must be set if VERIEXEC_LABEL is set */
        if ((flags & VERIEXEC_LABEL) != 0 && (label == NULL || labellen == 0))
                return (EINVAL);

        /* Look up the device entry */
        if (file_dev)
                head = &veriexec_file_dev_head;
        else
                head = &veriexec_dev_head;
        lp = find_veriexec_dev(fsid, head);

        /* Look up the fingerprint operations for the digest type */
        fpops = mac_veriexec_fingerprint_lookup_ops(fp_type);
        if (fpops == NULL)
                return (EOPNOTSUPP);

search:
        for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
             ip = LIST_NEXT(ip, entries)) {
                  /* check for a dupe file in the list, skip if an entry
                   * exists for this file except for when the flags contains
                   * VERIEXEC_INDIRECT, always set the flags when it is so
                   * we don't get a hole caused by conflicting flags on
                   * hardlinked files.  XXX maybe we should validate
                   * fingerprint is same and complain if it is not...
                   */
                if (ip->fileid == fileid && ip->gen == gen) {
                        if (override) {
                                /*
                                 * for a signed load we allow overrides,
                                 * otherwise fingerpints needed for pkg loads
                                 * can fail (the files are on temp device).
                                 */
                                ip->flags = flags;
                                ip->ops = fpops;
                                memcpy(ip->fingerprint, fingerprint,
                                    fpops->digest_len);
                                if (flags & VERIEXEC_LABEL) {
                                        ip->labellen = mac_veriexec_init_label(
                                            &ip->label, ip->labellen, label,
                                            labellen);
                                } else if (ip->labellen > 0) {
                                        free(ip->label, M_VERIEXEC);
                                        ip->labellen = 0;
                                        ip->label = NULL;
                                }
                        } else if ((flags & (VERIEXEC_INDIRECT|VERIEXEC_FILE)))
                                ip->flags |= flags;

                        if (np) {
                                /* unlikely but... we don't need it now. */
                                mtx_unlock(&ve_mutex);
                                free(np, M_VERIEXEC);
                                mtx_lock(&ve_mutex);
                        }
                        return (0);
                }
        }

        /*
         * We may have been past here before...
         */
        if (np == NULL) {
                /*
                 * We first try with mutex held and nowait.
                 */
                np = malloc(sizeof(struct mac_veriexec_file_info), M_VERIEXEC,
                    M_NOWAIT);
                if (np == NULL) {
                        /*
                         * It was worth a try, now
                         * drop mutex while we malloc.
                         */
                        mtx_unlock(&ve_mutex);
                        np = malloc(sizeof(struct mac_veriexec_file_info),
                            M_VERIEXEC, M_WAITOK);
                        mtx_lock(&ve_mutex);
                        /*
                         * We now have to repeat our search!
                         */
                        goto search;
                }
        }

        /* Set up the meta-data entry */
        ip = np;
        ip->flags = flags;
        ip->ops = fpops;
        ip->fileid = fileid;
        ip->gen = gen;
        ip->label = NULL;
        ip->labellen = 0;
        memcpy(ip->fingerprint, fingerprint, fpops->digest_len);
        if (flags & VERIEXEC_LABEL)
                ip->labellen = mac_veriexec_init_label(&ip->label,
                    ip->labellen, label, labellen);

        VERIEXEC_DEBUG(3, ("add file %ju.%lu (files=%d)\n",
            (uintmax_t)ip->fileid,
            ip->gen, file_dev));

        /* Add the entry to the list */
        LIST_INSERT_HEAD(&(lp->file_head), ip, entries);
#ifdef DEBUG_VERIEXEC_FINGERPRINT
        {
                off_t offset;

                printf("Stored %s fingerprint:\n", fp_type);
                for (offset = 0; offset < fpops->digest_len; offset++)
                        printf("%02x", fingerprint[offset]);
                printf("\n");
        }
#endif
        return (0);
}

/**
 * @brief Search the meta-data store for information on the specified file.
 *
 * @param fsid          file system identifier to look for
 * @param fileid        file to look for
 * @param gen           generation of file
 * @param found_dev     indicator that an entry for the file system was found
 * @param ipp           pointer to location to store the info pointer
 * @param check_files   if 1, check the files list first, otherwise check the
 *                      exectuables list first
 *
 * @return A pointer to the meta-data inforation if meta-data exists for
 *     the specified file identifier, otherwise @c NULL
 */
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)
{
        struct veriexec_devhead *search[3];
        struct mac_veriexec_file_info *ip;
        int x;

        /* Determine the order of the lists to search */
        if (check_files) {
                search[0] = &veriexec_file_dev_head;
                search[1] = &veriexec_dev_head;
        } else {
                search[0] = &veriexec_dev_head;
                search[1] = &veriexec_file_dev_head;
        }
        search[2] = NULL;

        VERIEXEC_DEBUG(3, ("%s: searching for dev %#jx, file %lu.%lu\n",
            __func__, (uintmax_t)fsid, fileid, gen));

        /* Search for the specified file */
        for (ip = NULL, x = 0; ip == NULL && search[x]; x++)
                ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev);

        if (ipp != NULL)
                *ipp = ip;
        if (ip == NULL)
                return (ENOENT);
        return (0);
}

/**
 * @brief Intialize the meta-data store
 */
void
mac_veriexec_metadata_init(void)
{

        mtx_init(&ve_mutex, "veriexec lock", NULL, MTX_DEF);
        LIST_INIT(&veriexec_dev_head);
        LIST_INIT(&veriexec_file_dev_head);
}