Massive restructuring and cleanup of Veriexec, mainly in preparation
for work on some future functionality. - Veriexec data-structures are no longer exposed. - Thanks to using proplib for data passing now, the interface changes further to accomodate that. Introduce four new functions. First, veriexec_file_add(), to add a new file to be monitored by Veriexec, to replace both veriexec_load() and veriexec_hashadd(). veriexec_table_add(), to replace veriexec_newtable(), will be used to optimize hash table size (during preload), and finally, veriexec_convert(), to convert an internal entry to one userland can read. - Introduce veriexec_unmountchk(), to enforce Veriexec unmount policy. This cleans up a bit of code in kern/vfs_syscalls.c. - Rename veriexec_tblfind() with veriexec_table_lookup(), and make it static. More functions that became static: veriexec_fp_cmp(), veriexec_fp_calc(). - veriexec_verify() no longer returns the entry as well, but just sets a boolean indicating whether an entry was found or not. - veriexec_purge() now takes a struct vnode *. - veriexec_add_fp_name() was merged into veriexec_add_fp_ops(), that changed its name to veriexec_fpops_add(). veriexec_find_ops() was also renamed to veriexec_fpops_lookup(). Also on the fp-ops front, the three function types used to initialize, update, and finalize a hash context were renamed to veriexec_fpop_init_t, veriexec_fpop_update_t, and veriexec_fpop_final_t respectively. - Introduce a new malloc(9) type, M_VERIEXEC, and use it instead of M_TEMP, so we can tell exactly how much memory is used by Veriexec. - And, most importantly, whitespace and indentation nits. Built successfuly for amd64, i386, sparc, and sparc64. Tested on amd64.
This commit is contained in:
parent
2abb1c7f9b
commit
0c67c581a5
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: verified_exec.c,v 1.50 2006/11/28 22:22:02 elad Exp $ */
|
||||
/* $NetBSD: verified_exec.c,v 1.51 2006/11/30 01:09:47 elad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2005 Elad Efrat <elad@NetBSD.org>
|
||||
@ -31,17 +31,20 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#if defined(__NetBSD__)
|
||||
__KERNEL_RCSID(0, "$NetBSD: verified_exec.c,v 1.50 2006/11/28 22:22:02 elad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: verified_exec.c,v 1.51 2006/11/30 01:09:47 elad Exp $");
|
||||
#else
|
||||
__RCSID("$Id: verified_exec.c,v 1.50 2006/11/28 22:22:02 elad Exp $\n$NetBSD: verified_exec.c,v 1.50 2006/11/28 22:22:02 elad Exp $");
|
||||
__RCSID("$Id: verified_exec.c,v 1.51 2006/11/30 01:09:47 elad Exp $\n$NetBSD: verified_exec.c,v 1.51 2006/11/30 01:09:47 elad Exp $");
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/buf.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/verified_exec.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/kernel.h>
|
||||
@ -53,26 +56,8 @@ __RCSID("$Id: verified_exec.c,v 1.50 2006/11/28 22:22:02 elad Exp $\n$NetBSD: ve
|
||||
#define DEVPORT_DEVICE struct device
|
||||
#endif
|
||||
|
||||
#include <sys/conf.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/sysctl.h>
|
||||
#define VERIEXEC_NEED_NODE
|
||||
#include <sys/verified_exec.h>
|
||||
#include <sys/kauth.h>
|
||||
|
||||
#include <sys/fileassoc.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <prop/proplib.h>
|
||||
|
||||
/* count of number of times device is open (we really only allow one open) */
|
||||
static unsigned int veriexec_dev_usage;
|
||||
static unsigned int veriexec_tablecount = 0;
|
||||
|
||||
struct veriexec_softc {
|
||||
DEVPORT_DEVICE veriexec_dev;
|
||||
};
|
||||
@ -108,19 +93,11 @@ const struct cdevsw veriexec_cdevsw = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Autoconfiguration glue */
|
||||
void veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
|
||||
void *aux);
|
||||
int veriexecopen(dev_t dev, int flags, int fmt, struct lwp *l);
|
||||
int veriexecclose(dev_t dev, int flags, int fmt, struct lwp *l);
|
||||
int veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
|
||||
struct lwp *l);
|
||||
|
||||
static int veriexec_newtable(prop_dictionary_t, struct lwp *);
|
||||
static int veriexec_load(prop_dictionary_t, struct lwp *);
|
||||
static int veriexec_delete(prop_dictionary_t, struct lwp *);
|
||||
static int veriexec_query(prop_dictionary_t, prop_dictionary_t, struct lwp *);
|
||||
|
||||
/* count of number of times device is open (we really only allow one open) */
|
||||
static unsigned int veriexec_dev_usage;
|
||||
|
||||
void
|
||||
veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
|
||||
void *aux)
|
||||
@ -190,7 +167,7 @@ veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
|
||||
if (error)
|
||||
break;
|
||||
|
||||
error = veriexec_newtable(dict, l);
|
||||
error = veriexec_table_add(l, dict);
|
||||
prop_object_release(dict);
|
||||
break;
|
||||
|
||||
@ -199,7 +176,7 @@ veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
|
||||
if (error)
|
||||
break;
|
||||
|
||||
error = veriexec_load(dict, l);
|
||||
error = veriexec_file_add(l, dict);
|
||||
prop_object_release(dict);
|
||||
break;
|
||||
|
||||
@ -208,7 +185,7 @@ veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
|
||||
if (error)
|
||||
break;
|
||||
|
||||
error = veriexec_delete(dict, l);
|
||||
error = veriexec_delete(l, dict);
|
||||
prop_object_release(dict);
|
||||
break;
|
||||
|
||||
@ -258,186 +235,6 @@ veriexec_drvinit(void *unused)
|
||||
SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Create a new Veriexec table.
|
||||
*/
|
||||
static int
|
||||
veriexec_newtable(prop_dictionary_t dict, struct lwp *l)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
struct nameidata nid;
|
||||
u_char buf[16];
|
||||
int error;
|
||||
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE,
|
||||
prop_string_cstring_nocopy(prop_dictionary_get(dict, "mount")), l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = fileassoc_table_add(nid.ni_vp->v_mount,
|
||||
prop_number_integer_value(prop_dictionary_get(dict, "count")));
|
||||
if (error && (error != EEXIST))
|
||||
goto out;
|
||||
|
||||
vte = malloc(sizeof(*vte), M_TEMP, M_WAITOK | M_ZERO);
|
||||
error = fileassoc_tabledata_add(nid.ni_vp->v_mount, veriexec_hook, vte);
|
||||
#ifdef DIAGNOSTIC
|
||||
if (error)
|
||||
panic("Fileassoc: Inconsistency after adding table");
|
||||
#endif /* DIAGNOSTIC */
|
||||
|
||||
snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++);
|
||||
sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
|
||||
0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
|
||||
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_mntonname,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
|
||||
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_fstypename,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
|
||||
NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
veriexec_load(prop_dictionary_t dict, struct lwp *l)
|
||||
{
|
||||
struct veriexec_file_entry *hh, *e;
|
||||
struct nameidata nid;
|
||||
const char *file, *fp_type;
|
||||
int error;
|
||||
|
||||
file = prop_string_cstring_nocopy(prop_dictionary_get(dict, "file"));
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, file, l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Add only regular files. */
|
||||
if (nid.ni_vp->v_type != VREG) {
|
||||
log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n",
|
||||
file);
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
|
||||
|
||||
fp_type = prop_string_cstring_nocopy(prop_dictionary_get(dict,
|
||||
"fp-type"));
|
||||
if ((e->ops = veriexec_find_ops(__UNCONST(fp_type))) == NULL) {
|
||||
free(e, M_TEMP);
|
||||
log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
|
||||
"`%s' for file `%s'.\n", fp_type, file);
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
e->fp = prop_data_data(prop_dictionary_get(dict, "fp"));
|
||||
if (e->fp == NULL) {
|
||||
free(e, M_TEMP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hh = veriexec_lookup(nid.ni_vp);
|
||||
if (hh != NULL) {
|
||||
boolean_t fp_mismatch;
|
||||
|
||||
if (strcmp(e->ops->type, fp_type) ||
|
||||
memcmp(hh->fp, e->fp, hh->ops->hash_len))
|
||||
fp_mismatch = TRUE;
|
||||
else
|
||||
fp_mismatch = FALSE;
|
||||
|
||||
if ((veriexec_verbose >= 1) || fp_mismatch)
|
||||
log(LOG_NOTICE, "Veriexec: Duplicate entry for `%s' "
|
||||
"ignored. (%s fingerprint)\n", file,
|
||||
fp_mismatch ? "different" : "same");
|
||||
|
||||
free(e->fp, M_TEMP);
|
||||
free(e, M_TEMP);
|
||||
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
e->type = prop_number_integer_value(prop_dictionary_get(dict,
|
||||
"entry-type"));
|
||||
|
||||
e->status = FINGERPRINT_NOTEVAL;
|
||||
|
||||
e->page_fp = NULL;
|
||||
e->page_fp_status = PAGE_FP_NONE;
|
||||
e->npages = 0;
|
||||
e->last_page_size = 0;
|
||||
|
||||
error = veriexec_hashadd(nid.ni_vp, e);
|
||||
if (error) {
|
||||
free(e->fp, M_TEMP);
|
||||
free(e, M_TEMP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
veriexec_report("New entry.", file, NULL, REPORT_DEBUG);
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
veriexec_delete(prop_dictionary_t dict, struct lwp *l)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
struct nameidata nid;
|
||||
int error;
|
||||
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE,
|
||||
prop_string_cstring_nocopy(prop_dictionary_get(dict, "file")), l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
vte = veriexec_tblfind(nid.ni_vp);
|
||||
if (vte == NULL) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* XXX this should either receive the filename to remove OR a mount point! */
|
||||
/* Delete an entire table */
|
||||
if (nid.ni_vp->v_type == VDIR) {
|
||||
sysctl_free(__UNCONST(vte->vte_node));
|
||||
|
||||
veriexec_tablecount--;
|
||||
|
||||
error = fileassoc_table_clear(nid.ni_vp->v_mount, veriexec_hook);
|
||||
if (error)
|
||||
goto out;
|
||||
} else if (nid.ni_vp->v_type == VREG) {
|
||||
error = fileassoc_clear(nid.ni_vp, veriexec_hook);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
vte->vte_count--;
|
||||
}
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
veriexec_query(prop_dictionary_t dict, prop_dictionary_t rdict, struct lwp *l)
|
||||
{
|
||||
@ -452,20 +249,12 @@ veriexec_query(prop_dictionary_t dict, prop_dictionary_t rdict, struct lwp *l)
|
||||
return (error);
|
||||
|
||||
vfe = veriexec_lookup(nid.ni_vp);
|
||||
if (vfe == NULL) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
prop_dictionary_set_uint8(rdict, "entry-type", vfe->type);
|
||||
prop_dictionary_set_uint8(rdict, "status", vfe->status);
|
||||
prop_dictionary_set(rdict, "fp-type",
|
||||
prop_string_create_cstring(vfe->ops->type));
|
||||
prop_dictionary_set(rdict, "fp",
|
||||
prop_data_create_data(vfe->fp, vfe->ops->hash_len));
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
|
||||
if (vfe != NULL)
|
||||
veriexec_convert(vfe, rdict);
|
||||
else
|
||||
error = ENOENT;
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: kern_verifiedexec.c,v 1.74 2006/11/28 22:22:02 elad Exp $ */
|
||||
/* $NetBSD: kern_verifiedexec.c,v 1.75 2006/11/30 01:09:47 elad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2005 Elad Efrat <elad@NetBSD.org>
|
||||
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.74 2006/11/28 22:22:02 elad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.75 2006/11/30 01:09:47 elad Exp $");
|
||||
|
||||
#include "opt_veriexec.h"
|
||||
|
||||
@ -44,7 +44,6 @@ __KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.74 2006/11/28 22:22:02 elad
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/inttypes.h>
|
||||
#define VERIEXEC_NEED_NODE
|
||||
#include <sys/verified_exec.h>
|
||||
#if defined(__FreeBSD__)
|
||||
# include <sys/systm.h>
|
||||
@ -63,6 +62,37 @@ __KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.74 2006/11/28 22:22:02 elad
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/conf.h>
|
||||
#include <miscfs/specfs/specdev.h>
|
||||
#include <prop/proplib.h>
|
||||
|
||||
MALLOC_DEFINE(M_VERIEXEC, "Veriexec", "Veriexec data-structures");
|
||||
|
||||
struct veriexec_fpops {
|
||||
const char *type;
|
||||
size_t hash_len;
|
||||
size_t context_size;
|
||||
veriexec_fpop_init_t init;
|
||||
veriexec_fpop_update_t update;
|
||||
veriexec_fpop_final_t final;
|
||||
LIST_ENTRY(veriexec_fpops) entries;
|
||||
};
|
||||
|
||||
/* Veriexec per-file entry data. */
|
||||
struct veriexec_file_entry {
|
||||
u_char type; /* Entry type. */
|
||||
u_char status; /* Evaluation status. */
|
||||
u_char page_fp_status; /* Per-page FP status. */
|
||||
u_char *fp; /* Fingerprint. */
|
||||
void *page_fp; /* Per-page fingerprints */
|
||||
size_t npages; /* Number of pages. */
|
||||
size_t last_page_size; /* To support < PAGE_SIZE */
|
||||
struct veriexec_fpops *ops; /* Fingerprint ops vector*/
|
||||
};
|
||||
|
||||
/* Veriexec per-table data. */
|
||||
struct veriexec_table_entry {
|
||||
uint64_t vte_count; /* Number of Veriexec entries. */
|
||||
const struct sysctlnode *vte_node;
|
||||
};
|
||||
|
||||
int veriexec_verbose;
|
||||
int veriexec_strict;
|
||||
@ -74,13 +104,14 @@ const struct sysctlnode *veriexec_count_node;
|
||||
|
||||
int veriexec_hook;
|
||||
|
||||
/* Veriexecs table of hash types and their associated information. */
|
||||
LIST_HEAD(veriexec_ops_head, veriexec_fp_ops) veriexec_ops_list;
|
||||
LIST_HEAD(, veriexec_fpops) veriexec_fpops_list;
|
||||
|
||||
static int veriexec_raw_cb(kauth_cred_t, kauth_action_t, void *,
|
||||
void *, void *, void *, void *);
|
||||
static int sysctl_kern_veriexec(SYSCTLFN_PROTO);
|
||||
|
||||
static unsigned int veriexec_tablecount = 0;
|
||||
|
||||
/*
|
||||
* Sysctl helper routine for Veriexec.
|
||||
*/
|
||||
@ -165,16 +196,35 @@ SYSCTL_SETUP(sysctl_security_pax_setup, "sysctl security.pax setup")
|
||||
}
|
||||
|
||||
/*
|
||||
* Add fingerprint names to the global list.
|
||||
* Add ops to the fignerprint ops vector list.
|
||||
*/
|
||||
static void
|
||||
veriexec_add_fp_name(const char *name)
|
||||
int
|
||||
veriexec_fpops_add(const char *fp_type, size_t hash_len, size_t ctx_size,
|
||||
veriexec_fpop_init_t init, veriexec_fpop_update_t update,
|
||||
veriexec_fpop_final_t final)
|
||||
{
|
||||
struct veriexec_fpops *ops;
|
||||
char *newp;
|
||||
unsigned int new_max;
|
||||
|
||||
if (name == NULL)
|
||||
return;
|
||||
/* Sanity check all parameters. */
|
||||
if ((fp_type == NULL) || (hash_len == 0) || (ctx_size == 0) ||
|
||||
(init == NULL) || (update == NULL) || (final == NULL))
|
||||
return (EFAULT);
|
||||
|
||||
if (veriexec_fpops_lookup(fp_type) != NULL)
|
||||
return (EEXIST);
|
||||
|
||||
ops = malloc(sizeof(*ops), M_VERIEXEC, M_WAITOK);
|
||||
|
||||
ops->type = fp_type;
|
||||
ops->hash_len = hash_len;
|
||||
ops->context_size = ctx_size;
|
||||
ops->init = init;
|
||||
ops->update = update;
|
||||
ops->final = final;
|
||||
|
||||
LIST_INSERT_HEAD(&veriexec_fpops_list, ops, entries);
|
||||
|
||||
/*
|
||||
* If we don't have space for any names, allocate enough for six
|
||||
@ -183,7 +233,7 @@ veriexec_add_fp_name(const char *name)
|
||||
*/
|
||||
if (veriexec_fp_names == NULL) {
|
||||
veriexec_name_max = 64;
|
||||
veriexec_fp_names = malloc(veriexec_name_max, M_TEMP,
|
||||
veriexec_fp_names = malloc(veriexec_name_max, M_VERIEXEC,
|
||||
M_WAITOK|M_ZERO);
|
||||
}
|
||||
|
||||
@ -192,10 +242,10 @@ veriexec_add_fp_name(const char *name)
|
||||
* extend the buffer with space for four names.
|
||||
*/
|
||||
while (veriexec_name_max - (strlen(veriexec_fp_names) + 1) <
|
||||
strlen(name)) {
|
||||
strlen(fp_type)) {
|
||||
/* Add space for four algorithm names. */
|
||||
new_max = veriexec_name_max + 64;
|
||||
newp = realloc(veriexec_fp_names, new_max, M_TEMP,
|
||||
newp = realloc(veriexec_fp_names, new_max, M_VERIEXEC,
|
||||
M_WAITOK|M_ZERO);
|
||||
veriexec_fp_names = newp;
|
||||
veriexec_name_max = new_max;
|
||||
@ -204,27 +254,7 @@ veriexec_add_fp_name(const char *name)
|
||||
if (*veriexec_fp_names != '\0')
|
||||
strlcat(veriexec_fp_names, " ", veriexec_name_max);
|
||||
|
||||
strlcat(veriexec_fp_names, name, veriexec_name_max);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add ops to the fignerprint ops vector list.
|
||||
*/
|
||||
int veriexec_add_fp_ops(struct veriexec_fp_ops *ops)
|
||||
{
|
||||
if (ops == NULL)
|
||||
return (EFAULT);
|
||||
|
||||
if ((ops->init == NULL) ||
|
||||
(ops->update == NULL) ||
|
||||
(ops->final == NULL))
|
||||
return (EFAULT);
|
||||
|
||||
if (veriexec_find_ops(ops->type) != NULL)
|
||||
return (EEXIST);
|
||||
|
||||
LIST_INSERT_HEAD(&veriexec_ops_list, ops, entries);
|
||||
veriexec_add_fp_name(ops->type);
|
||||
strlcat(veriexec_fp_names, fp_type, veriexec_name_max);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -235,8 +265,6 @@ int veriexec_add_fp_ops(struct veriexec_fp_ops *ops)
|
||||
void
|
||||
veriexec_init(void)
|
||||
{
|
||||
struct veriexec_fp_ops *ops;
|
||||
|
||||
/* Register a fileassoc for Veriexec. */
|
||||
veriexec_hook = fileassoc_register("veriexec", veriexec_clear);
|
||||
if (veriexec_hook == FILEASSOC_INVAL)
|
||||
@ -247,68 +275,57 @@ veriexec_init(void)
|
||||
NULL)
|
||||
panic("Veriexec: Can't listen on device scope");
|
||||
|
||||
LIST_INIT(&veriexec_ops_list);
|
||||
LIST_INIT(&veriexec_fpops_list);
|
||||
veriexec_fp_names = NULL;
|
||||
veriexec_name_max = 0;
|
||||
|
||||
#define FPOPS_ADD(a, b, c, d, e, f) \
|
||||
veriexec_fpops_add(a, b, c, (veriexec_fpop_init_t)d, \
|
||||
(veriexec_fpop_update_t)e, (veriexec_fpop_final_t)f)
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_RMD160
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "RMD160", RMD160_DIGEST_LENGTH,
|
||||
sizeof(RMD160_CTX), RMD160Init, RMD160Update,
|
||||
RMD160Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD("RMD160", RMD160_DIGEST_LENGTH, sizeof(RMD160_CTX),
|
||||
RMD160Init, RMD160Update, RMD160Final);
|
||||
#endif /* VERIFIED_EXEC_FP_RMD160 */
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_SHA256
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "SHA256", SHA256_DIGEST_LENGTH,
|
||||
sizeof(SHA256_CTX), SHA256_Init, SHA256_Update,
|
||||
SHA256_Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD("SHA256", SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX),
|
||||
SHA256_Init, SHA256_Update, SHA256_Final);
|
||||
#endif /* VERIFIED_EXEC_FP_SHA256 */
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_SHA384
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "SHA384", SHA384_DIGEST_LENGTH,
|
||||
sizeof(SHA384_CTX), SHA384_Init, SHA384_Update,
|
||||
SHA384_Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD( "SHA384", SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX),
|
||||
SHA384_Init, SHA384_Update, SHA384_Final);
|
||||
#endif /* VERIFIED_EXEC_FP_SHA384 */
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_SHA512
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "SHA512", SHA512_DIGEST_LENGTH,
|
||||
sizeof(SHA512_CTX), SHA512_Init, SHA512_Update,
|
||||
SHA512_Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD("SHA512", SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX),
|
||||
SHA512_Init, SHA512_Update, SHA512_Final);
|
||||
#endif /* VERIFIED_EXEC_FP_SHA512 */
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_SHA1
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "SHA1", SHA1_DIGEST_LENGTH,
|
||||
sizeof(SHA1_CTX), SHA1Init, SHA1Update,
|
||||
SHA1Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD("SHA1", SHA1_DIGEST_LENGTH, sizeof(SHA1_CTX),
|
||||
SHA1Init, SHA1Update, SHA1Final);
|
||||
#endif /* VERIFIED_EXEC_FP_SHA1 */
|
||||
|
||||
#ifdef VERIFIED_EXEC_FP_MD5
|
||||
ops = (struct veriexec_fp_ops *) malloc(sizeof(*ops), M_TEMP, M_WAITOK);
|
||||
VERIEXEC_OPINIT(ops, "MD5", MD5_DIGEST_LENGTH, sizeof(MD5_CTX),
|
||||
MD5Init, MD5Update, MD5Final);
|
||||
(void) veriexec_add_fp_ops(ops);
|
||||
FPOPS_ADD("MD5", MD5_DIGEST_LENGTH, sizeof(MD5_CTX),
|
||||
MD5Init, MD5Update, MD5Final);
|
||||
#endif /* VERIFIED_EXEC_FP_MD5 */
|
||||
|
||||
#undef FPOPS_ADD
|
||||
}
|
||||
|
||||
struct veriexec_fp_ops *
|
||||
veriexec_find_ops(const char *name)
|
||||
struct veriexec_fpops *
|
||||
veriexec_fpops_lookup(const char *name)
|
||||
{
|
||||
struct veriexec_fp_ops *ops;
|
||||
struct veriexec_fpops *ops;
|
||||
|
||||
if ((name == NULL) || (strlen(name) == 0))
|
||||
if (name == NULL)
|
||||
return (NULL);
|
||||
|
||||
LIST_FOREACH(ops, &veriexec_ops_list, entries) {
|
||||
if (strncasecmp(name, ops->type, sizeof(ops->type) - 1) == 0)
|
||||
LIST_FOREACH(ops, &veriexec_fpops_list, entries) {
|
||||
if (strcasecmp(name, ops->type) == 0)
|
||||
return (ops);
|
||||
}
|
||||
|
||||
@ -319,7 +336,7 @@ veriexec_find_ops(const char *name)
|
||||
* Calculate fingerprint. Information on hash length and routines used is
|
||||
* extracted from veriexec_hash_list according to the hash type.
|
||||
*/
|
||||
int
|
||||
static int
|
||||
veriexec_fp_calc(struct lwp *l, struct vnode *vp,
|
||||
struct veriexec_file_entry *vfe, u_char *fp)
|
||||
{
|
||||
@ -342,8 +359,8 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
|
||||
#endif
|
||||
do_perpage = 0;
|
||||
|
||||
ctx = (void *) malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
|
||||
buf = (u_char *) malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
|
||||
ctx = (void *) malloc(vfe->ops->context_size, M_VERIEXEC, M_WAITOK);
|
||||
buf = (u_char *) malloc(PAGE_SIZE, M_VERIEXEC, M_WAITOK);
|
||||
|
||||
page_ctx = NULL;
|
||||
page_fp = NULL;
|
||||
@ -351,10 +368,10 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
|
||||
if (do_perpage) {
|
||||
npages = (va.va_size >> PAGE_SHIFT) + 1;
|
||||
page_fp = (u_char *) malloc(vfe->ops->hash_len * npages,
|
||||
M_TEMP, M_WAITOK|M_ZERO);
|
||||
M_VERIEXEC, M_WAITOK|M_ZERO);
|
||||
vfe->page_fp = page_fp;
|
||||
page_ctx = (void *) malloc(vfe->ops->context_size, M_TEMP,
|
||||
M_WAITOK);
|
||||
page_ctx = (void *) malloc(vfe->ops->context_size, M_VERIEXEC,
|
||||
M_WAITOK);
|
||||
}
|
||||
|
||||
(vfe->ops->init)(ctx);
|
||||
@ -377,7 +394,7 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
|
||||
|
||||
if (error) {
|
||||
if (do_perpage) {
|
||||
free(vfe->page_fp, M_TEMP);
|
||||
free(vfe->page_fp, M_VERIEXEC);
|
||||
vfe->page_fp = NULL;
|
||||
}
|
||||
|
||||
@ -418,16 +435,16 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
|
||||
|
||||
bad:
|
||||
if (do_perpage)
|
||||
free(page_ctx, M_TEMP);
|
||||
free(ctx, M_TEMP);
|
||||
free(buf, M_TEMP);
|
||||
free(page_ctx, M_VERIEXEC);
|
||||
free(ctx, M_VERIEXEC);
|
||||
free(buf, M_VERIEXEC);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Compare two fingerprints of the same type. */
|
||||
int
|
||||
veriexec_fp_cmp(struct veriexec_fp_ops *ops, u_char *fp1, u_char *fp2)
|
||||
static int
|
||||
veriexec_fp_cmp(struct veriexec_fpops *ops, u_char *fp1, u_char *fp2)
|
||||
{
|
||||
if (veriexec_verbose >= 2) {
|
||||
int i;
|
||||
@ -447,8 +464,8 @@ veriexec_fp_cmp(struct veriexec_fp_ops *ops, u_char *fp1, u_char *fp2)
|
||||
return (memcmp(fp1, fp2, ops->hash_len));
|
||||
}
|
||||
|
||||
struct veriexec_table_entry *
|
||||
veriexec_tblfind(struct vnode *vp)
|
||||
static struct veriexec_table_entry *
|
||||
veriexec_table_lookup(struct vnode *vp)
|
||||
{
|
||||
return (fileassoc_tabledata_lookup(vp->v_mount, veriexec_hook));
|
||||
}
|
||||
@ -459,28 +476,6 @@ veriexec_lookup(struct vnode *vp)
|
||||
return (fileassoc_lookup(vp, veriexec_hook));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to a hash table. If a collision is found, handle it.
|
||||
* The passed entry is allocated in kernel memory.
|
||||
*/
|
||||
int
|
||||
veriexec_hashadd(struct vnode *vp, struct veriexec_file_entry *vfe)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
int error;
|
||||
|
||||
error = fileassoc_add(vp, veriexec_hook, vfe);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
vte = veriexec_tblfind(vp);
|
||||
KASSERT(vte != NULL);
|
||||
|
||||
vte->vte_count++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the fingerprint of the given file. If we're called directly from
|
||||
* sys_execve(), 'flag' will be VERIEXEC_DIRECT. If we're called from
|
||||
@ -489,7 +484,7 @@ veriexec_hashadd(struct vnode *vp, struct veriexec_file_entry *vfe)
|
||||
*/
|
||||
int
|
||||
veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
|
||||
struct veriexec_file_entry **ret)
|
||||
boolean_t *found)
|
||||
{
|
||||
struct veriexec_file_entry *vfe;
|
||||
u_char *digest;
|
||||
@ -500,8 +495,12 @@ veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
|
||||
|
||||
/* Lookup veriexec table entry, save pointer if requested. */
|
||||
vfe = veriexec_lookup(vp);
|
||||
if (ret != NULL)
|
||||
*ret = vfe;
|
||||
if (found != NULL) {
|
||||
if (vfe != NULL)
|
||||
*found = TRUE;
|
||||
else
|
||||
*found = FALSE;
|
||||
}
|
||||
if (vfe == NULL)
|
||||
goto out;
|
||||
|
||||
@ -511,13 +510,13 @@ veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
|
||||
if ((vfe->status == FINGERPRINT_NOTEVAL) ||
|
||||
(vfe->type & VERIEXEC_UNTRUSTED)) {
|
||||
/* Calculate fingerprint for on-disk file. */
|
||||
digest = (u_char *) malloc(vfe->ops->hash_len, M_TEMP,
|
||||
M_WAITOK);
|
||||
digest = (u_char *) malloc(vfe->ops->hash_len, M_VERIEXEC,
|
||||
M_WAITOK);
|
||||
error = veriexec_fp_calc(l, vp, vfe, digest);
|
||||
if (error) {
|
||||
veriexec_report("Fingerprint calculation error.",
|
||||
name, NULL, REPORT_ALWAYS);
|
||||
free(digest, M_TEMP);
|
||||
free(digest, M_VERIEXEC);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -528,7 +527,7 @@ veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
|
||||
vfe->status = FINGERPRINT_NOMATCH;
|
||||
}
|
||||
|
||||
free(digest, M_TEMP);
|
||||
free(digest, M_VERIEXEC);
|
||||
}
|
||||
|
||||
if (!(vfe->type & flag)) {
|
||||
@ -614,8 +613,8 @@ veriexec_page_verify(struct veriexec_file_entry *vfe, struct vm_page *pg,
|
||||
if (idx >= vfe->npages)
|
||||
return (0);
|
||||
|
||||
ctx = malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
|
||||
fp = malloc(vfe->ops->hash_len, M_TEMP, M_WAITOK);
|
||||
ctx = malloc(vfe->ops->context_size, M_VERIEXEC, M_WAITOK);
|
||||
fp = malloc(vfe->ops->hash_len, M_VERIEXEC, M_WAITOK);
|
||||
kva = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
|
||||
pmap_kenter_pa(kva, VM_PAGE_TO_PHYS(pg), VM_PROT_READ);
|
||||
|
||||
@ -655,8 +654,8 @@ veriexec_page_verify(struct veriexec_file_entry *vfe, struct vm_page *pg,
|
||||
}
|
||||
}
|
||||
|
||||
free(ctx, M_TEMP);
|
||||
free(fp, M_TEMP);
|
||||
free(ctx, M_VERIEXEC);
|
||||
free(fp, M_VERIEXEC);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -687,7 +686,7 @@ veriexec_removechk(struct vnode *vp, const char *pathbuf, struct lwp *l)
|
||||
|
||||
fileassoc_clear(vp, veriexec_hook);
|
||||
|
||||
vte = veriexec_tblfind(vp);
|
||||
vte = veriexec_table_lookup(vp);
|
||||
KASSERT(vte != NULL);
|
||||
|
||||
vte->vte_count--;
|
||||
@ -767,16 +766,16 @@ veriexec_clear(void *data, int file_specific)
|
||||
|
||||
if (vfe != NULL) {
|
||||
if (vfe->fp != NULL)
|
||||
free(vfe->fp, M_TEMP);
|
||||
free(vfe->fp, M_VERIEXEC);
|
||||
if (vfe->page_fp != NULL)
|
||||
free(vfe->page_fp, M_TEMP);
|
||||
free(vfe, M_TEMP);
|
||||
free(vfe->page_fp, M_VERIEXEC);
|
||||
free(vfe, M_VERIEXEC);
|
||||
}
|
||||
} else {
|
||||
struct veriexec_table_entry *vte = data;
|
||||
|
||||
if (vte != NULL)
|
||||
free(vte, M_TEMP);
|
||||
free(vte, M_VERIEXEC);
|
||||
}
|
||||
}
|
||||
|
||||
@ -785,8 +784,15 @@ veriexec_clear(void *data, int file_specific)
|
||||
* XXX: This should be updated when per-page fingerprints are added.
|
||||
*/
|
||||
void
|
||||
veriexec_purge(struct veriexec_file_entry *vfe)
|
||||
veriexec_purge(struct vnode *vp)
|
||||
{
|
||||
struct veriexec_file_entry *vfe;
|
||||
|
||||
vfe = veriexec_lookup(vp);
|
||||
|
||||
if (vfe == NULL)
|
||||
return;
|
||||
|
||||
vfe->status = FINGERPRINT_NOTEVAL;
|
||||
}
|
||||
|
||||
@ -895,7 +901,7 @@ veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
|
||||
/*
|
||||
* XXX: See vfs_mountedon() comment in secmodel/bsd44.
|
||||
*/
|
||||
vte = veriexec_tblfind(bvp);
|
||||
vte = veriexec_table_lookup(bvp);
|
||||
if (vte == NULL) {
|
||||
result = KAUTH_RESULT_ALLOW;
|
||||
break;
|
||||
@ -939,3 +945,260 @@ veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a file to be monitored by Veriexec.
|
||||
*
|
||||
* Expected elements in dict: file, fp, fp-type, entry-type.
|
||||
*/
|
||||
int
|
||||
veriexec_file_add(struct lwp *l, prop_dictionary_t dict)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
struct veriexec_file_entry *vfe, *hh;
|
||||
struct nameidata nid;
|
||||
const char *file, *fp_type;
|
||||
int error;
|
||||
|
||||
file = prop_string_cstring_nocopy(prop_dictionary_get(dict, "file"));
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, file, l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Add only regular files. */
|
||||
if (nid.ni_vp->v_type != VREG) {
|
||||
log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n",
|
||||
file);
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vfe = malloc(sizeof(*vfe), M_VERIEXEC, M_WAITOK);
|
||||
|
||||
/* Lookup fingerprint hashing algorithm. */
|
||||
fp_type = prop_string_cstring_nocopy(prop_dictionary_get(dict,
|
||||
"fp-type"));
|
||||
if ((vfe->ops = veriexec_fpops_lookup(fp_type)) == NULL) {
|
||||
free(vfe, M_VERIEXEC);
|
||||
log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
|
||||
"`%s' for file `%s'.\n", fp_type, file);
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vfe->fp = prop_data_data(prop_dictionary_get(dict, "fp"));
|
||||
if (vfe->fp == NULL) {
|
||||
free(vfe, M_VERIEXEC);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we already have an entry for this file. If we do, then
|
||||
* let the user know and silently pretend to succeed.
|
||||
*/
|
||||
hh = veriexec_lookup(nid.ni_vp);
|
||||
if (hh != NULL) {
|
||||
boolean_t fp_mismatch;
|
||||
|
||||
if (strcmp(vfe->ops->type, fp_type) ||
|
||||
memcmp(hh->fp, vfe->fp, hh->ops->hash_len))
|
||||
fp_mismatch = TRUE;
|
||||
else
|
||||
fp_mismatch = FALSE;
|
||||
|
||||
if ((veriexec_verbose >= 1) || fp_mismatch)
|
||||
log(LOG_NOTICE, "Veriexec: Duplicate entry for `%s' "
|
||||
"ignored. (%s fingerprint)\n", file,
|
||||
fp_mismatch ? "different" : "same");
|
||||
|
||||
free(vfe->fp, M_VERIEXEC);
|
||||
free(vfe, M_VERIEXEC);
|
||||
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Continue entry initialization. */
|
||||
vfe->type = prop_number_integer_value(prop_dictionary_get(dict,
|
||||
"entry-type"));
|
||||
vfe->status = FINGERPRINT_NOTEVAL;
|
||||
|
||||
vfe->page_fp = NULL;
|
||||
vfe->page_fp_status = PAGE_FP_NONE;
|
||||
vfe->npages = 0;
|
||||
vfe->last_page_size = 0;
|
||||
|
||||
error = fileassoc_add(nid.ni_vp, veriexec_hook, vfe);
|
||||
if (error) {
|
||||
free(vfe->fp, M_VERIEXEC);
|
||||
free(vfe, M_VERIEXEC);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vte = veriexec_table_lookup(nid.ni_vp);
|
||||
vte->vte_count++;
|
||||
|
||||
veriexec_report("New entry.", file, NULL, REPORT_DEBUG);
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Veriexec table using hints from userland.
|
||||
*
|
||||
* Expects dict to have mount and count.
|
||||
*/
|
||||
int
|
||||
veriexec_table_add(struct lwp *l, prop_dictionary_t dict)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
struct nameidata nid;
|
||||
u_char buf[16];
|
||||
int error;
|
||||
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE,
|
||||
prop_string_cstring_nocopy(prop_dictionary_get(dict, "mount")), l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
error = fileassoc_table_add(nid.ni_vp->v_mount,
|
||||
prop_number_integer_value(prop_dictionary_get(dict, "count")));
|
||||
if (error && (error != EEXIST))
|
||||
goto out;
|
||||
|
||||
vte = malloc(sizeof(*vte), M_VERIEXEC, M_WAITOK | M_ZERO);
|
||||
error = fileassoc_tabledata_add(nid.ni_vp->v_mount, veriexec_hook, vte);
|
||||
#ifdef DIAGNOSTIC
|
||||
if (error)
|
||||
panic("Fileassoc: Inconsistency after adding table");
|
||||
#endif /* DIAGNOSTIC */
|
||||
|
||||
snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++);
|
||||
sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
|
||||
0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
|
||||
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_mntonname,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
|
||||
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_fstypename,
|
||||
0, CTL_CREATE, CTL_EOL);
|
||||
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
|
||||
CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
|
||||
NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove either a single file from being monitored by Veriexec or an entire
|
||||
* mount, depending on whether the vnode is for a VREG or a VDIR.
|
||||
*
|
||||
* Expects dict to have file.
|
||||
*/
|
||||
int
|
||||
veriexec_delete(struct lwp *l, prop_dictionary_t dict)
|
||||
{
|
||||
struct veriexec_table_entry *vte;
|
||||
struct nameidata nid;
|
||||
int error;
|
||||
|
||||
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE,
|
||||
prop_string_cstring_nocopy(prop_dictionary_get(dict, "file")), l);
|
||||
error = namei(&nid);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
vte = veriexec_table_lookup(nid.ni_vp);
|
||||
if (vte == NULL) {
|
||||
error = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* XXX this should either receive the filename to remove OR a mount point! */
|
||||
/* Delete an entire table */
|
||||
if (nid.ni_vp->v_type == VDIR) {
|
||||
sysctl_free(__UNCONST(vte->vte_node));
|
||||
|
||||
veriexec_tablecount--;
|
||||
|
||||
error = fileassoc_table_clear(nid.ni_vp->v_mount, veriexec_hook);
|
||||
if (error)
|
||||
goto out;
|
||||
} else if (nid.ni_vp->v_type == VREG) {
|
||||
error = fileassoc_clear(nid.ni_vp, veriexec_hook);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
vte->vte_count--;
|
||||
}
|
||||
|
||||
out:
|
||||
vrele(nid.ni_vp);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert Veriexec entry data to a dictionary readable by userland tools.
|
||||
*/
|
||||
void
|
||||
veriexec_convert(struct veriexec_file_entry *vfe, prop_dictionary_t rdict)
|
||||
{
|
||||
prop_dictionary_set_uint8(rdict, "entry-type", vfe->type);
|
||||
prop_dictionary_set_uint8(rdict, "status", vfe->status);
|
||||
prop_dictionary_set(rdict, "fp-type",
|
||||
prop_string_create_cstring(vfe->ops->type));
|
||||
prop_dictionary_set(rdict, "fp",
|
||||
prop_data_create_data(vfe->fp, vfe->ops->hash_len));
|
||||
}
|
||||
|
||||
int
|
||||
veriexec_unmountchk(struct mount *mp)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (doing_shutdown)
|
||||
return (0);
|
||||
|
||||
switch (veriexec_strict) {
|
||||
case VERIEXEC_LEARNING:
|
||||
case VERIEXEC_IDS:
|
||||
error = 0;
|
||||
break;
|
||||
|
||||
case VERIEXEC_IPS: {
|
||||
struct veriexec_table_entry *vte;
|
||||
|
||||
vte = fileassoc_tabledata_lookup(mp, veriexec_hook);
|
||||
if ((vte != NULL) && (vte->vte_count > 0)) {
|
||||
log(LOG_ALERT, "Veriexec: IPS mode, preventing"
|
||||
" unmount of \"%s\" with monitored files.",
|
||||
mp->mnt_stat.f_mntonname);
|
||||
|
||||
error = EPERM;
|
||||
} else
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case VERIEXEC_LOCKDOWN:
|
||||
default:
|
||||
log(LOG_ALERT, "Veriexec: Lockdown mode, preventing unmount "
|
||||
"of \"%s\".\n", mp->mnt_stat.f_mntonname);
|
||||
error = EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: vfs_syscalls.c,v 1.278 2006/11/21 23:52:41 elad Exp $ */
|
||||
/* $NetBSD: vfs_syscalls.c,v 1.279 2006/11/30 01:09:48 elad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
@ -37,7 +37,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.278 2006/11/21 23:52:41 elad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.279 2006/11/30 01:09:48 elad Exp $");
|
||||
|
||||
#include "opt_compat_netbsd.h"
|
||||
#include "opt_compat_43.h"
|
||||
@ -551,29 +551,9 @@ dounmount(struct mount *mp, int flags, struct lwp *l)
|
||||
int used_syncer;
|
||||
|
||||
#if NVERIEXEC > 0
|
||||
if (!doing_shutdown) {
|
||||
if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
|
||||
log(LOG_ALERT, "Veriexec: Lockdown mode, "
|
||||
"preventing unmount of \"%s\". (uid=%u)\n",
|
||||
mp->mnt_stat.f_mntonname,
|
||||
kauth_cred_getuid(l->l_cred));
|
||||
return (EPERM);
|
||||
}
|
||||
|
||||
if (veriexec_strict == VERIEXEC_IPS) {
|
||||
struct veriexec_table_entry *vte;
|
||||
|
||||
/* Check if we have fingerprints on mount. */
|
||||
vte = fileassoc_tabledata_lookup(mp, veriexec_hook);
|
||||
if ((vte != NULL) && (vte->vte_count > 0)) {
|
||||
log(LOG_ALERT, "Veriexec: IPS mode, preventing"
|
||||
" unmount of \"%s\" with monitored files. "
|
||||
"(uid=%u)\n", mp->mnt_stat.f_mntonname,
|
||||
kauth_cred_getuid(l->l_cred));
|
||||
return (EPERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
error = veriexec_unmountchk(mp);
|
||||
if (error)
|
||||
return (error);
|
||||
#endif /* NVERIEXEC > 0 */
|
||||
|
||||
#ifdef FILEASSOC
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: vfs_vnops.c,v 1.128 2006/11/01 22:45:14 elad Exp $ */
|
||||
/* $NetBSD: vfs_vnops.c,v 1.129 2006/11/30 01:09:47 elad Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
@ -37,7 +37,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.128 2006/11/01 22:45:14 elad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.129 2006/11/30 01:09:47 elad Exp $");
|
||||
|
||||
#include "fs_union.h"
|
||||
#include "veriexec.h"
|
||||
@ -105,9 +105,9 @@ vn_open(struct nameidata *ndp, int fmode, int cmode)
|
||||
struct vattr va;
|
||||
int error;
|
||||
#if NVERIEXEC > 0
|
||||
struct veriexec_file_entry *vfe = NULL;
|
||||
const char *pathbuf;
|
||||
char *tmppathbuf;
|
||||
boolean_t veriexec_monitored = FALSE;
|
||||
#endif /* NVERIEXEC > 0 */
|
||||
|
||||
#if NVERIEXEC > 0
|
||||
@ -208,7 +208,7 @@ restart:
|
||||
if ((fmode & O_CREAT) == 0) {
|
||||
#if NVERIEXEC > 0
|
||||
if ((error = veriexec_verify(l, vp, pathbuf, VERIEXEC_FILE,
|
||||
&vfe)) != 0)
|
||||
&veriexec_monitored)) != 0)
|
||||
goto bad;
|
||||
#endif /* NVERIEXEC > 0 */
|
||||
|
||||
@ -226,7 +226,7 @@ restart:
|
||||
(error = VOP_ACCESS(vp, VWRITE, cred, l)) != 0)
|
||||
goto bad;
|
||||
#if NVERIEXEC > 0
|
||||
if (vfe != NULL) {
|
||||
if (veriexec_monitored) {
|
||||
veriexec_report("Write access request.",
|
||||
pathbuf, l, REPORT_ALWAYS|REPORT_ALARM);
|
||||
|
||||
@ -235,7 +235,7 @@ restart:
|
||||
error = EPERM;
|
||||
goto bad;
|
||||
} else {
|
||||
veriexec_purge(vfe);
|
||||
veriexec_purge(vp);
|
||||
}
|
||||
}
|
||||
#endif /* NVERIEXEC > 0 */
|
||||
@ -245,22 +245,21 @@ restart:
|
||||
if (fmode & O_TRUNC) {
|
||||
#if NVERIEXEC > 0
|
||||
if ((error = veriexec_verify(l, vp, pathbuf, VERIEXEC_FILE,
|
||||
&vfe)) != 0) {
|
||||
&veriexec_monitored)) != 0) {
|
||||
/*VOP_UNLOCK(vp, 0);*/
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (vfe != NULL) {
|
||||
veriexec_report("truncate access request.",
|
||||
pathbuf, l,
|
||||
REPORT_VERBOSE | REPORT_ALARM);
|
||||
if (veriexec_monitored) {
|
||||
veriexec_report("Truncate access request.", pathbuf, l,
|
||||
REPORT_VERBOSE | REPORT_ALARM);
|
||||
|
||||
/* IPS mode: Deny truncating monitored files. */
|
||||
if (veriexec_strict >= 2) {
|
||||
error = EPERM;
|
||||
goto bad;
|
||||
} else {
|
||||
veriexec_purge(vfe);
|
||||
veriexec_purge(vp);
|
||||
}
|
||||
}
|
||||
#endif /* NVERIEXEC > 0 */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: verified_exec.h,v 1.43 2006/11/29 14:52:11 elad Exp $ */
|
||||
/* $NetBSD: verified_exec.h,v 1.44 2006/11/30 01:09:47 elad Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2005 Elad Efrat <elad@NetBSD.org>
|
||||
@ -34,12 +34,15 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/hash.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/malloc.h>
|
||||
#include <uvm/uvm_extern.h>
|
||||
#include <uvm/uvm_pglist.h>
|
||||
#include <uvm/uvm_page.h>
|
||||
|
||||
#include <prop/proplib.h>
|
||||
#endif /* _KERNEL */
|
||||
|
||||
/* Flags for a Veriexec entry. These can be OR'd together. */
|
||||
#define VERIEXEC_DIRECT 0x01 /* Direct execution (exec) */
|
||||
@ -53,11 +56,11 @@
|
||||
#define VERIEXEC_DELETE _IOW('X', 0x3, struct plistref)
|
||||
#define VERIEXEC_QUERY _IOWR('X', 0x4, struct plistref)
|
||||
|
||||
/* Verified exec sysctl objects. */
|
||||
#define VERIEXEC_VERBOSE 1 /* Verbosity level. */
|
||||
#define VERIEXEC_STRICT 2 /* Strict mode level. */
|
||||
#define VERIEXEC_ALGORITHMS 3 /* Supported hashing algorithms. */
|
||||
#define VERIEXEC_COUNT 4 /* # of fingerprinted files on device. */
|
||||
/* Veriexec modes (strict levels). */
|
||||
#define VERIEXEC_LEARNING 0 /* Learning mode. */
|
||||
#define VERIEXEC_IDS 1 /* Intrusion detection mode. */
|
||||
#define VERIEXEC_IPS 2 /* Intrusion prevention mode. */
|
||||
#define VERIEXEC_LOCKDOWN 3 /* Lockdown mode. */
|
||||
|
||||
/* Valid status field values. */
|
||||
#define FINGERPRINT_NOTEVAL 0 /* fingerprint has not been evaluated */
|
||||
@ -69,64 +72,23 @@
|
||||
#define PAGE_FP_READY 1 /* per-page fingerprints ready for use. */
|
||||
#define PAGE_FP_FAIL 2 /* mismatch in per-page fingerprints. */
|
||||
|
||||
#ifdef _KERNEL
|
||||
void veriexecattach(struct device *, struct device *, void *);
|
||||
int veriexecopen(dev_t, int, int, struct lwp *);
|
||||
int veriexecclose(dev_t, int, int, struct lwp *);
|
||||
int veriexecioctl(dev_t, u_long, caddr_t, int, struct lwp *);
|
||||
|
||||
/* defined in kern_verifiedexec.c */
|
||||
extern char *veriexec_fp_names;
|
||||
extern int veriexec_verbose;
|
||||
extern int veriexec_strict;
|
||||
/* this one requires sysctl.h to be included before verified_exec.h */
|
||||
#ifdef VERIEXEC_NEED_NODE
|
||||
extern const struct sysctlnode *veriexec_count_node;
|
||||
#endif /* VERIEXEC_NEED_NODE */
|
||||
extern int veriexec_hook;
|
||||
|
||||
/*
|
||||
* Operations vector for verified exec, this defines the characteristics
|
||||
* for the fingerprint type.
|
||||
* Function types: init, update, final.
|
||||
*/
|
||||
typedef void (*VERIEXEC_INIT_FN)(void *);
|
||||
typedef void (*VERIEXEC_UPDATE_FN)(void *, u_char *, u_int);
|
||||
typedef void (*VERIEXEC_FINAL_FN)(u_char *, void *);
|
||||
typedef void (*veriexec_fpop_init_t)(void *);
|
||||
typedef void (*veriexec_fpop_update_t)(void *, u_char *, u_int);
|
||||
typedef void (*veriexec_fpop_final_t)(u_char *, void *);
|
||||
|
||||
struct veriexec_fp_ops {
|
||||
const char *type;
|
||||
size_t hash_len;
|
||||
size_t context_size;
|
||||
VERIEXEC_INIT_FN init;
|
||||
VERIEXEC_UPDATE_FN update;
|
||||
VERIEXEC_FINAL_FN final;
|
||||
LIST_ENTRY(veriexec_fp_ops) entries;
|
||||
};
|
||||
#ifdef _KERNEL
|
||||
MALLOC_DECLARE(M_VERIEXEC);
|
||||
|
||||
/* Veriexec per-file entry data. */
|
||||
struct veriexec_file_entry {
|
||||
u_char type; /* Entry type. */
|
||||
u_char status; /* Evaluation status. */
|
||||
u_char page_fp_status; /* Per-page FP status. */
|
||||
u_char *fp; /* Fingerprint. */
|
||||
void *page_fp; /* Per-page fingerprints */
|
||||
size_t npages; /* Number of pages. */
|
||||
size_t last_page_size; /* To support < PAGE_SIZE */
|
||||
struct veriexec_fp_ops *ops; /* Fingerprint ops vector*/
|
||||
};
|
||||
struct veriexec_file_entry;
|
||||
struct veriexec_table_entry;
|
||||
|
||||
/* Veriexec per-table data. */
|
||||
struct veriexec_table_entry {
|
||||
uint64_t vte_count; /* Number of Veriexec entries. */
|
||||
const struct sysctlnode *vte_node;
|
||||
};
|
||||
|
||||
/* Veriexec modes (strict levels). */
|
||||
#define VERIEXEC_LEARNING 0 /* Learning mode. */
|
||||
#define VERIEXEC_IDS 1 /* Intrusion detection mode. */
|
||||
#define VERIEXEC_IPS 2 /* Intrusion prevention mode. */
|
||||
#define VERIEXEC_LOCKDOWN 3 /* Lockdown mode. */
|
||||
extern int veriexec_verbose;
|
||||
extern int veriexec_strict;
|
||||
|
||||
/* Readable values for veriexec_report(). */
|
||||
#define REPORT_ALWAYS 0x01 /* Always print */
|
||||
@ -136,38 +98,31 @@ struct veriexec_table_entry {
|
||||
#define REPORT_ALARM 0x10 /* Alarm - also print pid/uid/.. */
|
||||
#define REPORT_LOGMASK (REPORT_ALWAYS|REPORT_VERBOSE|REPORT_DEBUG)
|
||||
|
||||
/* Initialize a fingerprint ops struct. */
|
||||
#define VERIEXEC_OPINIT(ops, fp_type, hashlen, ctx_size, init_fn, \
|
||||
update_fn, final_fn) \
|
||||
do { \
|
||||
(ops)->type = fp_type; \
|
||||
(ops)->hash_len = (hashlen); \
|
||||
(ops)->context_size = (ctx_size); \
|
||||
(ops)->init = (VERIEXEC_INIT_FN) (init_fn); \
|
||||
(ops)->update = (VERIEXEC_UPDATE_FN) (update_fn); \
|
||||
(ops)->final = (VERIEXEC_FINAL_FN) (final_fn); \
|
||||
} while (0);
|
||||
void veriexecattach(struct device *, struct device *, void *);
|
||||
int veriexecopen(dev_t, int, int, struct lwp *);
|
||||
int veriexecclose(dev_t, int, int, struct lwp *);
|
||||
int veriexecioctl(dev_t, u_long, caddr_t, int, struct lwp *);
|
||||
|
||||
int veriexec_add_fp_ops(struct veriexec_fp_ops *);
|
||||
void veriexec_init(void);
|
||||
struct veriexec_fp_ops *veriexec_find_ops(const char *name);
|
||||
int veriexec_fp_calc(struct lwp *, struct vnode *, struct veriexec_file_entry *,
|
||||
u_char *);
|
||||
int veriexec_fp_cmp(struct veriexec_fp_ops *, u_char *, u_char *);
|
||||
struct veriexec_table_entry *veriexec_tblfind(struct vnode *);
|
||||
int veriexec_fpops_add(const char *, size_t, size_t, veriexec_fpop_init_t,
|
||||
veriexec_fpop_update_t, veriexec_fpop_final_t);
|
||||
struct veriexec_fpops *veriexec_fpops_lookup(const char *);
|
||||
int veriexec_table_add(struct lwp *, prop_dictionary_t);
|
||||
int veriexec_file_add(struct lwp *, prop_dictionary_t);
|
||||
int veriexec_verify(struct lwp *, struct vnode *, const u_char *, int,
|
||||
boolean_t *);
|
||||
struct veriexec_file_entry *veriexec_lookup(struct vnode *);
|
||||
int veriexec_hashadd(struct vnode *, struct veriexec_file_entry *);
|
||||
int veriexec_verify(struct lwp *, struct vnode *,
|
||||
const u_char *, int, struct veriexec_file_entry **);
|
||||
int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *, size_t,
|
||||
struct lwp *);
|
||||
int veriexec_delete(struct lwp *, prop_dictionary_t);
|
||||
void veriexec_convert(struct veriexec_file_entry *, prop_dictionary_t);
|
||||
void veriexec_report(const u_char *, const u_char *, struct lwp *, int);
|
||||
void veriexec_clear(void *, int);
|
||||
void veriexec_purge(struct vnode *);
|
||||
int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *,
|
||||
size_t, struct lwp *);
|
||||
int veriexec_removechk(struct vnode *, const char *, struct lwp *l);
|
||||
int veriexec_renamechk(struct vnode *, const char *, struct vnode *,
|
||||
const char *, struct lwp *);
|
||||
void veriexec_report(const u_char *, const u_char *, struct lwp *, int);
|
||||
void veriexec_clear(void *, int);
|
||||
void veriexec_purge(struct veriexec_file_entry *);
|
||||
|
||||
int veriexec_unmountchk(struct mount *);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_SYS_VERIFIED_EXEC_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user