MAC/veriexec implements a verified execution environment using the MAC

framework.

The code is organized into a few distinct pieces:

* The meta-data store (in veriexec_metadata.c) which maps a file system
  identifier, file identifier, and generation key tuple to veriexec
  meta-data record.

* Fingerprint management (in veriexec_fingerprint.c) which deals with
  calculating the cryptographic hash for a file and verifying it. It also
  manages the loadable fingerprint modules.

* MAC policy implementation (in mac_veriexec.c) which implements the
  following MAC methods:

mpo_init
  Initializes the veriexec state, meta-data store, fingerprint modules,
  and registers mount and unmount EVENTHANDLERs

mpo_syscall
  Implements the following per-policy system calls:
  MAC_VERIEXEC_CHECK_FD_SYSCALL
    Check a file descriptor to see if the referenced file has a valid
    fingerprint.
  MAC_VERIEXEC_CHECK_PATH_SYSCALL
    Check a path to see if the referenced file has a valid fingerprint.

mpo_kld_check_load
  Check if loading a kld is allowed. This checks if the referenced vnode
  has a valid fingerprint.

mpo_mount_destroy_label
  Clears the veriexec slot data in a mount point label.

mpo_mount_init_label
  Initializes the veriexec slot data in a mount point label.
  The file system identifier is saved in the veriexec slot data.

mpo_priv_check
  Check if a process is allowed to write to /dev/kmem and /dev/mem
  devices.
  If a process is flagged as trusted, it is allowed to write.

mpo_proc_check_debug
  Check if a process is allowed to be debugged. If a process is not
  flagged with VERIEXEC_NOTRACE, then debugging is allowed.

mpo_vnode_check_exec
  Check is an exectuable is allowed to run. If veriexec is not enforcing
  or the executable has a valid fingerprint, then it is allowed to run.
  NOTE: veriexec will complain about mismatched fingerprints if it is
  active, regardless of the state of the enforcement.

mpo_vnode_check_open
  Check is a file is allowed to be opened. If verification was not
  requested, veriexec is not enforcing, or the file has a valid
  fingerprint, then veriexec will allow the file to be opened.

mpo_vnode_copy_label
  Copies the veriexec slot data from one label to another.

mpo_vnode_destroy_label
  Clears the veriexec slot data in a vnode label.

mpo_vnode_init_label
  Initializes the veriexec slot data in a vnode label.
  The fingerprint status for the file is stored in the veriexec slot data.

* Some sysctls, under security.mac.veriexec, for setting debug level,
  fetching the current state in a human-readable form, and dumping the
  fingerprint database are implemented.

* The MAC policy implementation source file also contains some utility
  functions.

* A set of fingerprint modules for the following cryptographic hash
  algorithms:
  RIPEMD-160, SHA1, SHA2-256, SHA2-384, SHA2-512

* Loadable module builds for MAC/veriexec and fingerprint modules.

 WARNING: Using veriexec with NFS (or other network-based) file systems is
          not recommended as one cannot guarantee the integrity of the files
          served, nor the uniqueness of file system identifiers which are
          used as key in the meta-data store.

Reviewed by:	ian, jtl
Obtained from:	Juniper Networks, Inc.
Differential Revision:	https://reviews.freebsd.org/D8554
This commit is contained in:
Stephen J. Kiernan 2018-06-20 00:41:30 +00:00
parent ec82884e87
commit fb47a3769c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=335399
18 changed files with 2559 additions and 0 deletions

View File

@ -3424,6 +3424,7 @@ dev/videomode/videomode.c optional videomode
dev/videomode/edid.c optional videomode
dev/videomode/pickmode.c optional videomode
dev/videomode/vesagtf.c optional videomode
dev/veriexec/verified_exec.c optional veriexec mac_veriexec
dev/vge/if_vge.c optional vge
dev/viapm/viapm.c optional viapm pci
dev/virtio/virtio.c optional virtio
@ -4892,6 +4893,14 @@ security/mac_portacl/mac_portacl.c optional mac_portacl
security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids
security/mac_stub/mac_stub.c optional mac_stub
security/mac_test/mac_test.c optional mac_test
security/mac_veriexec/mac_veriexec.c optional mac_veriexec
security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec
security/mac_veriexec/veriexec_metadata.c optional mac_veriexec
security/mac_veriexec/mac_veriexec_rmd160.c optional mac_veriexec_rmd160
security/mac_veriexec/mac_veriexec_sha1.c optional mac_veriexec_sha1
security/mac_veriexec/mac_veriexec_sha256.c optional mac_veriexec_sha256
security/mac_veriexec/mac_veriexec_sha384.c optional mac_veriexec_sha384
security/mac_veriexec/mac_veriexec_sha512.c optional mac_veriexec_sha512
teken/teken.c optional sc | vt
ufs/ffs/ffs_alloc.c optional ffs
ufs/ffs/ffs_balloc.c optional ffs

View File

@ -237,6 +237,12 @@ SUBDIR= \
mac_seeotheruids \
mac_stub \
mac_test \
mac_veriexec \
mac_veriexec_rmd160 \
mac_veriexec_sha1 \
mac_veriexec_sha256 \
mac_veriexec_sha384 \
mac_veriexec_sha512 \
malo \
md \
mdio \

View File

@ -0,0 +1,40 @@
# $FreeBSD$
.PATH: ${.PARSEDIR:H:H}/security/mac_veriexec
KMOD = mac_veriexec
SRCS = \
bus_if.h \
device_if.h \
vnode_if.h
SRCS += \
opt_capsicum.h \
opt_global.h \
opt_mac.h \
opt_veriexec.h
SRCS += \
mac_veriexec.c \
veriexec_fingerprint.c \
veriexec_metadata.c
EXPORT_SYMS+= ve_mutex \
mac_veriexec_in_state \
mac_veriexec_get_executable_flags
.if defined(KERNBUILDDIR)
MKDEP= -include ${KERNBUILDDIR}/opt_global.h
.else
CFLAGS+= -include opt_global.h
MKDEP= -include opt_global.h
opt_mac.h:
echo "#define MAC_DEBUG 1" >> ${.TARGET}
opt_global.h:
echo "#define MAC 1" > ${.TARGET}
.endif
.ifndef WITHOUT_VERIEXEC_DEBUG
CFLAGS+= -DVERIFIED_EXEC_DEBUG
.endif
.include <bsd.kmod.mk>

View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../security/mac_veriexec
.PATH: ${.CURDIR}/../../opencrypto
KMOD= mac_veriexec_rmd160
SRCS= mac_veriexec_rmd160.c rmd160.c
.include <bsd.kmod.mk>

View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.PARSEDIR:H:H}/security/mac_veriexec
.PATH: ${.PARSEDIR:H:H}/crypto
KMOD= mac_veriexec_sha1
SRCS= mac_veriexec_sha1.c sha1.c
.include <bsd.kmod.mk>

View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../security/mac_veriexec
.PATH: ${.CURDIR}/../../crypto/sha2
KMOD= mac_veriexec_sha256
SRCS= mac_veriexec_sha256.c sha256c.c
.include <bsd.kmod.mk>

View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../security/mac_veriexec
.PATH: ${.CURDIR}/../../crypto/sha2
KMOD= mac_veriexec_sha384
SRCS= mac_veriexec_sha384.c sha512c.c
.include <bsd.kmod.mk>

View File

@ -0,0 +1,10 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../security/mac_veriexec
.PATH: ${.CURDIR}/../../crypto/sha2
KMOD= mac_veriexec_sha512
SRCS= mac_veriexec_sha512.c sha512c.c
.include <bsd.kmod.mk>

View File

@ -0,0 +1,803 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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_capsicum.h"
#include "opt_mac.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/capsicum.h>
#include <sys/eventhandler.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/imgact.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/sbuf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <fs/nullfs/null.h>
#include <security/mac/mac_policy.h>
#include "mac_veriexec.h"
#include "mac_veriexec_internal.h"
#define SLOT(l) \
mac_label_get((l), mac_veriexec_slot)
#define SLOT_SET(l, v) \
mac_label_set((l), mac_veriexec_slot, (v))
#ifdef MAC_DEBUG
#define MAC_VERIEXEC_DBG(_lvl, _fmt, ...) \
do { \
VERIEXEC_DEBUG((_lvl), (MAC_VERIEXEC_FULLNAME ": " _fmt \
"\n", ##__VA_ARGS__)); \
} while(0)
#else
#define MAC_VERIEXEC_DBG(_lvl, _fmt, ...)
#endif
static int sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS);
static int sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS);
SYSCTL_DECL(_security_mac);
SYSCTL_NODE(_security_mac, OID_AUTO, veriexec, CTLFLAG_RW, 0,
"MAC/veriexec policy controls");
int mac_veriexec_debug;
SYSCTL_INT(_security_mac_veriexec, OID_AUTO, debug, CTLFLAG_RW,
&mac_veriexec_debug, 0, "Debug level");
static int mac_veriexec_state;
SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, state,
CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_mac_veriexec_state, "A",
"Verified execution subsystem state");
SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, db,
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP, 0, 0, sysctl_mac_veriexec_db,
"A", "Verified execution fingerprint database");
static int mac_veriexec_slot;
MALLOC_DEFINE(M_VERIEXEC, "veriexec", "Verified execution data");
/**
* @internal
* @brief Handler for security.mac.veriexec.db sysctl
*
* Display a human-readable form of the current fingerprint database.
*/
static int
sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS)
{
struct sbuf sb;
int error;
error = sysctl_wire_old_buffer(req, 0);
if (error != 0)
return (error);
sbuf_new_for_sysctl(&sb, NULL, 1024, req);
mac_veriexec_print_db(&sb);
error = sbuf_finish(&sb);
sbuf_delete(&sb);
return (error);
}
/**
* @internal
* @brief Generate human-readable output about the current verified execution
* state.
*
* @param sbp sbuf to write output to
*/
static void
mac_veriexec_print_state(struct sbuf *sbp)
{
if (mac_veriexec_state & VERIEXEC_STATE_INACTIVE)
sbuf_printf(sbp, "inactive ");
if (mac_veriexec_state & VERIEXEC_STATE_LOADED)
sbuf_printf(sbp, "loaded ");
if (mac_veriexec_state & VERIEXEC_STATE_ACTIVE)
sbuf_printf(sbp, "active ");
if (mac_veriexec_state & VERIEXEC_STATE_ENFORCE)
sbuf_printf(sbp, "enforce ");
if (mac_veriexec_state & VERIEXEC_STATE_LOCKED)
sbuf_printf(sbp, "locked ");
if (mac_veriexec_state != 0)
sbuf_trim(sbp);
}
/**
* @internal
* @brief Handler for security.mac.veriexec.state sysctl
*
* Display a human-readable form of the current verified execution subsystem
* state.
*/
static int
sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS)
{
struct sbuf sb;
int error;
sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
mac_veriexec_print_state(&sb);
sbuf_finish(&sb);
error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
sbuf_delete(&sb);
return (error);
}
/**
* @internal
* @brief Event handler called when a virtual file system is mounted.
*
* We need to record the file system identifier in the MAC per-policy slot
* assigned to veriexec, so we have a key to use in order to reference the
* mount point in the meta-data store.
*
* @param arg unused argument
* @param mp mount point that is being mounted
* @param fsrootvp vnode of the file system root
* @param td calling thread
*/
static void
mac_veriexec_vfs_mounted(void *arg __unused, struct mount *mp,
struct vnode *fsrootvp, struct thread *td)
{
struct vattr va;
int error;
error = VOP_GETATTR(fsrootvp, &va, td->td_ucred);
if (error)
return;
SLOT_SET(mp->mnt_label, va.va_fsid);
#ifdef MAC_DEBUG
MAC_VERIEXEC_DBG(3, "set fsid to %u for mount %p", va.va_fsid, mp);
#endif
}
/**
* @internal
* @brief Event handler called when a virtual file system is unmounted.
*
* If we recorded a file system identifier in the MAC per-policy slot assigned
* to veriexec, then we need to tell the meta-data store to clean up.
*
* @param arg unused argument
* @param mp mount point that is being unmounted
* @param td calling thread
*/
static void
mac_veriexec_vfs_unmounted(void *arg __unused, struct mount *mp,
struct thread *td)
{
dev_t fsid;
fsid = SLOT(mp->mnt_label);
if (fsid) {
MAC_VERIEXEC_DBG(3, "fsid %u, cleaning up mount", fsid);
mac_veriexec_metadata_unmounted(fsid, td);
}
}
/**
* @internal
* @brief The mount point is being initialized, set the value in the MAC
* per-policy slot for veriexec to zero.
*
* @note A value of zero in this slot indicates no file system identifier
* is assigned.
*
* @param label the label that is being initialized
*/
static void
mac_veriexec_mount_init_label(struct label *label)
{
SLOT_SET(label, 0);
}
/**
* @internal
* @brief The mount-point is being destroyed, reset the value in the MAC
* per-policy slot for veriexec back to zero.
*
* @note A value of zero in this slot indicates no file system identifier
* is assigned.
*
* @param label the label that is being destroyed
*/
static void
mac_veriexec_mount_destroy_label(struct label *label)
{
SLOT_SET(label, 0);
}
/**
* @internal
* @brief The vnode label is being initialized, set the value in the MAC
* per-policy slot for veriexec to @c FINGERPRINT_INVALID
*
* @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
*
* @param label the label that is being initialized
*/
static void
mac_veriexec_vnode_init_label(struct label *label)
{
SLOT_SET(label, FINGERPRINT_INVALID);
}
/**
* @internal
* @brief The vnode label is being destroyed, reset the value in the MAC
* per-policy slot for veriexec back to @c FINGERPRINT_INVALID
*
* @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
*
* @param label the label that is being destroyed
*/
static void
mac_veriexec_vnode_destroy_label(struct label *label)
{
SLOT_SET(label, FINGERPRINT_INVALID);
}
/**
* @internal
* @brief Copy the value in the MAC per-policy slot assigned to veriexec from
* the @p src label to the @p dest label
*/
static void
mac_veriexec_copy_label(struct label *src, struct label *dest)
{
SLOT_SET(dest, SLOT(src));
}
/**
* @internal
* @brief Check if the requested process can be debugged
*
* @param cred credentials to use
* @param p process to debug
*
* @return 0 if debugging is allowed, otherwise an error code.
*/
static int
mac_veriexec_proc_check_debug(struct ucred *cred, struct proc *p)
{
int error, flags;
/* If we are not enforcing veriexec, nothing for us to check */
if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
return (0);
error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
if (error != 0)
return (0);
return ((flags & VERIEXEC_NOTRACE) ? EACCES : 0);
}
/**
* @internal
* @brief A KLD load has been requested and needs to be validated.
*
* @param cred credentials to use
* @param vp vnode of the KLD that has been requested
* @param vlabel vnode label assigned to the vnode
*
* @return 0 if the KLD load is allowed, otherwise an error code.
*/
static int
mac_veriexec_kld_check_load(struct ucred *cred, struct vnode *vp,
struct label *vlabel)
{
struct vattr va;
struct thread *td = curthread;
fingerprint_status_t status;
int error;
/*
* If we are not actively enforcing, allow it
*/
if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
return (0);
/* Get vnode attributes */
error = VOP_GETATTR(vp, &va, cred);
if (error)
return (error);
/*
* Fetch the fingerprint status for the vnode
* (starting with files first)
*/
error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
VERIEXEC_FILES_FIRST);
if (error && error != EAUTH)
return (error);
/*
* By now we should have status...
*/
status = mac_veriexec_get_fingerprint_status(vp);
switch (status) {
case FINGERPRINT_FILE:
case FINGERPRINT_VALID:
case FINGERPRINT_INDIRECT:
if (error)
return (error);
break;
default:
/*
* kldload should fail unless there is a valid fingerprint
* registered.
*/
MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev %u, "
"file %lu.%lu\n", status, va.va_fsid, va.va_fileid,
va.va_gen);
return (EAUTH);
}
/* Everything is good, allow the KLD to be loaded */
return (0);
}
/**
* @internal
* @brief Check privileges that veriexec needs to be concerned about.
*
* The following privileges are checked by this function:
* - PRIV_KMEM_WRITE\n
* Check if writes to /dev/mem and /dev/kmem are allowed\n
* (Only trusted processes are allowed)
*
* @param cred credentials to use
* @param priv privilege to check
*
* @return 0 if the privilege is allowed, error code otherwise.
*/
static int
mac_veriexec_priv_check(struct ucred *cred, int priv)
{
/* If we are not enforcing veriexec, nothing for us to check */
if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
return (0);
switch (priv) {
case PRIV_KMEM_WRITE:
if (!mac_veriexec_proc_is_trusted(cred, curproc))
return (EPERM);
break;
default:
break;
}
return (0);
}
/**
* @internal
* @brief A program is being executed and needs to be validated.
*
* @param cred credentials to use
* @param vp vnode of the program that is being executed
* @param label vnode label assigned to the vnode
* @param imgp parameters for the image to be executed
* @param execlabel optional exec label
*
* @return 0 if the program should be allowed to execute, otherwise an error
* code.
*/
static int
mac_veriexec_vnode_check_exec(struct ucred *cred __unused,
struct vnode *vp __unused, struct label *label __unused,
struct image_params *imgp, struct label *execlabel __unused)
{
struct thread *td = curthread;
int error;
error = mac_veriexec_fingerprint_check_image(imgp, 0, td);
return (error);
}
/**
* @brief Check fingerprint for the specified vnode and validate it
*
* @param cred credentials to use
* @param vp vnode of the file
* @param accmode access mode to check (read, write, append, create,
* verify, etc.)
*
* @return 0 if the file validated, otherwise an error code.
*/
static int
mac_veriexec_check_vp(struct ucred *cred, struct vnode *vp, accmode_t accmode)
{
struct vattr va;
struct thread *td = curthread;
fingerprint_status_t status;
int error;
/* Get vnode attributes */
error = VOP_GETATTR(vp, &va, cred);
if (error)
return (error);
/* Get the fingerprint status for the file */
error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
VERIEXEC_FILES_FIRST);
if (error && error != EAUTH)
return (error);
/*
* By now we should have status...
*/
status = mac_veriexec_get_fingerprint_status(vp);
if (accmode & VWRITE) {
/*
* If file has a fingerprint then deny the write request,
* otherwise invalidate the status so we don't keep checking
* for the file having a fingerprint.
*/
switch (status) {
case FINGERPRINT_FILE:
case FINGERPRINT_VALID:
case FINGERPRINT_INDIRECT:
MAC_VERIEXEC_DBG(2,
"attempted write to fingerprinted file for dev "
"%u, file %lu.%lu\n", va.va_fsid,
va.va_fileid, va.va_gen);
return (EPERM);
default:
break;
}
}
if (accmode & VVERIFY) {
switch (status) {
case FINGERPRINT_FILE:
case FINGERPRINT_VALID:
case FINGERPRINT_INDIRECT:
if (error)
return (error);
break;
default:
/*
* Caller wants open to fail unless there is a valid
* fingerprint registered.
*/
MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev "
"%u, file %lu.%lu\n", status, va.va_fsid,
va.va_fileid, va.va_gen);
return (EAUTH);
}
}
return (0);
}
/**
* @brief Opening a file has been requested and may need to be validated.
*
* @param cred credentials to use
* @param vp vnode of the file to open
* @param label vnode label assigned to the vnode
* @param accmode access mode to use for opening the file (read, write,
* append, create, verify, etc.)
*
* @return 0 if opening the file should be allowed, otherwise an error code.
*/
static int
mac_veriexec_vnode_check_open(struct ucred *cred, struct vnode *vp,
struct label *label __unused, accmode_t accmode)
{
int error;
/*
* Look for the file on the fingerprint lists iff it has not been seen
* before.
*/
if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
return (0);
error = mac_veriexec_check_vp(cred, vp, accmode);
return (error);
}
/**
* @internal
* @brief Initialize the mac_veriexec MAC policy
*
* @param mpc MAC policy configuration
*/
static void
mac_veriexec_init(struct mac_policy_conf *mpc __unused)
{
/* Initialize state */
mac_veriexec_state = VERIEXEC_STATE_INACTIVE;
/* Initialize meta-data storage */
mac_veriexec_metadata_init();
/* Initialize fingerprint ops */
mac_veriexec_fingerprint_init();
/* Register event handlers */
EVENTHANDLER_REGISTER(vfs_mounted, mac_veriexec_vfs_mounted, NULL,
EVENTHANDLER_PRI_FIRST);
EVENTHANDLER_REGISTER(vfs_unmounted, mac_veriexec_vfs_unmounted, NULL,
EVENTHANDLER_PRI_LAST);
}
/**
* @internal
* @brief MAC policy-specific syscall for mac_veriexec
*
* The following syscalls are implemented:
* - @c MAC_VERIEXEC_CHECK_SYSCALL
* Check if the file referenced by a file descriptor has a fingerprint
* registered in the meta-data store.
*
* @param td calling thread
* @param call system call number
* @param arg arugments to the syscall
*
* @return 0 on success, otherwise an error code.
*/
static int
mac_veriexec_syscall(struct thread *td, int call, void *arg)
{
struct image_params img;
struct nameidata nd;
cap_rights_t rights;
struct vattr va;
struct file *fp;
int error;
switch (call) {
case MAC_VERIEXEC_CHECK_FD_SYSCALL:
/* Get the vnode associated with the file descriptor passed */
error = getvnode(td, (uintptr_t) arg, cap_rights_init(&rights,
CAP_READ), &fp);
if (error)
return (error);
if (fp->f_type != DTYPE_VNODE) {
MAC_VERIEXEC_DBG(3, "MAC_VERIEXEC_CHECK_SYSCALL: "
"file is not vnode type (type=0x%x)",
fp->f_type);
error = EINVAL;
goto cleanup_file;
}
/*
* setup the bits of image_params that are used by
* mac_veriexec_check_fingerprint().
*/
bzero(&img, sizeof(img));
img.proc = td->td_proc;
img.vp = fp->f_vnode;
img.attr = &va;
/*
* Get vnode attributes
* (need to obtain a lock on the vnode first)
*/
vn_lock(img.vp, LK_EXCLUSIVE | LK_RETRY);
error = VOP_GETATTR(fp->f_vnode, &va, td->td_ucred);
if (error)
goto check_done;
MAC_VERIEXEC_DBG(2, "mac_veriexec_fingerprint_check_image: "
"va_mode=%o, check_files=%d\n", va.va_mode,
((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0));
error = mac_veriexec_fingerprint_check_image(&img,
((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0), td);
check_done:
/* Release the lock we obtained earlier */
VOP_UNLOCK(img.vp, 0);
cleanup_file:
fdrop(fp, td);
break;
case MAC_VERIEXEC_CHECK_PATH_SYSCALL:
/* Look up the path to get the vnode */
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
UIO_USERSPACE, arg, td);
error = namei(&nd);
if (error != 0)
break;
NDFREE(&nd, NDF_ONLY_PNBUF);
/* Check the fingerprint status of the vnode */
error = mac_veriexec_check_vp(td->td_ucred, nd.ni_vp, VVERIFY);
vput(nd.ni_vp);
break;
default:
error = EOPNOTSUPP;
}
return (error);
}
static struct mac_policy_ops mac_veriexec_ops =
{
.mpo_init = mac_veriexec_init,
.mpo_syscall = mac_veriexec_syscall,
.mpo_kld_check_load = mac_veriexec_kld_check_load,
.mpo_mount_destroy_label = mac_veriexec_mount_destroy_label,
.mpo_mount_init_label = mac_veriexec_mount_init_label,
.mpo_priv_check = mac_veriexec_priv_check,
.mpo_proc_check_debug = mac_veriexec_proc_check_debug,
.mpo_vnode_check_exec = mac_veriexec_vnode_check_exec,
.mpo_vnode_check_open = mac_veriexec_vnode_check_open,
.mpo_vnode_copy_label = mac_veriexec_copy_label,
.mpo_vnode_destroy_label = mac_veriexec_vnode_destroy_label,
.mpo_vnode_init_label = mac_veriexec_vnode_init_label,
};
MAC_POLICY_SET(&mac_veriexec_ops, mac_veriexec, MAC_VERIEXEC_FULLNAME,
MPC_LOADTIME_FLAG_NOTLATE, &mac_veriexec_slot);
MODULE_VERSION(mac_veriexec, 1);
/**
* @brief Get the fingerprint status set on a vnode.
*
* @param vp vnode to obtain fingerprint status from
*
* @return Fingerprint status assigned to the vnode.
*/
fingerprint_status_t
mac_veriexec_get_fingerprint_status(struct vnode *vp)
{
fingerprint_status_t fps;
fps = SLOT(vp->v_label);
switch (fps) {
case FINGERPRINT_VALID:
case FINGERPRINT_INDIRECT:
case FINGERPRINT_FILE:
break;
default:
/* we may need to recurse */
if (strcmp(vp->v_tag, "null") == 0) {
struct vnode *ldvp;
ldvp = NULLVPTOLOWERVP(vp);
return mac_veriexec_get_fingerprint_status(ldvp);
}
break;
}
return fps;
}
/**
* @brief Get the current verified execution subsystem state.
*
* @return Current set of verified execution subsystem state flags.
*/
int
mac_veriexec_get_state(void)
{
return (mac_veriexec_state);
}
/**
* @brief Determine if the verified execution subsystem state has specific
* flags set.
*
* @param state mask of flags to check
*
* @return State flags set within the masked bits
*/
int
mac_veriexec_in_state(int state)
{
return (mac_veriexec_state & state);
}
/**
* @brief Set the fingerprint status for a vnode
*
* Fingerprint status is stored in the MAC per-policy slot assigned to
* mac_veriexec.
*
* @param vp vnode to store the fingerprint status on
* @param fp_status fingerprint status to store
*/
void
mac_veriexec_set_fingerprint_status(struct vnode *vp,
fingerprint_status_t fp_status)
{
/* recurse until we find the real storage */
if (strcmp(vp->v_tag, "null") == 0) {
struct vnode *ldvp;
ldvp = NULLVPTOLOWERVP(vp);
mac_veriexec_set_fingerprint_status(ldvp, fp_status);
return;
}
SLOT_SET(vp->v_label, fp_status);
}
/**
* @brief Set verified execution subsystem state flags
*
* @note Flags can only be added to the current state, not removed.
*
* @param state state flags to add to the current state
*/
void
mac_veriexec_set_state(int state)
{
mac_veriexec_state |= state;
}
/**
* @brief Determine if the process is trusted
*
* @param cred credentials to use
* @param p the process in question
*
* @return 1 if the process is trusted, otherwise 0.
*/
int
mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p)
{
int error, flags;
error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
/* Any errors, deny access */
if (error != 0)
return (0);
/* Check that the trusted flag is set */
return ((flags & VERIEXEC_TRUSTED) == VERIEXEC_TRUSTED);
}

View File

@ -0,0 +1,161 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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.
*/
#ifndef _SECURITY_MAC_VERIEXEC_H
#define _SECURITY_MAC_VERIEXEC_H
#ifdef _KERNEL
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/module.h>
#endif
/**
* Name of the MAC module
*/
#define MAC_VERIEXEC_NAME "mac_veriexec"
/* MAC/veriexec syscalls */
#define MAC_VERIEXEC_CHECK_FD_SYSCALL 1
#define MAC_VERIEXEC_CHECK_PATH_SYSCALL 2
/**
* Enough room for the largest signature...
*/
#define MAXFINGERPRINTLEN 64 /* enough room for largest signature */
/*
* Types of veriexec inodes we can have
*/
#define VERIEXEC_INDIRECT (1<<0) /* Only allow indirect execution */
#define VERIEXEC_FILE (1<<1) /* Fingerprint of a plain file */
#define VERIEXEC_NOTRACE (1<<2) /**< PTRACE not allowed */
#define VERIEXEC_TRUSTED (1<<3) /**< Safe to write /dev/mem */
/* XXX these are currently unimplemented */
#define VERIEXEC_NOFIPS (1<<4) /**< Not allowed in FIPS mode */
#define VERIEXEC_STATE_INACTIVE 0 /**< Ignore */
#define VERIEXEC_STATE_LOADED (1<<0) /**< Sigs have been loaded */
#define VERIEXEC_STATE_ACTIVE (1<<1) /**< Pay attention to it */
#define VERIEXEC_STATE_ENFORCE (1<<2) /**< Fail execs for files that do not
match signature */
#define VERIEXEC_STATE_LOCKED (1<<3) /**< Do not allow further changes */
#ifdef _KERNEL
/**
* Version of the MAC/veriexec module
*/
#define MAC_VERIEXEC_VERSION 1
/* Valid states for the fingerprint flag - if signed exec is being used */
typedef enum fingerprint_status {
FINGERPRINT_INVALID, /**< Fingerprint has not been evaluated */
FINGERPRINT_VALID, /**< Fingerprint evaluated and matches list */
FINGERPRINT_INDIRECT, /**< Fingerprint eval'd/matched but only
indirect execs allowed */
FINGERPRINT_FILE, /**< Fingerprint evaluated/matched but
not executable */
FINGERPRINT_NOMATCH, /**< Fingerprint evaluated but does not match */
FINGERPRINT_NOENTRY, /**< Fingerprint evaluated but no list entry */
FINGERPRINT_NODEV, /**< Fingerprint evaluated but no dev list */
} fingerprint_status_t;
typedef void (*mac_veriexec_fpop_init_t)(void *);
typedef void (*mac_veriexec_fpop_update_t)(void *, const uint8_t *, size_t);
typedef void (*mac_veriexec_fpop_final_t)(uint8_t *, void *);
struct mac_veriexec_fpops {
const char *type;
size_t digest_len;
size_t context_size;
mac_veriexec_fpop_init_t init;
mac_veriexec_fpop_update_t update;
mac_veriexec_fpop_final_t final;
LIST_ENTRY(mac_veriexec_fpops) entries;
};
/**
* Verified execution subsystem debugging level
*/
extern int mac_veriexec_debug;
/**
* @brief Define a fingerprint module.
*
* @param _name Name of the fingerprint module
* @param _digest_len Length of the digest string, in number of characters
* @param _context_size Size of the context structure, in bytes
* @param _init Initialization function of type
* mac_veriexec_fpop_init_t
* @param _update Update function of type mac_veriexec_fpop_update_t
* @param _final Finalize function of type mac_veriexec_fpop_final_t
* @param _vers Module version
*/
#define MAC_VERIEXEC_FPMOD(_name, _digest_len, _context_size, _init, \
_update, _final, _vers) \
static struct mac_veriexec_fpops \
mac_veriexec_##_name##_fpops = { \
.type = #_name, \
.digest_len = _digest_len, \
.context_size = _context_size, \
.init = _init, \
.update = _update, \
.final = _final, \
}; \
static moduledata_t mac_veriexec_##_name##_mod = { \
"mac_veriexec/" #_name, \
mac_veriexec_fingerprint_modevent, \
&(mac_veriexec_##_name##_fpops) \
}; \
MODULE_VERSION(mac_veriexec_##_name, _vers); \
DECLARE_MODULE(mac_veriexec_##_name, \
mac_veriexec_##_name##_mod, SI_SUB_MAC_POLICY, \
SI_ORDER_ANY); \
MODULE_DEPEND(mac_veriexec_##_name, mac_veriexec, \
MAC_VERIEXEC_VERSION, MAC_VERIEXEC_VERSION, \
MAC_VERIEXEC_VERSION)
/*
* The following function should not be called directly. The prototype is
* included here to satisfy the compiler when using the macro above.
*/
int mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data);
/*
* Public functions
*/
int mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN],
int flags, const char *fp_type, int override);
int mac_veriexec_metadata_has_file(dev_t fsid, long fileid,
unsigned long gen);
int mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p);
#endif
#endif /* _SECURITY_MAC_VERIEXEC_H */

View File

@ -0,0 +1,103 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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.
*/
#ifndef _SECURITY_MAC_VERIEXEC_INTERNAL_H
#define _SECURITY_MAC_VERIEXEC_INTERNAL_H
#ifndef _KERNEL
#error "no user-serviceable parts inside"
#endif
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#define MAC_VERIEXEC_FULLNAME "MAC/veriexec"
#define VERIEXEC_FILES_FIRST 1
#if defined(VERIFIED_EXEC_DEBUG) || defined(VERIFIED_EXEC_DEBUG_VERBOSE)
# define VERIEXEC_DEBUG(n, x) if (mac_veriexec_debug > (n)) printf x
#else
# define VERIEXEC_DEBUG(n, x)
#endif
struct mac_veriexec_file_info
{
int flags;
long fileid;
unsigned long gen;
struct mac_veriexec_fpops *ops;
unsigned char fingerprint[MAXFINGERPRINTLEN];
LIST_ENTRY(mac_veriexec_file_info) entries;
};
MALLOC_DECLARE(M_VERIEXEC);
SYSCTL_DECL(_security_mac_veriexec);
struct cred;
struct image_params;
struct proc;
struct sbuf;
struct thread;
struct ucred;
struct vattr;
struct vnode;
int mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
struct vattr *vap, struct thread *td, int check_files);
int mac_veriexec_metadata_get_executable_flags(struct ucred *cred,
struct proc *p, int *flags, int check_files);
int mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid,
unsigned long gen, int *flags, int check_files);
void mac_veriexec_metadata_init(void);
void mac_veriexec_metadata_print_db(struct sbuf *sbp);
int mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td);
int mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops);
int mac_veriexec_fingerprint_check_image(struct image_params *imgp,
int check_files, struct thread *td);
int mac_veriexec_fingerprint_check_vnode(struct vnode *vp,
struct mac_veriexec_file_info *ip, struct thread *td,
off_t file_size, unsigned char *fingerprint);
void mac_veriexec_fingerprint_init(void);
struct mac_veriexec_fpops *
mac_veriexec_fingerprint_lookup_ops(const char *type);
fingerprint_status_t
mac_veriexec_get_fingerprint_status(struct vnode *vp);
int mac_veriexec_get_state(void);
int mac_veriexec_in_state(int state);
void mac_veriexec_print_db(struct sbuf *);
void mac_veriexec_set_fingerprint_status(struct vnode *vp,
fingerprint_status_t fp_status);
void mac_veriexec_set_state(int state);
#endif /* !_SECURITY_MAC_VERIEXEC_INTERNAL_H */

View File

@ -0,0 +1,45 @@
/*
* $FreeBSD$
*
* Copyright (c) 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <opencrypto/rmd160.h>
#include <security/mac_veriexec/mac_veriexec.h>
#ifndef RMD160_DIGEST_LENGTH
#define RMD160_DIGEST_LENGTH 20
#endif
MAC_VERIEXEC_FPMOD(RMD160, RMD160_DIGEST_LENGTH, sizeof(RMD160_CTX),
(mac_veriexec_fpop_init_t) RMD160Init,
(mac_veriexec_fpop_update_t) RMD160Update,
(mac_veriexec_fpop_final_t) RMD160Final, 1);

View File

@ -0,0 +1,49 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <crypto/sha1.h>
#include <security/mac_veriexec/mac_veriexec.h>
#define SHA1_Init (mac_veriexec_fpop_init_t) sha1_init
#define SHA1_Update (mac_veriexec_fpop_update_t) sha1_loop
static void
SHA1_Final(unsigned char *buf, void *ctx)
{
sha1_result((SHA1_CTX *) ctx, (caddr_t) buf);
}
MAC_VERIEXEC_FPMOD(SHA1, SHA1_RESULTLEN, sizeof(SHA1_CTX), SHA1_Init,
SHA1_Update, SHA1_Final, 1);

View File

@ -0,0 +1,41 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <crypto/sha2/sha256.h>
#include <security/mac_veriexec/mac_veriexec.h>
MAC_VERIEXEC_FPMOD(SHA256, SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX),
(mac_veriexec_fpop_init_t) SHA256_Init,
(mac_veriexec_fpop_update_t) SHA256_Update,
(mac_veriexec_fpop_final_t) SHA256_Final, 1);

View File

@ -0,0 +1,41 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <crypto/sha2/sha384.h>
#include <security/mac_veriexec/mac_veriexec.h>
MAC_VERIEXEC_FPMOD(SHA384, SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX),
(mac_veriexec_fpop_init_t) SHA384_Init,
(mac_veriexec_fpop_update_t) SHA384_Update,
(mac_veriexec_fpop_final_t) SHA384_Final, 1);

View File

@ -0,0 +1,41 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, Juniper Networks, Inc.
* All rights reserved.
*
* 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 <sys/param.h>
#include <sys/systm.h>
#include <crypto/sha2/sha512.h>
#include <security/mac_veriexec/mac_veriexec.h>
MAC_VERIEXEC_FPMOD(SHA512, SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX),
(mac_veriexec_fpop_init_t) SHA512_Init,
(mac_veriexec_fpop_update_t) SHA512_Update,
(mac_veriexec_fpop_final_t) SHA512_Final, 1);

View File

@ -0,0 +1,443 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, 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/imgact.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sbuf.h>
#include <sys/syslog.h>
#include <sys/vnode.h>
#include "mac_veriexec.h"
#include "mac_veriexec_internal.h"
/**
* @var fpops_list
* @internal
* @brief Fingerprint operations list
*
* This is essentially the list of fingerprint modules currently loaded
*/
static LIST_HEAD(fpopshead, mac_veriexec_fpops) fpops_list;
static int mac_veriexec_late;
static int sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS);
SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, algorithms,
CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_mac_veriexec_algorithms, "A",
"Verified execution supported hashing algorithms");
static int
sysctl_mac_veriexec_algorithms(SYSCTL_HANDLER_ARGS)
{
struct sbuf sb;
struct mac_veriexec_fpops *fpops;
int algorithms, error;
algorithms = 0;
sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
LIST_FOREACH(fpops, &fpops_list, entries) {
if (algorithms++)
sbuf_printf(&sb, " ");
sbuf_printf(&sb, "%s", fpops->type);
}
sbuf_finish(&sb);
error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
sbuf_delete(&sb);
return (error);
}
/**
* @internal
* @brief Consistently identify file encountering errors
*
* @param imgp image params to display
* @param td calling thread
* @param msg message to display
*
* @return String form of the information stored in @p imgp
*/
static void
identify_error (struct image_params *imgp, struct thread *td, const char *msg)
{
struct proc *parent;
pid_t ppid, gppid;
parent = imgp->proc->p_pptr;
ppid = (parent != NULL) ? parent->p_pid : 0;
gppid = (parent != NULL && parent->p_pptr != NULL) ?
parent->p_pptr->p_pid : 0;
log(LOG_ERR, MAC_VERIEXEC_FULLNAME ": %s (file=%s fsid=%lu fileid=%lu "
"gen=%lu uid=%u pid=%u ppid=%u gppid=%u)", msg,
(imgp->args != NULL) ? imgp->args->fname : "",
imgp->attr->va_fsid, imgp->attr->va_fileid, imgp->attr->va_gen,
td->td_ucred->cr_ruid, imgp->proc->p_pid, ppid, gppid);
}
/**
* @internal
* @brief Check the fingerprint type for the given file and evaluate the
* fingerprint for that file.
*
* It is assumed that @p fingerprint has sufficient storage to hold the
* resulting fingerprint string.
*
* @param vp vnode to check
* @param ip file info from the meta-data store
* @param td calling thread
* @param file_size size of the file to read
* @param fingerprint resulting fingerprint
*
* @return 0 on success, otherwise an error code.
*/
static int
evaluate_fingerprint(struct vnode *vp, struct mac_veriexec_file_info *ip,
struct thread *td, off_t file_size, unsigned char *fingerprint)
{
uint8_t *filebuf;
void *ctx;
off_t offset;
size_t count, nread, resid;
int error = EINVAL;
filebuf = malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
ctx = malloc(ip->ops->context_size, M_VERIEXEC, M_WAITOK);
(ip->ops->init)(ctx);
for (offset = 0; offset < file_size; offset += nread) {
if ((offset + PAGE_SIZE) > file_size)
count = file_size - offset;
else
count = PAGE_SIZE;
error = vn_rdwr_inchunks(UIO_READ, vp, filebuf, count, offset,
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid,
td);
if (error)
goto failed;
nread = count - resid;
(ip->ops->update)(ctx, filebuf, nread);
}
(ip->ops->final)(fingerprint, ctx);
#ifdef DEBUG_VERIEXEC_FINGERPRINT
for (offset = 0; offset < ip->ops->digest_len; offset++)
printf("%02x", fingerprint[offset]);
printf("\n");
#endif
failed:
free(ctx, M_VERIEXEC);
free(filebuf, M_VERIEXEC);
return (error);
}
/**
* @internal
* @brief Compare the two given fingerprints to see if they are the same.
*
* Differing fingerprint methods may have differing lengths which
* is handled by this routine.
*
* @param ip file info from the meta-data store
* @param digest digest to compare
*
* @return 0 if the fingerprints match and non-zero if they do not.
*/
static int
fingerprintcmp(struct mac_veriexec_file_info *ip, unsigned char *digest)
{
return memcmp(ip->fingerprint, digest, ip->ops->digest_len);
}
/**
* @brief Check if @p fingerprint matches the one associated with the vnode
* @p vp
*
* @param vp vnode to check
* @param ip file info from the meta-data store
* @param td calling thread
* @param file_size size of the file to read
* @param fingerprint fingerprint to compare
*
* @return 0 if they match, otherwise an error code.
*/
int
mac_veriexec_fingerprint_check_vnode(struct vnode *vp,
struct mac_veriexec_file_info *ip, struct thread *td, off_t file_size,
unsigned char *fingerprint)
{
int error;
/* reject fingerprint if writers are active */
if (vp->v_writecount)
return (ETXTBSY);
if ((vp->v_mount->mnt_flag & MNT_VERIFIED) != 0) {
VERIEXEC_DEBUG(2, ("file %lu.%lu on verified %s mount\n",
ip->fileid, ip->gen, vp->v_mount->mnt_vfc->vfc_name));
/*
* The VFS is backed by a file which has been verified.
* No need to waste time here.
*/
return (0);
}
error = evaluate_fingerprint(vp, ip, td, file_size, fingerprint);
if (error)
return (error);
if (fingerprintcmp(ip, fingerprint) != 0)
return (EAUTH);
return (0);
}
/**
* @brief Check a file signature and validate it.
*
* @param imgp parameters for the image to check
* @param check_files if 1, check the files list first, otherwise check the
* exectuables list first
* @param td calling thread
*
* @note Called with imgp->vp locked.
*
* @return 0 if the signature is valid, otherwise an error code.
*/
int
mac_veriexec_fingerprint_check_image(struct image_params *imgp,
int check_files, struct thread *td)
{
struct vnode *vp = imgp->vp;
int error;
fingerprint_status_t status;
if (!mac_veriexec_in_state(VERIEXEC_STATE_ACTIVE))
return 0;
error = mac_veriexec_metadata_fetch_fingerprint_status(vp, imgp->attr,
td, check_files);
if (error && error != EAUTH)
return (error);
/*
* By now status is set.
*/
status = mac_veriexec_get_fingerprint_status(vp);
switch (status) {
case FINGERPRINT_INVALID: /* should not happen */
identify_error(imgp, td, "got unexpected FINGERPRINT_INVALID");
error = EPERM;
break;
case FINGERPRINT_FILE:
if (!check_files) {
if (prison0.pr_securelevel > 1 ||
mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
error = EPERM;
}
break;
case FINGERPRINT_VALID: /* is ok - report so if debug is on */
VERIEXEC_DEBUG(4, ("Fingerprint matches\n"));
break;
case FINGERPRINT_INDIRECT: /* fingerprint ok but need to check
for direct execution */
if (!imgp->interpreted) {
identify_error(imgp, td, "attempted direct execution");
if (prison0.pr_securelevel > 1 ||
mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
error = EPERM;
}
break;
case FINGERPRINT_NOMATCH: /* does not match - whine about it */
identify_error(imgp, td,
"fingerprint does not match loaded value");
if (prison0.pr_securelevel > 1 ||
mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
error = EAUTH;
break;
case FINGERPRINT_NOENTRY: /* no entry in the list, complain */
identify_error(imgp, td, "no fingerprint");
if (prison0.pr_securelevel > 1 ||
mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
error = EAUTH;
break;
case FINGERPRINT_NODEV: /* no signatures for the device, complain */
identify_error(imgp, td, "no signatures for device");
if (prison0.pr_securelevel > 1 ||
mac_veriexec_in_state(VERIEXEC_STATE_ENFORCE))
error = EAUTH;
break;
default: /* this should never happen. */
identify_error(imgp, td, "invalid status field for vnode");
error = EPERM;
}
return error;
}
/**
* @brief Look up the fingerprint operations for a specific digest type
*
* @return A pointer to fingerprint operations, if found, or else @c NULL.
*/
struct mac_veriexec_fpops *
mac_veriexec_fingerprint_lookup_ops(const char *type)
{
struct mac_veriexec_fpops *fpops;
if (type == NULL)
return (NULL);
LIST_FOREACH(fpops, &fpops_list, entries) {
if (!strcasecmp(type, fpops->type))
break;
}
return (fpops);
}
/**
* @brief Add fingerprint operations for a specific digest type
*
* Any attempts to add a duplicate digest type results in an error.
*
* @return 0 if the ops were added successfully, otherwise an error code.
*/
int
mac_veriexec_fingerprint_add_ops(struct mac_veriexec_fpops *fpops)
{
/* Sanity check the ops */
if (fpops->type == NULL || fpops->digest_len == 0 ||
fpops->context_size == 0 || fpops->init == NULL ||
fpops->update == NULL || fpops->final == NULL)
return (EINVAL);
/* Make sure we do not already have ops for this digest type */
if (mac_veriexec_fingerprint_lookup_ops(fpops->type))
return (EEXIST);
/* Add the ops to the list */
LIST_INSERT_HEAD(&fpops_list, fpops, entries);
printf("MAC/veriexec fingerprint module loaded: %s\n", fpops->type);
return (0);
}
/**
* @brief Initialize the fingerprint operations list
*/
void
mac_veriexec_fingerprint_init(void)
{
LIST_INIT(&fpops_list);
}
/**
* @brief Handle fingerprint module events
*
* This function is called by the @c MAC_VERIEXEC_FPMOD macro.
*
* @param mod module information
* @param type event type
* @param data event-specific data
*
* @return On @c MOD_LOAD, 0 if the fingerprint ops were added successfully,
* otherwise an error code. All other event types result in an error code.
*/
int
mac_veriexec_fingerprint_modevent(module_t mod, int type, void *data)
{
struct mac_veriexec_fpops *fpops;
int error;
error = 0;
fpops = (struct mac_veriexec_fpops *) data;
switch (type) {
case MOD_LOAD:
/* We do not allow late loading of fingerprint modules */
if (mac_veriexec_late) {
printf("%s: can't load %s fingerprint module after "
"booting\n", __func__, fpops->type);
error = EBUSY;
break;
}
error = mac_veriexec_fingerprint_add_ops(fpops);
break;
case MOD_UNLOAD:
error = EBUSY;
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
/**
* @internal
* @brief Mark veriexec late initialization flag
*/
static void
mac_veriexec_late_init(void)
{
mac_veriexec_late = 1;
}
SYSINIT(mac_veriexec_late, SI_SUB_MAC_LATE, SI_ORDER_ANY,
mac_veriexec_late_init, NULL);

View File

@ -0,0 +1,727 @@
/*
* $FreeBSD$
*
* Copyright (c) 2011, 2012, 2013, 2015, 2016, 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>
#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 %lu.%lu on device %lu,"
" files=%d\n", fileid, gen, (unsigned long)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 %lu\n",
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);
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 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 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
*/
static struct mac_veriexec_file_info *
find_veriexec_file(dev_t fsid, long fileid, unsigned long gen, int *found_dev,
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 %lu, file %lu\n",
__func__, fsid, fileid));
/* 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);
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: %lu\n", 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 (find_veriexec_file(fsid, fileid, gen, NULL,
VERIEXEC_FILES_FIRST) != NULL);
}
/**
* @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);
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 seach, 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);
}
/**
* @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 found_dev;
ip = find_veriexec_file(fsid, fileid, gen, &found_dev, check_files);
if (ip == NULL)
return (ENOENT);
*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;
ip = find_veriexec_file(vap->va_fsid, vap->va_fileid,
vap->va_gen, &found_dev, check_files);
if (ip == NULL) {
status = (found_dev) ? FINGERPRINT_NOENTRY :
FINGERPRINT_NODEV;
VERIEXEC_DEBUG(3,
("fingerprint status is %d for dev %lu, file "
"%lu.%lu\n", status, vap->va_fsid, 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 %lu, file "
"%lu.%lu\n",
(status == FINGERPRINT_INDIRECT) ?
"indirect " :
(status == FINGERPRINT_FILE) ?
"file " : "", vap->va_fsid,
vap->va_fileid, vap->va_gen));
break;
case EAUTH:
#ifdef VERIFIED_EXEC_DEBUG_VERBOSE
{
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 %lu, file "
"%lu.%lu %s != %s\n", vap->va_fsid,
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);
}
/**
* 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],
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;
/* 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);
} 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);
VERIEXEC_DEBUG(3, ("add file %lu.%lu (files=%d)\n", 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 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);
}