Add mac_veriexec_metadata_get_file_label to avoid the need to expose internals to other MAC modules. Obtained from: Juniper Networks, Inc.
820 lines
22 KiB
C
820 lines
22 KiB
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;
|
|
error = mac_veriexec_metadata_get_file_info(vap->va_fsid,
|
|
vap->va_fileid, vap->va_gen, &found_dev, &ip, check_files);
|
|
if (error != 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;
|
|
memcpy(ip->fingerprint, fingerprint, fpops->digest_len);
|
|
if (flags & VERIEXEC_LABEL)
|
|
ip->labellen = mac_veriexec_init_label(&ip->label,
|
|
ip->labellen, label, labellen);
|
|
else {
|
|
ip->label = NULL;
|
|
ip->labellen = 0;
|
|
}
|
|
|
|
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);
|
|
}
|