2005-06-14 00:17:54 +04:00
|
|
|
/* $NetBSD: kern_verifiedexec.c,v 1.20 2005/06/13 20:17:54 elad Exp $ */
|
2002-10-29 15:31:20 +03:00
|
|
|
|
|
|
|
/*-
|
2005-04-20 17:44:45 +04:00
|
|
|
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
|
|
|
|
* Copyright 2005 Brett Lymn <blymn@netbsd.org>
|
2002-10-29 15:31:20 +03:00
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
2005-04-20 17:44:45 +04:00
|
|
|
* by Brett Lymn and Elad Efrat
|
2002-10-29 15:31:20 +03:00
|
|
|
*
|
|
|
|
* 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.
|
2005-04-20 17:44:45 +04:00
|
|
|
* 2. Neither the name of The NetBSD Foundation nor the names of its
|
2002-10-29 15:31:20 +03:00
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
|
* ``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 FOUNDATION OR CONTRIBUTORS
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2003-07-14 18:59:01 +04:00
|
|
|
#include <sys/cdefs.h>
|
2005-06-14 00:17:54 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.20 2005/06/13 20:17:54 elad Exp $");
|
2003-07-14 18:59:01 +04:00
|
|
|
|
2002-10-29 15:31:20 +03:00
|
|
|
#include <sys/param.h>
|
2005-02-27 00:34:55 +03:00
|
|
|
#include <sys/mount.h>
|
2002-10-29 15:31:20 +03:00
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/vnode.h>
|
2005-04-20 17:44:45 +04:00
|
|
|
#include <sys/namei.h>
|
2002-10-29 15:31:20 +03:00
|
|
|
#include <sys/exec.h>
|
2005-04-20 17:44:45 +04:00
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/syslog.h>
|
2005-05-23 02:34:01 +04:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#define VERIEXEC_NEED_NODE
|
2002-10-29 15:31:20 +03:00
|
|
|
#include <sys/verified_exec.h>
|
2005-04-20 17:44:45 +04:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
# include <sys/systm.h>
|
|
|
|
# include <sys/imgact.h>
|
|
|
|
# include <crypto/sha1.h>
|
|
|
|
#else
|
|
|
|
# include <sys/sha1.h>
|
|
|
|
#endif
|
|
|
|
#include "crypto/sha2/sha2.h"
|
|
|
|
#include "crypto/ripemd160/rmd160.h"
|
|
|
|
#include <sys/md5.h>
|
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
int veriexec_verbose = 0;
|
|
|
|
int veriexec_strict = 0;
|
2005-04-20 17:44:45 +04:00
|
|
|
|
2005-05-28 19:49:36 +04:00
|
|
|
char *veriexec_fp_names = NULL;
|
|
|
|
unsigned int veriexec_name_max = 0;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-05-23 02:34:01 +04:00
|
|
|
struct sysctlnode *veriexec_count_node = NULL;
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
/* Veriexecs table of hash types and their associated information. */
|
|
|
|
LIST_HEAD(veriexec_ops_head, veriexec_fp_ops) veriexec_ops_list;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
|
|
|
/*
|
2005-04-20 17:44:45 +04:00
|
|
|
* Add fingerprint names to the global list.
|
2002-10-29 15:31:20 +03:00
|
|
|
*/
|
2005-04-20 17:44:45 +04:00
|
|
|
static void
|
|
|
|
veriexec_add_fp_name(char *name)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
char *newp;
|
2005-05-20 00:16:19 +04:00
|
|
|
unsigned int new_max;
|
2005-04-20 17:44:45 +04:00
|
|
|
|
2005-05-28 19:49:36 +04:00
|
|
|
if (name == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (veriexec_fp_names == NULL) {
|
|
|
|
veriexec_name_max = (VERIEXEC_TYPE_MAXLEN + 1) * 6;
|
|
|
|
veriexec_fp_names = malloc(veriexec_name_max, M_TEMP, M_WAITOK);
|
|
|
|
memset(veriexec_fp_names, 0, veriexec_name_max);
|
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
if ((strlen(veriexec_fp_names) + VERIEXEC_TYPE_MAXLEN + 1) >=
|
|
|
|
veriexec_name_max) {
|
|
|
|
new_max = veriexec_name_max + 4 * (VERIEXEC_TYPE_MAXLEN + 1);
|
2005-05-28 19:49:36 +04:00
|
|
|
newp = realloc(veriexec_fp_names, new_max, M_TEMP, M_WAITOK);
|
2005-04-20 17:44:45 +04:00
|
|
|
veriexec_fp_names = newp;
|
|
|
|
veriexec_name_max = new_max;
|
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
strlcat(veriexec_fp_names, name, veriexec_name_max);
|
|
|
|
strlcat(veriexec_fp_names, " ", veriexec_name_max);
|
|
|
|
}
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-05-28 19:49:36 +04:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
ops->type[sizeof(ops->type) - 1] = '\0';
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (veriexec_find_ops(ops->type) != NULL)
|
|
|
|
return (EEXIST);
|
|
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&veriexec_ops_list, ops, entries);
|
|
|
|
veriexec_add_fp_name(ops->type);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
/*
|
|
|
|
* Initialise the internal "default" fingerprint ops vector list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
veriexec_init_fp_ops(void)
|
|
|
|
{
|
2005-05-28 19:49:36 +04:00
|
|
|
struct veriexec_fp_ops *ops = NULL;
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
LIST_INIT(&veriexec_ops_list);
|
|
|
|
|
2005-05-28 19:49:36 +04:00
|
|
|
#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);
|
|
|
|
#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);
|
|
|
|
#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);
|
|
|
|
#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);
|
|
|
|
#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);
|
|
|
|
#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);
|
|
|
|
#endif /* VERIFIED_EXEC_FP_MD5 */
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
struct veriexec_fp_ops *
|
|
|
|
veriexec_find_ops(u_char *name)
|
|
|
|
{
|
|
|
|
struct veriexec_fp_ops *ops;
|
|
|
|
|
|
|
|
name[VERIEXEC_TYPE_MAXLEN] = '\0';
|
|
|
|
|
|
|
|
LIST_FOREACH(ops, &veriexec_ops_list, entries) {
|
|
|
|
if ((strlen(name) == strlen(ops->type)) &&
|
|
|
|
(strncasecmp(name, ops->type, sizeof(ops->type) - 1)
|
|
|
|
== 0))
|
|
|
|
return (ops);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (NULL);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
/*
|
|
|
|
* Calculate fingerprint. Information on hash length and routines used is
|
|
|
|
* extracted from veriexec_hash_list according to the hash type.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
veriexec_fp_calc(struct proc *p, struct vnode *vp,
|
|
|
|
struct veriexec_hash_entry *vhe, uint64_t size, u_char *fp)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
void *ctx = NULL;
|
|
|
|
u_char *buf = NULL;
|
|
|
|
off_t offset, len;
|
|
|
|
size_t resid;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
/* XXX: This should not happen. Print more details? */
|
|
|
|
if (vhe->ops == NULL) {
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("veriexec: Operations vector is NULL");
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
memset(fp, 0, vhe->ops->hash_len);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
ctx = (void *) malloc(vhe->ops->context_size, M_TEMP, M_WAITOK);
|
2005-05-28 20:37:20 +04:00
|
|
|
buf = (u_char *) malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
(vhe->ops->init)(ctx); /* init the fingerprint context */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The vnode is locked. sys_execve() does it for us; We have our
|
|
|
|
* own locking in vn_open().
|
|
|
|
*/
|
2005-05-28 20:37:20 +04:00
|
|
|
for (offset = 0; offset < size; offset += PAGE_SIZE) {
|
|
|
|
len = ((size - offset) < PAGE_SIZE) ? (size - offset)
|
|
|
|
: PAGE_SIZE;
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
error = vn_rdwr(UIO_READ, vp, buf, len, offset,
|
|
|
|
UIO_SYSSPACE,
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
IO_NODELOCKED,
|
|
|
|
#else
|
|
|
|
0,
|
|
|
|
#endif
|
|
|
|
p->p_ucred, &resid, NULL);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
/* calculate fingerprint for each chunk */
|
2005-04-20 17:44:45 +04:00
|
|
|
(vhe->ops->update)(ctx, buf, (unsigned int) len);
|
|
|
|
}
|
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
/* finalise the fingerprint calculation */
|
2005-04-20 17:44:45 +04:00
|
|
|
(vhe->ops->final)(fp, ctx);
|
|
|
|
|
|
|
|
bad:
|
|
|
|
free(ctx, M_TEMP);
|
|
|
|
free(buf, M_TEMP);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare two fingerprints of the same type. */
|
|
|
|
int
|
2005-05-29 20:07:10 +04:00
|
|
|
veriexec_fp_cmp(struct veriexec_fp_ops *ops, u_char *fp1, u_char *fp2)
|
2005-04-20 17:44:45 +04:00
|
|
|
{
|
|
|
|
#ifdef VERIFIED_EXEC_DEBUG
|
|
|
|
int i;
|
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
if (veriexec_verbose > 1) {
|
2005-04-20 17:44:45 +04:00
|
|
|
printf("comparing hashes...\n");
|
2005-05-29 20:07:10 +04:00
|
|
|
printf("fp1: ");
|
|
|
|
for (i = 0; i < ops->hash_len; i++) {
|
|
|
|
printf("%x", fp1[i]);
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
2005-05-29 20:07:10 +04:00
|
|
|
printf("\nfp2: ");
|
|
|
|
for (i = 0; i < ops->hash_len; i++) {
|
|
|
|
printf("%x", fp2[i]);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
2005-04-20 17:44:45 +04:00
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-05-29 20:07:10 +04:00
|
|
|
return (memcmp(fp1, fp2, ops->hash_len));
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the hash table for the specified device. */
|
|
|
|
struct veriexec_hashtbl *
|
|
|
|
veriexec_tblfind(dev_t device) {
|
|
|
|
struct veriexec_hashtbl *tbl;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
LIST_FOREACH(tbl, &veriexec_tables, hash_list) {
|
|
|
|
if (tbl->hash_dev == device)
|
|
|
|
return (tbl);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (NULL);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
/* Perform a lookup on a hash table. */
|
|
|
|
struct veriexec_hash_entry *
|
|
|
|
veriexec_lookup(dev_t device, ino_t inode)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
struct veriexec_hashtbl *tbl;
|
|
|
|
struct veriexec_hashhead *tble;
|
|
|
|
struct veriexec_hash_entry *e;
|
|
|
|
size_t indx;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
tbl = veriexec_tblfind(device);
|
|
|
|
if (tbl == NULL)
|
|
|
|
return (NULL);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
indx = VERIEXEC_HASH(tbl, inode);
|
|
|
|
tble = &(tbl->hash_tbl[indx & VERIEXEC_HASH_MASK(tbl)]);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
LIST_FOREACH(e, tble, entries) {
|
|
|
|
if ((e != NULL) && (e->inode == inode))
|
|
|
|
return (e);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (NULL);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-04-20 17:44:45 +04:00
|
|
|
* Add an entry to a hash table. If a collision is found, handle it.
|
|
|
|
* The passed entry is allocated in kernel memory.
|
2002-10-29 15:31:20 +03:00
|
|
|
*/
|
|
|
|
int
|
2005-04-20 17:44:45 +04:00
|
|
|
veriexec_hashadd(struct veriexec_hashtbl *tbl, struct veriexec_hash_entry *e)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
struct veriexec_hashhead *vhh;
|
|
|
|
size_t indx;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
if (tbl == NULL)
|
|
|
|
return (EFAULT);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
indx = VERIEXEC_HASH(tbl, e->inode);
|
|
|
|
vhh = &(tbl->hash_tbl[indx]);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
if (vhh == NULL)
|
2005-05-08 22:44:39 +04:00
|
|
|
panic("veriexec: veriexec_hashadd: vhh is NULL.");
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
LIST_INSERT_HEAD(vhh, e, entries);
|
|
|
|
|
2005-05-23 02:34:01 +04:00
|
|
|
tbl->hash_count++;
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (0);
|
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
|
|
|
/*
|
2005-04-20 17:44:45 +04:00
|
|
|
* 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
|
|
|
|
* exec_script(), 'flag' will be VERIEXEC_INDIRECT. If we are called from
|
|
|
|
* vn_open(), 'flag' will be VERIEXEC_FILE.
|
2002-10-29 15:31:20 +03:00
|
|
|
*/
|
2005-04-20 17:44:45 +04:00
|
|
|
int
|
|
|
|
veriexec_verify(struct proc *p, struct vnode *vp, struct vattr *va,
|
|
|
|
const u_char *name, int flag)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
u_char *digest;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
/* Evaluate fingerprint if needed and set the status on the vp. */
|
2005-06-14 00:17:54 +04:00
|
|
|
if ((vp->vhe == NULL) || (vp->fp_status == FINGERPRINT_NOTEVAL)) {
|
|
|
|
vp->vhe = veriexec_lookup(va->va_fsid, va->va_fileid);
|
|
|
|
if (vp->vhe == NULL) {
|
2005-04-20 17:44:45 +04:00
|
|
|
vp->fp_status = FINGERPRINT_NOENTRY;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-06-14 00:17:54 +04:00
|
|
|
|
2005-05-08 22:44:39 +04:00
|
|
|
veriexec_dprintf(("veriexec: veriexec_verify: Got entry for "
|
2005-04-20 17:44:45 +04:00
|
|
|
"%s. (dev=%d, inode=%u)\n", name,
|
|
|
|
va->va_fsid, va->va_fileid));
|
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
digest = (u_char *) malloc(vp->vhe->ops->hash_len, M_TEMP,
|
2005-04-20 17:44:45 +04:00
|
|
|
M_WAITOK);
|
2005-06-14 00:17:54 +04:00
|
|
|
error = veriexec_fp_calc(p, vp, vp->vhe, va->va_size,
|
|
|
|
digest);
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
if (error) {
|
2005-05-08 22:44:39 +04:00
|
|
|
veriexec_dprintf(("veriexec: veriexec_verify: "
|
2005-04-20 17:44:45 +04:00
|
|
|
"Calculation error.\n"));
|
|
|
|
free(digest, M_TEMP);
|
|
|
|
return (error);
|
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
if (veriexec_fp_cmp(vp->vhe->ops, vp->vhe->fp, digest) == 0) {
|
|
|
|
if (vp->vhe->type == VERIEXEC_INDIRECT) {
|
2005-04-20 17:44:45 +04:00
|
|
|
vp->fp_status = FINGERPRINT_INDIRECT;
|
|
|
|
} else {
|
|
|
|
vp->fp_status = FINGERPRINT_VALID;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vp->fp_status = FINGERPRINT_NOMATCH;
|
|
|
|
}
|
|
|
|
free(digest, M_TEMP);
|
|
|
|
}
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-06-14 00:17:54 +04:00
|
|
|
switch (flag) {
|
|
|
|
case VERIEXEC_DIRECT:
|
|
|
|
case VERIEXEC_INDIRECT:
|
|
|
|
break;
|
|
|
|
case VERIEXEC_FILE:
|
|
|
|
if (vp->vhe->type != VERIEXEC_FILE) {
|
|
|
|
veriexec_report("Execution of 'FILE' entry.",
|
|
|
|
name, va, p, REPORT_NOVERBOSE,
|
|
|
|
REPORT_ALARM, REPORT_NOPANIC);
|
|
|
|
|
|
|
|
if (veriexec_strict > 1)
|
|
|
|
return (EPERM);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
out:
|
|
|
|
switch (vp->fp_status) {
|
|
|
|
case FINGERPRINT_NOTEVAL:
|
|
|
|
/* Should not happen. */
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Not-evaluated status "
|
|
|
|
"post evaluation; inconsistency detected.", name, va,
|
|
|
|
NULL, REPORT_NOVERBOSE, REPORT_NOALARM, REPORT_PANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
case FINGERPRINT_VALID:
|
|
|
|
/* Valid fingerprint. */
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Match.", name, va, NULL,
|
|
|
|
REPORT_VERBOSE, REPORT_NOALARM, REPORT_NOPANIC);
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
break;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
case FINGERPRINT_INDIRECT:
|
|
|
|
/* Fingerprint is okay; Make sure it's indirect execution. */
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Match. [indirect]",
|
|
|
|
name, va, NULL, REPORT_VERBOSE, REPORT_NOALARM,
|
|
|
|
REPORT_NOPANIC);
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
if (flag == VERIEXEC_DIRECT) {
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Direct "
|
|
|
|
"execution.", name, va, NULL,
|
|
|
|
REPORT_NOVERBOSE, REPORT_ALARM,
|
|
|
|
REPORT_NOPANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
if (veriexec_strict > 0)
|
|
|
|
error = EPERM;
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
break;
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
case FINGERPRINT_NOMATCH:
|
|
|
|
/* Fingerprint mismatch. Deny execution. */
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Mismatch.", name, va,
|
|
|
|
NULL, REPORT_NOVERBOSE, REPORT_ALARM, REPORT_NOPANIC);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
if (veriexec_strict > 0)
|
2005-04-20 17:44:45 +04:00
|
|
|
error = EPERM;
|
2005-05-20 00:16:19 +04:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FINGERPRINT_NOENTRY:
|
|
|
|
/* No entry in the list. */
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: No entry.", name, va,
|
|
|
|
p, REPORT_VERBOSE, REPORT_NOALARM, REPORT_NOPANIC);
|
|
|
|
|
|
|
|
/* We don't care about these in learning mode. */
|
2005-06-14 00:17:54 +04:00
|
|
|
if (veriexec_strict == 0) {
|
2005-05-20 00:16:19 +04:00
|
|
|
break;
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
/*
|
|
|
|
* Deny access to files with no entry if
|
|
|
|
* - File is being executed, and we're in strict
|
|
|
|
* level 1; or
|
|
|
|
* - File is being accessed, and we're in strict
|
|
|
|
* level 2.
|
|
|
|
*/
|
|
|
|
if ((veriexec_strict > 1) ||
|
|
|
|
((veriexec_strict == 1) &&
|
|
|
|
((flag == VERIEXEC_FILE) || (flag == VERIEXEC_INDIRECT))))
|
|
|
|
error = EPERM;
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Should never happen.
|
|
|
|
* XXX: Print vnode/process?
|
|
|
|
*/
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_verify: Invalid stats "
|
|
|
|
"post evaluation.", name, va, NULL, REPORT_NOVERBOSE,
|
|
|
|
REPORT_NOALARM, REPORT_PANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-04-20 17:44:45 +04:00
|
|
|
* Veriexec remove policy code. If we have an entry for the file in our
|
|
|
|
* tables, we disallow removing if the securelevel is high or we're in
|
|
|
|
* strict mode.
|
2002-10-29 15:31:20 +03:00
|
|
|
*/
|
|
|
|
int
|
2005-04-20 17:44:45 +04:00
|
|
|
veriexec_removechk(struct proc *p, struct vnode *vp, const char *pathbuf)
|
2002-10-29 15:31:20 +03:00
|
|
|
{
|
2005-04-20 17:44:45 +04:00
|
|
|
struct veriexec_hashtbl *tbl;
|
|
|
|
struct veriexec_hash_entry *vhe = NULL;
|
|
|
|
struct vattr va;
|
|
|
|
int error;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
error = VOP_GETATTR(vp, &va, p->p_ucred, p);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
2005-05-20 00:16:19 +04:00
|
|
|
/*
|
|
|
|
* Evaluate fingerprint to eliminate FINGERPRINT_NOTEVAL.
|
|
|
|
* The flag here should have no affect on the return value.
|
|
|
|
*/
|
|
|
|
error = veriexec_verify(p, vp, &va, pathbuf, VERIEXEC_FILE);
|
|
|
|
if (error) {
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
switch (vp->fp_status) {
|
|
|
|
case FINGERPRINT_VALID:
|
|
|
|
case FINGERPRINT_INDIRECT:
|
|
|
|
case FINGERPRINT_NOMATCH:
|
2005-05-20 00:16:19 +04:00
|
|
|
if (veriexec_strict > 0) {
|
|
|
|
veriexec_report("veriexec_removechk: Denying "
|
|
|
|
"unlink.", pathbuf, &va, p, REPORT_NOVERBOSE,
|
|
|
|
REPORT_ALARM, REPORT_NOPANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
error = EPERM;
|
|
|
|
} else {
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_removechk: Removing "
|
|
|
|
"entry.", pathbuf, &va, NULL,
|
|
|
|
REPORT_NOVERBOSE, REPORT_NOALARM,
|
|
|
|
REPORT_NOPANIC);
|
2005-04-24 16:58:26 +04:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
goto veriexec_rm;
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
2005-02-27 00:34:55 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FINGERPRINT_NOENTRY:
|
2005-05-20 00:16:19 +04:00
|
|
|
if (veriexec_strict > 1) {
|
|
|
|
veriexec_report("veriexec_removechk: Denying "
|
|
|
|
"unlink. [strict]", pathbuf, &va, p,
|
|
|
|
REPORT_NOVERBOSE, REPORT_ALARM, REPORT_NOPANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
error = EPERM;
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
2005-04-20 17:44:45 +04:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_removechk: Invalid status post "
|
|
|
|
"evaluation; inconsistency detected.", pathbuf, &va,
|
|
|
|
NULL, REPORT_NOVERBOSE, REPORT_NOALARM, REPORT_PANIC);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (error);
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
veriexec_rm:
|
2005-05-20 00:16:19 +04:00
|
|
|
vhe = veriexec_lookup(va.va_fsid, va.va_fileid);
|
|
|
|
if (vhe == NULL) {
|
|
|
|
veriexec_report("veriexec_removechk: Inconsistency "
|
|
|
|
"detected: Trying to remove entry without having one.",
|
|
|
|
pathbuf, &va, NULL, REPORT_NOVERBOSE, REPORT_NOALARM,
|
|
|
|
REPORT_PANIC);
|
|
|
|
}
|
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
tbl = veriexec_tblfind(va.va_fsid);
|
|
|
|
if (tbl == NULL) {
|
2005-05-20 00:16:19 +04:00
|
|
|
veriexec_report("veriexec_removechk: Inconsistency "
|
|
|
|
"detected: Could not get table for file in lists.",
|
|
|
|
pathbuf, &va, NULL, REPORT_NOVERBOSE, REPORT_NOALARM,
|
|
|
|
REPORT_PANIC);
|
2005-04-20 17:44:45 +04:00
|
|
|
}
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
LIST_REMOVE(vhe, entries);
|
|
|
|
free(vhe->fp, M_TEMP);
|
|
|
|
free(vhe, M_TEMP);
|
2005-05-23 02:34:01 +04:00
|
|
|
tbl->hash_count--;
|
2005-06-14 00:17:54 +04:00
|
|
|
vp->fp_status = FINGERPRINT_NOENTRY;
|
|
|
|
vp->vhe = NULL;
|
2002-10-29 15:31:20 +03:00
|
|
|
|
2005-04-20 17:44:45 +04:00
|
|
|
return (error);
|
2002-10-29 15:31:20 +03:00
|
|
|
}
|
2005-05-20 00:16:19 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine for maintaining mostly consistent message formats in Verified
|
|
|
|
* Exec.
|
|
|
|
*
|
|
|
|
* 'verbose_only' - if 1, the message will be printed only if veriexec is
|
|
|
|
* in verbose mode.
|
|
|
|
* 'alarm' - if 1, the message is considered an alarm and will be printed
|
|
|
|
* at all times along with pid and user credentials.
|
|
|
|
* 'die' - if 1, the system will call panic() instead of printf().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
veriexec_report(const u_char *msg, const u_char *filename,
|
|
|
|
struct vattr *va, struct proc *p, int verbose_only,
|
|
|
|
int alarm, int die)
|
|
|
|
{
|
|
|
|
void (*f)(const char *, ...);
|
|
|
|
|
|
|
|
if (msg == NULL || filename == NULL || va == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (die)
|
|
|
|
f = panic;
|
|
|
|
else
|
|
|
|
f = (void (*)(const char *, ...)) printf;
|
|
|
|
|
|
|
|
if (!verbose_only || veriexec_verbose) {
|
|
|
|
if (!alarm || p == NULL)
|
|
|
|
f("veriexec: %s [%s, %d:%u%s", msg, filename,
|
|
|
|
va->va_fsid, va->va_fileid,
|
|
|
|
die ? "]" : "]\n");
|
|
|
|
else
|
|
|
|
f("veriexec: %s [%s, %d:%u, pid=%u, uid=%u, "
|
|
|
|
"gid=%u%s", msg, filename, va->va_fsid,
|
|
|
|
va->va_fileid, p->p_pid, p->p_cred->p_ruid,
|
|
|
|
p->p_cred->p_rgid, die ? "]" : "]\n");
|
|
|
|
}
|
|
|
|
}
|