[RFC][PATCH 1/3] EVM

From: David Safford (safford@private)
Date: Tue Nov 15 2005 - 06:08:53 PST


EVM - Extended Verification Module

EVM is a mandatory access control LSM module which measures the
integrity of a file’s data and metadata, and allows or denies access
based on the measured integrity. Integrity measurements for each file
are stored in configured extended attributes. EVM has one built-in
extended attribute, security.evm.hmac, which contains an HMAC-sha1
across all configured extended attributes, and which is keyed by the
kernel master key obtained from the TPM (Trusted Platform Module)
device driver, based on a trusted boot. Since the kernel master
key is unsealed by the hardware TPM only as a result of a valid
trusted boot, and the key is never visible outside the kernel,
the EVM HMAC attribute cannot be forged in an offline attack.

EVM is configured by writing a configuration file to
/sys/kernel/security/evm/config. Normally, this configuration is stored
in /etc/evm.conf. The configuration file defines the names and types
of all monitored extended attributes, beyond the built-in HMAC.
Attributes may be of type MD5, SHA1 or STRING. Attributes of type
MD5 or SHA1 indicate the respective hash of the file's data. EVM
will perform the respective hash on the file's data, and compare
to verify the integrity of the data. Attributes of type STRING
simply have their names and values included in the calculated
HMAC attribute. The EVM configuration specifies cache timeouts
and attribute expiration times. For performance reasons, the
results of EVM data and metadata verification are cached in an
inode security structure, until the refresh time is reached.
A typical EVM configuration looks like:

# extended attribute    refresh         expire          type
xattr security.evm.hash -cache 300 -expire 7776000 -type MD5
xattr security.evm.sha1 -cache 300 -expire 7776000 -type SHA1
xattr security.evm.mutable -cache 300 -expire 7776000 -type STRING
xattr security.slim.level -cache 300 -expire 7776000 -type STRING

When LSM stacked with SLIM, EVM can be configured to measure the
security.slim.level attributes. Similarly, if stacked with selinux, it can
measure the security.selinux label on files. When stacked with another
mandatory access  control LSM module, EVM can defer enforcement to the
other module, so that rather than simply denying access to apparently
tampered files, the respective process can be sandboxed in some way.

EVM accepts the module parameter:

   evm_mode=NORMAL | LOGONLY | INSTALL
       evm defaults to NORMAL mode, in which all rules are enforced,
       unless MAC sandboxing is available from another stacked LSM
       module, such as SLIM.  LOGONLY mode is useful for installation
       of evm on an existing system. It logs access control decisions,
       but does not enforce them. It does update the security.evm.hmac
       attributes if a sufficiently privileged process writes any of the
       monitored attributes, typically with a program like tc_label.

EVM securityfs files:
       /sys/kernel/security/evm/config       - configuration input
       /sys/kernel/security/evm/debug/cache  - debugging control
       /sys/kernel/security/evm/debug/xattr  - debugging control
       /sys/kernel/security/evm/debug/crypto - debugging control

EVM debugging can be enabled by writing a ’1’ to the xattr, cache, and
crypto files in /sys/kernel/security/evm/debug/. Similarly the debugging
can be disabled by writing ’0’ to these files.


diff -rpuN linux-2.6.14.2.orig/include/linux/evm.h linux-2.6.14.2.tc/include/linux/evm.h
--- linux-2.6.14.2.orig/include/linux/evm.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/include/linux/evm.h	2005-11-11 13:48:03.000000000 -0500
@@ -0,0 +1,17 @@
+/*
+ *  evm_export.h - exported function call definitions
+ *
+ */
+
+extern int sandbox_avail; /* can't use MODULE_STATE_GOING notification */
+
+typedef enum {
+	EVM_PERMIT = 0, EVM_SANDBOX = -1, EVM_DENY = -2, EVM_NOLABEL = -3
+} evm_status;
+
+extern evm_status evm_analyze_cacheinfo(struct dentry *dentry);
+extern int evm_idx;
+extern int evm_setxattr (struct dentry *d, char *name, void *value,
+                               size_t size, int flags);
+extern int evm_update_hmac(struct dentry *dentry, int flags);
+extern int mem2hex(char *mem, char *buffer, int count);
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_cache.c linux-2.6.14.2.tc/security/evm/evm_cache.c
--- linux-2.6.14.2.orig/security/evm/evm_cache.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_cache.c	2005-11-11 13:48:30.000000000 -0500
@@ -0,0 +1,573 @@
+/*
+ * evm_cache.c
+ *
+ * Cache the extended verification results in inode->i_security.
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/namei.h>
+#include <linux/evm.h>
+
+#include "evm.h"
+
+int evm_idx;
+
+/*
+ * Invalidate the cached information when the file actually changes.
+ * Flag mutable files in order to later update hash value, providing
+ * protection from offline attack.
+ */
+int evm_file_permission(struct file *file, int mask)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct evm_isec_cache *isec = NULL;
+	struct evm_isec_xattr *istatus, *istatus_p;
+	struct timespec now;
+
+	if ((mask & MAY_WRITE) || (mask & MAY_APPEND)) {
+		isec = evm_get_isec(inode->i_security);
+		if (isec != NULL) {
+			isec->file_mutable = 0;
+			istatus =
+			    (void *) isec + sizeof(struct evm_isec_cache);
+			if ((istatus->xattr_id == 0)	/* unlabeled HMAC */
+			    &&(istatus->xattr_flags == 0))
+				return 0;
+
+			for_each_xattr(istatus_p, istatus + 1,
+				       isec->cache_size) {
+				if (istatus_p->xattr_id == 0)
+					continue;
+
+				if (memcmp(istatus_p->xattr_name,
+					   "security.evm.mutable", 20) == 0)
+					isec->file_mutable = 1;
+			}
+			if (!isec->file_mutable)
+				isec->cache_flag = EVM_CACHE_DIRTY;
+			now = CURRENT_TIME;
+			isec->cache_refresh = now.tv_sec;
+		}
+	}
+	return 0;
+}
+
+/*
+ * In install mode, update the evm.hash extended attribute value, not only
+ * for mutable files, but for all files and log it.
+ */
+void evm_file_install_free(struct file *file)
+{
+	struct inode *inode = NULL;
+	char hashValue[SHA1_DIGEST_SIZE + 1];
+	char hashStr[SHA1_STR_SIZE + 11];
+	mode_t sav_fmode;
+	int rc;
+
+	if (!file || !file->f_dentry || !(file->f_mode & MAY_WRITE))
+		return;
+
+	inode = file->f_dentry->d_inode;
+	if (!inode)
+		return;
+
+	if (!S_ISREG(inode->i_mode))
+		return;
+
+	sav_fmode = file->f_mode;
+	file->f_mode |= FMODE_READ;
+	rc = evm_calc_hash(file, hashValue, EVM_TYPE_MD5);
+	file->f_mode = sav_fmode;
+	if (rc == 0) {
+		struct timespec now;
+		int len;
+		time_t nl_time;
+
+		memset(hashStr, 0, sizeof hashStr);
+		now = CURRENT_TIME;
+		nl_time = htonl(now.tv_sec);
+		memcpy(hashStr, &nl_time, sizeof(time_t));
+		len = mem2hex(hashValue, hashStr + sizeof(time_t),
+			      MD5_DIGEST_SIZE);
+		rc = evm_setxattr(file->f_dentry, "security.evm.hash",
+				  hashStr, sizeof(time_t) + len, 0);
+		printk(KERN_INFO "install: %s updated hash \n",
+		       file->f_dentry->d_name.name);
+	}
+}
+
+/*
+ * Called on close.  For mutable files, update the evm.hash extended
+ * attribute value.
+ */
+void evm_file_free(struct file *file)
+{
+	struct inode *inode = NULL;
+	char hashValue[SHA1_DIGEST_SIZE + 1];
+	char hashStr[SHA1_STR_SIZE + 11];
+	struct evm_isec_cache *isec;
+	int rc;
+
+	if (!file || !file->f_dentry)
+		return;
+
+	inode = file->f_dentry->d_inode;
+	if (!inode)
+		return;
+
+	isec = evm_get_isec(inode->i_security);
+	if (!isec)
+		return;
+
+	if (isec->file_mutable) {
+		mode_t sav_fmode;
+
+		sav_fmode = file->f_mode;
+		file->f_mode |= FMODE_READ;
+		rc = evm_calc_hash(file, hashValue, EVM_TYPE_MD5);
+		file->f_mode = sav_fmode;
+		if (rc == 0) {
+			struct timespec now;
+			int len;
+			time_t nl_time;
+
+			memset(hashStr, 0, sizeof hashStr);
+			now = CURRENT_TIME;
+			nl_time = htonl(now.tv_sec);
+			memcpy(hashStr, &nl_time, sizeof(time_t));
+			len = mem2hex(hashValue, hashStr + sizeof(time_t),
+				      MD5_DIGEST_SIZE);
+			rc = evm_setxattr(file->f_dentry,
+					  "security.evm.hash", hashStr,
+					  sizeof(time_t) + len, 0);
+		}
+		isec->file_mutable = 0;
+	}
+}
+
+static int evm_getxattr(struct dentry *dentry, char *xattrName,
+			char **xattr_value, int xattr_size)
+{
+	char *value = *xattr_value;
+	int size;
+	int error;
+
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->getxattr)
+		return xattr_size;
+
+	if ((size = dentry->d_inode->i_op->getxattr(dentry,
+						    xattrName, NULL,
+						    0)) < 0)
+		return size;
+
+	if (size > xattr_size) {
+		if (value)
+			kfree(value);
+		xattr_size = size;
+		value = kmalloc(xattr_size + 1, GFP_KERNEL);
+		if (!value) {
+			xattr_size = 0;
+			return -ENOMEM;
+		}
+		memset(value, 0, xattr_size + 1);
+	} else
+		memset(value, 0, xattr_size);
+	error = dentry->d_inode->i_op->getxattr(dentry, xattrName,
+						value, xattr_size);
+	*xattr_value = value;
+	return xattr_size;
+
+}
+
+static void evm_verify_timestamp(struct evm_isec_xattr *istatus,
+				 char *xattr_value, time_t xattrExpire)
+{
+	time_t timestamp;
+	struct timespec now;
+
+	now = CURRENT_TIME;
+	timestamp = ntohl(*((time_t *) xattr_value));
+	if (now.tv_sec > (timestamp + xattrExpire))
+		istatus->xattr_flags |= EVM_XATTR_EXPIRED;
+	return;
+}
+
+static void evm_verify_enum(struct evm_isec_xattr *istatus,
+			    struct nameidata *nd, char value)
+{
+	const unsigned char *debug_name = "???";
+
+	if (nd && nd->dentry && nd->dentry->d_name.name)
+		debug_name = nd->dentry->d_name.name;
+
+	switch (value) {
+	case EVM_VALUE_PASS:
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+		dprintk(EVM_CACHE, "%s: %s(%s) passed\n",
+			__FUNCTION__, debug_name, istatus->xattr_name);
+		break;
+	case EVM_VALUE_FAIL:
+		istatus->xattr_flags |= EVM_XATTR_FAIL;
+		dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+			__FUNCTION__, debug_name, istatus->xattr_name);
+		break;
+	case EVM_VALUE_EXPIRED:
+		istatus->xattr_flags |= EVM_XATTR_EXPIRED;
+		dprintk(EVM_CACHE, "%s: %s(%s) expired\n",
+			__FUNCTION__, debug_name, istatus->xattr_name);
+		break;
+	default:
+		dprintk(EVM_CACHE, "%s: %s(%s) unknown\n",
+			__FUNCTION__, debug_name, istatus->xattr_name);
+		break;
+	}
+	return;
+}
+
+static void evm_verify_md5(struct evm_isec_xattr *istatus,
+			   struct nameidata *nd, char *xattr_value,
+			   int xattr_size, char *hashValue)
+{
+	char hashStr[MD5_STR_SIZE + 1];	/* MD5 MAC as a string */
+	const unsigned char *debug_name = "???";
+
+
+	if (evm_calc_dhash(nd, hashValue, EVM_TYPE_MD5) != 0)
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+	else {
+		int len;
+		memset(hashStr, 0, sizeof hashStr);
+		len = mem2hex(hashValue, hashStr, MD5_DIGEST_SIZE);
+		if (nd && nd->dentry && nd->dentry->d_name.name)
+			debug_name = nd->dentry->d_name.name;
+
+		if (memcmp(hashStr, xattr_value + sizeof(time_t),
+			   xattr_size - sizeof(time_t)) != 0) {
+			dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+				__FUNCTION__, debug_name,
+				istatus->xattr_name);
+			dprintk(EVM_CACHE, "xattr %s\n",
+				xattr_value + sizeof(time_t));
+			dprintk(EVM_CACHE, "value %s\n", hashStr);
+			istatus->xattr_flags |= EVM_XATTR_FAIL;
+		} else {
+			dprintk(EVM_CACHE, "%s: %s(%s) succeeded\n",
+				__FUNCTION__, debug_name,
+				istatus->xattr_name);
+			istatus->xattr_flags |= EVM_XATTR_PASS;
+		}
+	}
+	return;
+}
+
+static void evm_verify_sha1(struct evm_isec_xattr *istatus,
+			    struct nameidata *nd, char *xattr_value,
+			    int xattr_size, char *hashValue)
+{
+	char hashStr[SHA1_STR_SIZE + 1];	/* SHA1 string */
+	const unsigned char *debug_name = "???";
+
+
+	if (evm_calc_dhash(nd, hashValue, EVM_TYPE_SHA1) != 0)
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+	else {
+		int len;
+		memset(hashStr, 0, sizeof hashStr);
+		len = mem2hex(hashValue, hashStr, SHA1_DIGEST_SIZE);
+		if (nd && nd->dentry && nd->dentry->d_name.name)
+			debug_name = nd->dentry->d_name.name;
+
+		if (memcmp(hashStr, xattr_value + sizeof(time_t),
+			   xattr_size - sizeof(time_t)) != 0) {
+			dprintk(EVM_CACHE, "%s: %s(%s) failed\n",
+				__FUNCTION__, debug_name,
+				istatus->xattr_name);
+			dprintk(EVM_CACHE, "xattr %s\n",
+				xattr_value + sizeof(time_t));
+			dprintk(EVM_CACHE, "value %s\n", hashStr);
+			istatus->xattr_flags |= EVM_XATTR_FAIL;
+		} else {
+			dprintk(EVM_CACHE, "%s: %s(%s) succeeded\n",
+				__FUNCTION__, debug_name,
+				istatus->xattr_name);
+			istatus->xattr_flags |= EVM_XATTR_PASS;
+		}
+	}
+	return;
+}
+
+static int evm_verify_hmac(struct dentry *dentry, struct inode *inode)
+{
+	int rc = 0;
+	struct evm_isec_cache *isec;
+	struct evm_isec_xattr *istatus;
+	char hmacVal[SHA1_DIGEST_SIZE];
+	char trustedHMAC[SHA1_DIGEST_SIZE];
+	struct timespec now;
+	const unsigned char *debug_name = "???";
+
+	if (!inode)
+		return -EINVAL;
+
+	isec = evm_get_isec(inode->i_security);
+	if (!isec)
+		return -EINVAL;
+
+	istatus = (void *) isec + sizeof(struct evm_isec_cache);
+	istatus->xattr_id = 0;	/* HMAC */
+
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->getxattr) {
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+		return -EINVAL;
+	}
+
+	rc = evm_calc_hmac(dentry, hmacVal);
+	if (rc < 0)
+		return rc;
+
+	if (dentry->d_name.name)
+		debug_name = dentry->d_name.name;
+	rc = evm_verify_xattr(dentry, hmacVal, "security.evm.hmac",
+			      trustedHMAC, SHA1_DIGEST_SIZE);
+	if (rc < 0) {
+		switch (rc) {
+		case -EOPNOTSUPP:	/* files not supported like /dev */
+			isec->cache_flag = EVM_CACHE_EOPNOTSUPP;
+			dprintk(EVM_CACHE,
+				"%s: %s getxattr not supported\n",
+				__FUNCTION__, debug_name);
+			break;
+		case -ENODATA:	/* files not labelled don't fail */
+			istatus->xattr_flags = EVM_XATTR_NOLABEL;
+			dprintk(EVM_CACHE, "%s: '%s' no HMAC label \n",
+				__FUNCTION__, debug_name);
+			break;
+		case -EPERM:
+			istatus->xattr_flags |= EVM_XATTR_FAIL;
+			break;
+		default:
+			dprintk(EVM_CACHE, "%s: %s rc = %d not "
+				"EOPNOTSUPP, ENODATA or EPERM\n",
+				__FUNCTION__, debug_name, rc);
+			break;
+		}
+
+		now = CURRENT_TIME;
+		isec->cache_refresh = now.tv_sec + 300;	/* min refresh */
+	} else {
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+		dprintk(EVM_CACHE, "%s: %s HMAC verification "
+			"succeeeded\n", __FUNCTION__, debug_name);
+	}
+
+	return rc;
+}
+
+/*
+ * Determine whether or not the security section is valid and up to date.
+ */
+static int evm_valid_security(struct dentry *dentry, struct inode *inode)
+{
+	int error = 0;
+	struct evm_isec_cache *isec;
+	struct timespec now;
+
+	if (!inode)
+		return -EINVAL;
+
+	isec = evm_get_isec(inode->i_security);
+	if (!isec)
+		return -EINVAL;
+
+		switch (isec->cache_flag) {
+	case EVM_CACHE_DIRTY:
+		error = -EINVAL;
+		break;
+	case EVM_CACHE_EOPNOTSUPP:
+		error = 0;
+	default:
+		break;
+	}
+
+	now = CURRENT_TIME;
+	if ((isec->cache_refresh != 0)
+	    && (now.tv_sec > isec->cache_refresh)) {
+		const unsigned char *debug_name = "???";
+
+		error = -EINVAL;	/* Cache entry expired */
+		if (dentry && dentry->d_name.name)
+			debug_name = dentry->d_name.name;
+		dprintk(EVM_CACHE, "%s: %s cache entry expired\n",
+			__FUNCTION__, debug_name);
+	}
+	return error;
+}
+
+static struct evm_isec_cache *evm_alloc_security(void)
+{
+	struct evm_isec_cache *isec;
+	int evm_isec_size;	/* size of inode->i_security buffer */
+	struct evm_xattr_config *config_data;
+
+	evm_isec_size = evm_get_isec_size();
+	isec =
+	    (struct evm_isec_cache *) kmalloc(evm_isec_size, GFP_KERNEL);
+	if (isec) {
+		memset(isec, 0, evm_isec_size);
+		evm_get_config_info(&config_data, &isec->cache_size,
+				    &isec->cache_ver);
+	}
+	return isec;
+}
+
+static void evm_update_cachehdr(struct evm_isec_cache *isec,
+				time_t xattrRefresh)
+{
+	struct timespec now;
+	struct evm_xattr_config *config_data;
+
+	if (!isec)
+		return;
+	now = CURRENT_TIME;
+	evm_get_config_info(&config_data, &isec->cache_size,
+			    &isec->cache_ver);
+	isec->cache_refresh = now.tv_sec + xattrRefresh;
+	isec->cache_flag = EVM_CACHE_CLEAN;
+}
+
+static void update_istatus(struct evm_xattr_config *config_p,
+			   struct nameidata *nd,
+			   struct evm_isec_xattr *istatus,
+			   char *xattr_value, int xattr_size,
+			   char *hash_value)
+{
+	if (!config_p || !nd || !istatus || !xattr_value)
+		return;
+
+	istatus->xattr_flags = 0;
+
+	evm_verify_timestamp(istatus, xattr_value, config_p->xattr_expire);
+	istatus->xattr_name = config_p->xattr_name;
+	switch (config_p->xattr_type) {
+	case EVM_TYPE_SHA1:
+		istatus->xattr_id = config_p->xattr_id;
+		evm_verify_sha1(istatus, nd, xattr_value, xattr_size,
+				hash_value);
+		break;
+	case EVM_TYPE_MD5:
+		istatus->xattr_id = config_p->xattr_id;
+		evm_verify_md5(istatus, nd, xattr_value, xattr_size,
+			       hash_value);
+		break;
+	case EVM_TYPE_ENUM:
+		istatus->xattr_id = config_p->xattr_id;
+		evm_verify_enum(istatus, nd,
+				*(xattr_value) + sizeof(time_t));
+		break;
+	case EVM_TYPE_STRING:
+		istatus->xattr_id = config_p->xattr_id;
+		istatus->xattr_flags |= EVM_XATTR_PASS;
+		break;
+	case EVM_TYPE_HMAC:
+		dprintk(EVM_CACHE, "%s: HMAC is special type\n",
+			__FUNCTION__);
+		break;
+	default:
+		printk("evm: DEFAULT\n");
+		break;
+	}
+	return;
+}
+
+/*
+ * Calculate the extended attribute "security.evm.hmac" based on
+ * the extended attributes listed in evm_config (i.e. /etc/evm.config)
+ * and verify it matches the "security.evm.hmac" value.  Only after
+ * validating the HMAC, verify the individual extended attribute values
+ * (i.e. the file's hash).
+
+ * The evaluation results are "cached" in the inode security structure
+ * (inode->i_security).
+ */
+int evm_inode_permission(struct inode *inode, int mask,
+			 struct nameidata *nd)
+{
+	struct evm_isec_cache *isec;
+	struct evm_isec_xattr *istatus;
+	struct dentry *dentry;
+	ssize_t error = 0;
+
+	int xattr_size = 0;
+	char *xattr_value = NULL;
+	struct evm_xattr_config *config_data, *config_p;
+	time_t xattr_refresh = 0;
+
+	if (!nd || !nd->dentry || !inode)
+		return 0;
+
+	dentry = nd->dentry;
+	if ((error = evm_valid_security(dentry, inode)) == 0)	/* Valid? */
+		return 0;
+
+	isec = evm_get_isec(inode->i_security);
+	if (!isec) {
+		isec = evm_alloc_security();
+		if (!isec)
+			return (-ENOMEM);
+		evm_set_isec(inode->i_security, isec);
+	}
+
+	/*Numerous reasons for no hmac, errors cached in istatus->xattr_flags.*/
+	error = evm_verify_hmac(dentry, inode);
+	if (error < 0)
+		return 0;
+
+	istatus = (void *) isec + sizeof(struct evm_isec_cache);
+	evm_get_config_info(&config_data, &isec->cache_size,
+			    &isec->cache_ver);
+
+	for_each_xattr(config_p, config_data, isec->cache_size) {
+		istatus++;
+		xattr_size = evm_getxattr(dentry, config_p->xattr_name,
+					  &xattr_value, xattr_size);
+		if (xattr_size > 0) {
+			update_istatus(config_p, nd, istatus, xattr_value,
+				       xattr_size, isec->hash);
+			if ((xattr_refresh > config_p->xattr_refresh)
+			    || (xattr_refresh == 0))
+				xattr_refresh = config_p->xattr_refresh;
+		} else
+			istatus->xattr_flags = 0;
+	}
+
+	evm_update_cachehdr(isec, xattr_refresh);
+
+	if (xattr_value)
+		kfree(xattr_value);
+	return 0;
+}
+
+void evm_inode_free_security(struct inode *inode)
+{
+	struct evm_isec_cache *isec;
+
+	isec = evm_del_isec(inode->i_security);
+	if (isec)
+		kfree(isec);
+}
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_config.c linux-2.6.14.2.tc/security/evm/evm_config.c
--- linux-2.6.14.2.orig/security/evm/evm_config.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_config.c	2005-11-11 13:43:18.000000000 -0500
@@ -0,0 +1,285 @@
+/*
+ * EVM - Extended Verification Module
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "evm.h"
+
+/*
+ * Hard coded default configuration values
+ */
+static struct evm_xattr_config evm_default_data[] = {
+	{"security.evm.hash", 300, 7776000, EVM_TYPE_MD5, 0},
+	{"security.evm.mutable", 300, 7776000, EVM_TYPE_STRING, 0},
+	{"security.evm.version", 300, 7776000, EVM_TYPE_ENUM, 0},
+	{"security.evm.antivirus", 300, 7776000, EVM_TYPE_ENUM, 0}
+};
+static int evm_default_datasize = 4;	/* number of extended attributes */
+
+/*
+ * Pointer to running configuration data
+ */
+static struct evm_xattr_config *evm_config_data;
+static int evm_config_datasize = 0;	/* number of extended attributes */
+static int evm_config_version;	/* using a timestamp as a config version no. */
+
+/*
+ * inode->security information
+ */
+static int evm_isec_size = 0;	/* size of inode->i_security buffer */
+static int evm_config_id = 1;	/* HMAC is always 0; start with 1 */
+int evm_get_isec_size(void)
+{
+	return evm_isec_size;
+}
+
+int evm_get_config_info(struct evm_xattr_config **head, int *size,
+			time_t * version)
+{
+	*head = evm_config_data;
+	*size = evm_config_datasize;
+	*version = evm_config_version;
+	return 0;
+}
+
+/*
+ * Initialize the Extended Verification module
+ */
+int evm_update_config(struct evm_xattr_config *evm_new_data,
+		      int evm_new_datasize)
+{
+	struct evm_xattr_config *config_p;
+	struct evm_xattr_config *evm_old_data;
+	int evm_old_datasize = 0;
+
+	ssize_t error = 0;
+
+	for_each_xattr(config_p, evm_new_data, evm_new_datasize) {
+		switch (config_p->xattr_type) {
+		case EVM_TYPE_ENUM:
+		case EVM_TYPE_HMAC:
+		case EVM_TYPE_MD5:
+		case EVM_TYPE_SHA1:
+		case EVM_TYPE_STRING:
+			break;
+		default:
+			dprintk(EVM_BASE, "%s: unknown type %d\n",
+				__FUNCTION__, config_p->xattr_type);
+			error = -EINVAL;
+			break;
+		}
+
+		if (!error)
+			config_p->xattr_id = evm_config_id++;
+	}
+
+	if (evm_new_datasize > 0) {
+		struct timespec now;
+
+		evm_old_data = evm_config_data;
+		evm_old_datasize = evm_config_datasize;
+
+		evm_config_data = evm_new_data;
+		evm_config_datasize = evm_new_datasize;
+		now = CURRENT_TIME;
+		evm_config_version = now.tv_sec;
+
+		evm_isec_size = sizeof(struct evm_isec_cache) +
+		    ((evm_config_datasize + 1)
+		     * sizeof(struct evm_isec_xattr));
+	} else {
+		printk(KERN_INFO "%s: config file definition missing\n",
+		       __FUNCTION__);
+		error = -EINVAL;
+	}
+	return error;
+}
+
+static char *get_tag(char *bufStart, char *bufEnd, char delimiter,
+		     int *taglen)
+{
+	char *bufp = bufStart;
+	char *tag;
+
+	/* Get start of tag */
+	while (bufp < bufEnd) {
+		if (*bufp == ' ')	/* skip blanks */
+			while ((*bufp == ' ') && (bufp++ < bufEnd));
+		else if (*bufp == '#') {	/* skip comment */
+			while ((*bufp != '\n') && (bufp++ < bufEnd));
+			bufp++;
+		} else if (*bufp == '\n')	/* skip newline */
+			bufp++;
+		else if (*bufp == '\t')	/* skip tabs */
+			bufp++;
+		else
+			break;
+	}
+	if (bufp < bufEnd)
+		tag = bufp;
+	else
+		return NULL;
+
+	/* Get tag */
+	*taglen = 0;
+	while ((bufp < bufEnd) && (*taglen == 0)) {
+		if ((*bufp == delimiter) || (*bufp == '\n'))
+			*taglen = bufp - tag;
+		bufp++;
+	}
+	if (*taglen == 0)	/* Didn't find end delimiter */
+		tag = NULL;
+	return tag;
+}
+
+static int verify_config_parm(struct evm_xattr_config *test_p,
+			      char *parmp, char *parmVal,
+			      char **parmValEnd)
+{
+	unsigned int parmBase = 10;
+	int error = 0;
+
+	if (memcmp(parmp, "-cache", 6) == 0) {
+		test_p->xattr_refresh = simple_strtol(parmVal,
+						      parmValEnd,
+						      parmBase);
+	} else if (memcmp(parmp, "-expire", 7) == 0) {
+		test_p->xattr_expire = simple_strtol(parmVal,
+						     parmValEnd, parmBase);
+	} else if (memcmp(parmp, "-type", 5) == 0) {
+		if ((memcmp(parmVal, "enum", 3) == 0)
+		    || (memcmp(parmVal, "ENUM", 3) == 0))
+			test_p->xattr_type = EVM_TYPE_ENUM;
+		else if ((memcmp(parmVal, "md5", 3) == 0)
+			 || (memcmp(parmVal, "MD5", 3) == 0))
+			test_p->xattr_type = EVM_TYPE_MD5;
+		else if ((memcmp(parmVal, "sha1", 3) == 0)
+			 || (memcmp(parmVal, "SHA1", 3) == 0))
+			test_p->xattr_type = EVM_TYPE_SHA1;
+		else if ((memcmp(parmVal, "string", 6) == 0)
+			 || (memcmp(parmVal, "STRING", 6) == 0))
+			test_p->xattr_type = EVM_TYPE_STRING;
+		else {
+			error = -EINVAL;
+			dprintk(EVM_BASE, "%s: %s unknown type %s\n",
+				__FUNCTION__, test_p->xattr_name, parmVal);
+		}
+	}
+	return error;
+}
+
+struct evm_xattr_config *evm_parse_config(char *data,
+					  unsigned long datalen,
+					  int *datasize)
+{
+	char *datap, *dataend;
+
+	struct evm_xattr_config *evm_test_data = NULL;
+	int evm_test_datasize = 0;
+
+	char *xattrParms[] =
+	    { "xattr", "-cache", "-expire", "-type", "-log" };
+	char **parmp, **parmend;
+	char *tag, *parmVal, *parmValEnd;
+	int taglen;
+	struct evm_xattr_config *test_p = NULL;
+
+	evm_test_datasize = 0;
+
+	/* get number of extended attribute definitions */
+	datap = data;
+	dataend = data + datalen;
+
+	while ((tag = get_tag(datap, dataend, ' ', &taglen)) != NULL) {
+		datap = tag == NULL ? dataend : tag + taglen;
+		if ((strncmp(tag, *xattrParms, taglen)) == 0) {
+			evm_test_datasize++;
+		}
+	}
+	*datasize = evm_test_datasize;
+
+	/* Allocate memory for extended attributes */
+	datap = data;
+	evm_test_data = (struct evm_xattr_config *)
+	    kmalloc(evm_test_datasize * sizeof(struct evm_xattr_config),
+		    GFP_KERNEL);
+	if (!evm_test_data)
+		return NULL;
+
+	parmp = xattrParms;
+	parmend = xattrParms + sizeof xattrParms / sizeof xattrParms[0];
+
+	/* Get the extended attributes and their parameters */
+	while ((tag = get_tag(datap, dataend, ' ', &taglen)) != NULL) {
+		datap = tag == NULL ? dataend : tag + taglen;
+		if ((memcmp(tag, *xattrParms, taglen)) == 0) {
+			if (test_p == NULL)
+				test_p = evm_test_data;
+			else
+				test_p++;
+			parmVal = get_tag(datap, dataend, ' ', &taglen);
+			memset(test_p->xattr_name, 0,
+			       sizeof test_p->xattr_name);
+			memcpy(test_p->xattr_name, parmVal, taglen);
+			continue;
+		}
+		if (!test_p)
+			continue;
+
+		for (parmp = xattrParms; parmp < parmend; parmp++) {
+			if ((memcmp(tag, *parmp, taglen)) == 0) {
+				parmVal =
+				    get_tag(datap, dataend, ' ', &taglen);
+				if (!parmVal)
+					datap = dataend;
+				else if (verify_config_parm(test_p, *parmp,
+						parmVal, &parmValEnd) < 0) {
+					if (!evm_test_data) {
+						kfree(evm_test_data);
+						evm_test_data = NULL;
+					}
+					return evm_test_data;
+				} else
+					datap = parmVal + taglen;
+				break;
+			}
+		}
+	}
+
+	return evm_test_data;
+}
+
+/*
+ * Set up the hard coded EVM configuration defaults.  To modify the default
+ * configuration, update the securityfs - /sys/kernel/security/evm/config.
+ */
+int evm_init_config(void)
+{
+	int error = 0;
+
+	evm_default_datasize =
+	    sizeof(evm_default_data) / sizeof(struct evm_xattr_config);
+	if ((error = evm_update_config(evm_default_data,
+				       evm_default_datasize)) < 0) {
+		dprintk(EVM_BASE, "%s: invalid config file\n",
+			__FUNCTION__);
+	}
+	return error;
+}
+
+void evm_cleanup_config(struct evm_xattr_config *evm_data)
+{
+	if (!evm_data)
+		kfree(evm_data);
+}
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_crypto.c linux-2.6.14.2.tc/security/evm/evm_crypto.c
--- linux-2.6.14.2.orig/security/evm/evm_crypto.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_crypto.c	2005-11-11 13:44:19.000000000 -0500
@@ -0,0 +1,300 @@
+/*
+ * evm_crypto.c
+
+ * Functions which calculate a hash/hmac using the TPM's
+ * Storage Root Key (SRK).
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/security.h>
+#include <linux/mount.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+
+#include "evm.h"
+
+static struct semaphore evm_crypto_sem;
+static unsigned char tpmKey[128];
+static int tpmKeylen;
+
+#define RBUF_SIZE 1024		/* size of temporary buffer */
+
+/*
+ * Calculate the MD5/SHA1 digest on a file using nameidata
+ */
+int evm_calc_dhash(struct nameidata *nd, char *digest, int xattr_type)
+{
+	struct crypto_tfm *tfm = NULL;
+	struct scatterlist sg[1];
+	struct dentry *dentry;
+
+	char *rbuf;
+	int rbuf_len;
+	int offset;
+	struct file *file;
+
+	if (!nd)
+		return -1;
+
+	/* Load either MD5/SHA1, if not already loaded */
+	down(&evm_crypto_sem);
+	if (xattr_type == EVM_TYPE_MD5)
+		tfm = crypto_alloc_tfm("md5", 0);
+	else if (xattr_type == EVM_TYPE_SHA1)
+		tfm = crypto_alloc_tfm("sha1", 0);
+	if (!tfm) {
+		dprintk(EVM_CRYPTO,
+			"%s: failed to load MD5/SHA1 transfrom\n",
+			__FUNCTION__);
+		up(&evm_crypto_sem);
+		return -1;
+	}
+
+	dentry = nd->dentry;
+	file = dentry_open(dget(dentry), mntget(nd->mnt), O_RDONLY);
+	if (IS_ERR(file)) {
+		printk(KERN_INFO "%s: dentry_open failed\n", __FUNCTION__);
+		up(&evm_crypto_sem);
+		return -1;
+	}
+	if ((rbuf = (char *) kmalloc(RBUF_SIZE, GFP_KERNEL)) == NULL) {
+		up(&evm_crypto_sem);
+		return -ENOMEM;
+	}
+	crypto_digest_init(tfm);
+	for (offset = 0; offset < file->f_dentry->d_inode->i_size;
+	     offset += RBUF_SIZE) {
+		rbuf_len = kernel_read(file, offset, rbuf, RBUF_SIZE);
+		if (rbuf_len <= 0)
+			break;
+		sg[0].page = virt_to_page(rbuf);
+		sg[0].offset = ((long) rbuf & ~PAGE_MASK);
+		sg[0].length = rbuf_len;
+
+		crypto_digest_update(tfm, sg, 1);
+	}
+	crypto_digest_final(tfm, digest);
+	crypto_free_tfm(tfm);
+
+	fput(file);		/* clean up dentry_open() */
+	kfree(rbuf);
+	up(&evm_crypto_sem);
+	return 0;
+}
+
+EXPORT_SYMBOL_GPL(evm_calc_dhash);
+
+/*
+ * Calculate the MD5/SHA1 digest on a file
+ */
+int evm_calc_hash(struct file *file, char *digest, int xattr_type)
+{
+	struct crypto_tfm *tfm = NULL;
+	struct scatterlist sg[1];
+
+	char *rbuf;
+	int rbuf_len;
+	int offset = 0;
+
+	if (!file)
+		return -1;
+
+	/* Load either MD5/SHA1, if not already loaded */
+	down(&evm_crypto_sem);
+	if (xattr_type == EVM_TYPE_MD5)
+		tfm = crypto_alloc_tfm("md5", 0);
+	else if (xattr_type == EVM_TYPE_SHA1)
+		tfm = crypto_alloc_tfm("sha1", 0);
+
+	if (!tfm) {
+		dprintk(EVM_CRYPTO,
+			"%s: failed to load MD5/SHA1 transfrom\n",
+			__FUNCTION__);
+		up(&evm_crypto_sem);
+		return -1;
+	}
+
+	if ((rbuf = (char *) kmalloc(RBUF_SIZE, GFP_KERNEL)) == NULL) {
+		up(&evm_crypto_sem);
+		return -ENOMEM;
+	}
+	crypto_digest_init(tfm);
+	for (offset = 0; offset < file->f_dentry->d_inode->i_size;
+	     offset += RBUF_SIZE) {
+		rbuf_len = kernel_read(file, offset, rbuf, RBUF_SIZE);
+		if (rbuf_len <= 0)
+			break;
+		sg[0].page = virt_to_page(rbuf);
+		sg[0].offset = ((long) rbuf & ~PAGE_MASK);
+		sg[0].length = rbuf_len;
+
+		crypto_digest_update(tfm, sg, 1);
+	}
+	crypto_digest_final(tfm, digest);
+	crypto_free_tfm(tfm);
+
+	kfree(rbuf);
+	up(&evm_crypto_sem);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(evm_calc_hash);
+
+/*
+ * Calculate the SHA1 HMAC across all the extended attributes included
+ * in the HMAC policy as defined in /etc/evm.conf.
+ */
+int evm_calc_hmac(struct dentry *dentry, char *digest)
+{
+	ssize_t error;
+	struct crypto_tfm *tfm;
+	struct scatterlist sg[1];
+	const unsigned char *debug_name = "???";
+
+	struct evm_xattr_config *config_p, *config_data;
+	int xattrSize = 0;
+	char *xattrValue = NULL;
+	int config_datasize = 0;	/* number of extended attributes */
+	time_t config_version;
+
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->getxattr)
+		return 0;
+
+	evm_get_config_info(&config_data, &config_datasize,
+			    &config_version);
+
+	/*  Load either SHA1, if not already loaded */
+	down(&evm_crypto_sem);
+	tfm = crypto_alloc_tfm("sha1", 0);
+	if (!tfm) {
+		dprintk(EVM_CRYPTO, "%s: failed to load SHA1 transfrom\n",
+			__FUNCTION__);
+		up(&evm_crypto_sem);
+		return -1;
+	}
+	if (dentry->d_name.name)
+		debug_name = dentry->d_name.name;
+
+	crypto_hmac_init(tfm, tpmKey, &tpmKeylen);
+
+	/* Get the HMAC policy extended attribute values  */
+	for_each_xattr(config_p, config_data, config_datasize) {
+		int size;
+
+		if ((size = dentry->d_inode->i_op->getxattr(dentry,
+			config_p-> xattr_name, NULL, 0)) < 0)
+			continue;
+		if (size > xattrSize) {
+			if (xattrValue)
+				kfree(xattrValue);
+			xattrSize = size;
+			xattrValue = kmalloc(xattrSize + 1, GFP_KERNEL);
+			if (!xattrValue) {
+				up(&evm_crypto_sem);
+				return -ENOMEM;
+			}
+		}
+
+		if ((error = dentry->d_inode->i_op->getxattr(dentry,
+				     config_p-> xattr_name, xattrValue,
+				     xattrSize)) > 0) {
+			dprintk(EVM_CRYPTO, "%s: %s(%s) included\n",
+				__FUNCTION__, debug_name,
+				config_p->xattr_name);
+			sg[0].page = virt_to_page(xattrValue);
+			sg[0].offset = ((long) xattrValue & ~PAGE_MASK);
+			sg[0].length = error;
+			crypto_hmac_update(tfm, sg, 1);
+		} else {
+			if (strncmp(dentry->d_name.name, "/",
+				    dentry->d_name.len) != 0)
+				dprintk(EVM_CRYPTO,
+					"%s: %s(%s) not found\n",
+					__FUNCTION__, debug_name,
+					config_p->xattr_name);
+		}
+	};
+
+	if (xattrValue)
+		kfree(xattrValue);
+	crypto_hmac_final(tfm, tpmKey, &tpmKeylen, digest);
+	crypto_free_tfm(tfm);
+	up(&evm_crypto_sem);
+	return 0;
+}
+
+/*
+ * Calculate the hmac, update it, and mark the cache dirty.
+ */
+int evm_update_hmac(struct dentry *dentry, int flags)
+{
+	char hmacVal[SHA1_DIGEST_SIZE];
+	int rc = -1;
+
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->setxattr)
+		return rc;
+
+	memset(hmacVal, 0, SHA1_DIGEST_SIZE);
+	if ((rc = evm_calc_hmac(dentry, hmacVal)) == 0) {
+		struct evm_isec_cache *isec;
+		struct inode *inode;
+
+		rc = dentry->d_inode->i_op->setxattr(dentry,
+				"security.evm.hmac", hmacVal,
+				SHA1_DIGEST_SIZE, flags);
+
+		inode = dentry->d_inode;
+		isec = evm_get_isec(inode->i_security);
+		if (isec)
+			isec->cache_flag = EVM_CACHE_DIRTY;
+	}
+	return rc;
+}
+EXPORT_SYMBOL_GPL(evm_update_hmac);
+
+/*
+ * Convert memory to a hex string
+ */
+int mem2hex(char *mem, char *buffer, int count)
+{
+	const char hexchars[] = "0123456789abcdef";
+	int i;
+	char *buf = buffer;
+	int ch;
+
+	memset(buffer, 0, count);
+	for (i = 0; i < count; i++) {
+		ch = (int) *mem++;
+		*buf++ = hexchars[(ch >> 4) & 0xf];
+		*buf++ = hexchars[ch & 0xf];
+	}
+	return (buf - buffer);
+}
+
+EXPORT_SYMBOL_GPL(mem2hex);
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_crypto(void)
+{
+	init_MUTEX(&evm_crypto_sem);
+
+	return (tpm_get_key(tpmKey, &tpmKeylen));
+}
diff -rpuN linux-2.6.14.2.orig/security/evm/evm.h linux-2.6.14.2.tc/security/evm/evm.h
--- linux-2.6.14.2.orig/security/evm/evm.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm.h	2005-11-11 13:45:18.000000000 -0500
@@ -0,0 +1,133 @@
+/*
+ *  evm.h - extended verification module
+ *
+*/
+#include <linux/security.h>
+#include <linux/version.h>
+#include <linux/security-stack.h>
+
+#define CONFIG_SECURITY_STACKER 1
+
+#define SHA1_STR_SIZE 40 	/* String value  */
+#define SHA1_DIGEST_SIZE 20 	/* SHA1 is 160-bits */
+#define MD5_STR_SIZE 32 	/* String value  */
+#define MD5_DIGEST_SIZE 16 	/* MD5 is 128-bits */
+#define EVM_XATTR_SIZE  256	/* XATTR_SIZE_MAX causes kernel to crash */
+
+/*
+ * EVM configuration policies as defined in /etc/evm.config are
+ * stored in struct evm_config *evm_config_data.
+ */
+typedef enum {
+	EVM_TYPE_ENUM = 1, EVM_TYPE_HMAC, EVM_TYPE_MD5, EVM_TYPE_SHA1,
+		EVM_TYPE_STRING
+} evm_configtype_t;
+typedef enum {
+	EVM_VALUE_NOLABEL = '0', EVM_VALUE_FAIL = '1', EVM_VALUE_PASS = '2',
+		EVM_VALUE_EXPIRED = '3'
+} evm_configvalue_t;
+
+struct evm_xattr_config {
+	char xattr_name[XATTR_NAME_MAX + 1];
+	time_t xattr_refresh;		/* cache refresh */
+	time_t xattr_expire;		/* attribute expiration	*/
+	evm_configtype_t xattr_type;
+	int xattr_id;			/* unique extended attribute ID */
+};
+
+/*
+ * The extended attribute verification results are memory cached in
+ * inode->i_security, preceeded by a header (structure evm_isec_cache),
+ * with the following format:
+ */
+typedef enum {
+	EVM_XATTR_NOLABEL = 0, EVM_XATTR_FAIL = 1, EVM_XATTR_EXPIRED = 2,
+	EVM_XATTR_PASS = 4, EVM_XATTR_UNVALIDATED = 8
+} evm_xattrflag_t;
+
+struct evm_isec_xattr {
+	int xattr_id; 	/* xattrID to xattr evm_xattr_config->xattrID */
+	evm_xattrflag_t xattr_flags; /* enumerated: fail, pass, expired */
+	char *xattr_name; /* xattrID to xattr evm_xattr_config->xattrName */
+};
+
+typedef enum {
+	EVM_CACHE_CLEAN = 0, EVM_CACHE_DIRTY = 01, EVM_CACHE_EOPNOTSUPP = 02
+} evm_cacheflag_t;
+
+struct evm_isec_cache {
+	struct security_list lsm_list;
+	evm_cacheflag_t cache_flag; /* cache status indication: clean|dirty*/
+	time_t cache_ver;/*Using a timestamp as a configuration version number*/
+	time_t cache_refresh;/* revalidate cached info after refresh time */
+	int cache_size; /* number of extended attributes */
+	int file_mutable;
+	char hash[SHA1_DIGEST_SIZE+1];
+};
+
+extern int evm_idx;
+#define EVM_LSM_ID 0x2ca18c41
+#define evm_get_isec(head) \
+	security_get_value_type(head, EVM_LSM_ID, struct evm_isec_cache,\
+		evm_idx);
+#define evm_set_isec(head, isec) \
+	security_set_value_type(head, EVM_LSM_ID, isec, evm_idx);
+#define evm_del_isec(head) \
+	security_del_value_type(head, EVM_LSM_ID, struct evm_isec_cache, \
+		evm_idx);
+
+/* semaphore for modifying the configuration file */
+extern struct semaphore evm_config_sem;
+
+extern int evm_init_config(void);
+extern void evm_init_mode(char *evm_mode, int *enforce, int *install);
+extern void evm_enable_lsm_hooks(void);
+extern struct evm_xattr_config *evm_parse_config(char *data,
+			unsigned long datalen, int *datasize);
+extern int evm_update_config(struct evm_xattr_config *evm_new_data,
+			int evm_new_datasize);
+extern void evm_cleanup_config(struct evm_xattr_config *evm_data);
+extern int evm_get_isec_size(void);
+
+extern int evm_get_config_info(struct evm_xattr_config **head, int *size,
+			time_t *version);
+#define for_each_xattr(ptr, head, size) \
+	for (ptr = head; ptr < (head + size); ptr++)
+
+extern int evm_inode_setxattr (struct dentry *d, char *name, void *value,
+                               size_t size, int flags);
+extern void evm_inode_post_setxattr (struct dentry *d, char *name, void *value,
+                               size_t size, int flags);
+extern ssize_t evm_verify_xattr(struct dentry *d, char *kVal, char *xattrName,
+			char *xattrVal, int xattrValSize);
+extern int evm_inode_alloc_security(struct inode *inode);
+extern void evm_inode_free_security(struct inode *inode);
+extern int evm_inode_permission(struct inode *inode, int mask,
+				    struct nameidata *nd);
+
+extern int evm_file_permission(struct file *file, int mask);
+extern void evm_file_free(struct file *file);
+extern void evm_file_install_free(struct file *file);
+extern void evm_file_alloc(struct file *file);
+
+extern void evm_init_secfs(void);
+void evm_cleanup_secfs(void);
+
+extern int evm_init_crypto(void);
+extern int evm_update_hmac(struct dentry *dentry, int flags);
+extern int evm_calc_hmac(struct dentry *d, char *digest);
+extern int evm_calc_hash(struct file *file, char *digest, int xattrType);
+extern int evm_calc_dhash(struct nameidata *nd, char *digest, int xattrType);
+extern int mem2hex(char *mem, char *buffer, int count);
+extern int tpm_get_key(unsigned char key[], int *len);
+
+extern unsigned int evm_debug;
+extern unsigned int evm_install;
+enum evm_debug_level {
+	EVM_BASE = 1, EVM_CACHE = 2, EVM_XATTR = 4, EVM_CRYPTO=8
+} ;
+
+#define dprintk(level, format, a...) \
+	if (evm_debug & level) \
+		printk(KERN_INFO format, ##a)
+
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_init.c linux-2.6.14.2.tc/security/evm/evm_init.c
--- linux-2.6.14.2.orig/security/evm/evm_init.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_init.c	2005-11-11 13:45:38.000000000 -0500
@@ -0,0 +1,345 @@
+/*
+ * EVM - Extended Verification Module
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/notifier.h>
+#include <linux/security.h>
+#include <linux/version.h>
+
+#include <linux/evm.h>
+#include "evm.h"
+
+unsigned int evm_debug = EVM_BASE;
+static char *evm_mode = "normal";
+unsigned int evm_install = 0;	/* default: not install mode */
+static int evm_enforce = 1;	/* enforcement enabled */
+static int secondary = 0;
+
+/*
+ * Replacing module_use with the global sandbox_avail,
+ * since we don't get MODULE_STATE_GOING notification,
+ * however using a global creates a dependency on EVM for other modules.
+ */
+int sandbox_avail = 0;
+
+/*
+ * Rigid enforcement of signed executables,
+ *	 when a sandboxing module is not present.
+ */
+static int evm_bprm_check_security(struct linux_binprm *bprm)
+{
+	struct dentry *dentry = NULL;
+	ssize_t error = 0;
+
+	if (!bprm || !bprm->file)
+		return 0;
+	else
+		dentry = bprm->file->f_dentry;
+
+	error = -EOPNOTSUPP;
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->getxattr)
+		return 0;
+
+	/* Possible return codes:
+	 *      EVM_PERMIT, EVM_SANDBOX, EVM_DENY, EVM_NOLABEL
+	 */
+	if (!sandbox_avail) {
+		evm_status rc;
+
+		error = 0;
+		rc = evm_analyze_cacheinfo(dentry);
+		if (rc < 0) {
+			switch (rc) {
+			case (EVM_DENY):
+				error = -EACCES;
+				dprintk(EVM_BASE, "%s: %s - deny\n",
+					__FUNCTION__, bprm->filename);
+				break;
+			case (EVM_SANDBOX):
+				error = -EACCES;
+				dprintk(EVM_BASE,
+					"%s: %s - can't sandbox \n",
+					__FUNCTION__, bprm->filename);
+				break;
+			case (EVM_NOLABEL):
+				error = 0;
+				dprintk(EVM_BASE,
+					"%s: %s - no hmac label \n",
+					__FUNCTION__, bprm->filename);
+				break;
+			default:
+				dprintk(EVM_BASE,
+					"%s: %s - analyze rc=%d\n",
+					__FUNCTION__, bprm->filename, rc);
+			}
+		} else
+			dprintk(EVM_CACHE, " %s: %s - succeeded\n",
+				__FUNCTION__, bprm->filename);
+	} else {
+		dprintk(EVM_CACHE, "%s: %s sandbox_avail\n",
+			__FUNCTION__, bprm->filename);
+		error = 0;
+	}
+
+	return (evm_enforce == 1) ? error : 0;
+}
+
+/*
+ * Display the cached values (FAIL=1, EXPIRED=2, PASS=4, UNVALIDATED=8)
+ */
+static void evm_display_xattrflags(const unsigned char *name,
+				   char *xattr_name,
+				   evm_xattrflag_t xattr_flags)
+{
+	if (!name || !xattr_name)
+		return;
+
+	switch (xattr_flags) {
+	case 0:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name, "nolabel");
+		break;
+	case 1:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name, "failed");
+		break;
+	case 2:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name, "expired");
+		break;
+	case 3:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name,
+			"failed | expired ");
+		break;
+	case 4:		/* PASS */
+		break;
+	case 5:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name,
+			"passed | nolabel");
+		break;
+	case 6:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name,
+			"passed | expired");
+		break;
+	case 7:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name,
+			"passed | nolabel | " " expired");
+		break;
+	case 8:
+	case 9:
+	case 10:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name, "unvalidated");
+		break;
+	default:
+		dprintk(EVM_CACHE, "%s: '%s' %s %s\n",
+			__FUNCTION__, name, xattr_name, "weird value");
+		break;
+	}
+	return;
+}
+
+/*
+ * Simplistic analyzation of the cache results.
+ * For now, base the results on the attribute with the least cached value.
+ * (FAIL = 1, EXPIRED = 2, PASS = 4)
+ * Replace this simplistic analysis, with one based on a policy definition.
+ *
+ * Possible return codes: EVM_PERMIT, EVM_DENY, EVM_SANDBOX, EVM_NOLABEL
+ */
+evm_status evm_analyze_cacheinfo(struct dentry * dentry)
+{
+	struct inode *inode = NULL;
+	struct evm_isec_cache *isec;
+	struct evm_isec_xattr *istatus, *istatus_p;
+	evm_status error = EVM_PERMIT;
+	int xattrFlags = 0;
+
+
+	if (!dentry || !(dentry->d_inode))
+		return EVM_PERMIT;
+
+	inode = dentry->d_inode;
+	isec = evm_get_isec(inode->i_security);
+	if (!isec)
+		return EVM_PERMIT;
+
+	istatus = (void *) isec + sizeof(struct evm_isec_cache);
+	if ((istatus->xattr_id == 0) && (istatus->xattr_flags == 0))
+		return EVM_NOLABEL;
+	xattrFlags = istatus->xattr_flags;
+
+	/* Consolidate all of the extended attribute flags
+	   possible xattrFlags: FAIL=1, EXPIRED=2, PASS=4, UNVALIDATED=8 */
+	for_each_xattr(istatus_p, istatus + 1, isec->cache_size) {
+		if (istatus_p->xattr_id == 0)
+			continue;	/* Ignore missing extended attribute */
+		xattrFlags |= istatus_p->xattr_flags;
+		evm_display_xattrflags(dentry->d_name.name,
+				       istatus_p->xattr_name,
+				       istatus_p->xattr_flags);
+	}
+	if ((EVM_XATTR_FAIL == (xattrFlags & EVM_XATTR_FAIL))
+	    || (isec->cache_flag != EVM_CACHE_CLEAN))
+		error = EVM_DENY;
+	else if (EVM_XATTR_EXPIRED == (xattrFlags & EVM_XATTR_EXPIRED))
+		error = EVM_PERMIT;
+	else
+		error = EVM_PERMIT;
+	return error;
+}
+
+/*
+ * Dummy getprocattr defined here, otherwise
+ * either security_getprocattr() or dummy_getprocattr() return -EINVAL,
+ * preventing other getprocattr definitions from being executed.
+ */
+static inline int evm_getprocattr(struct task_struct *p,
+				  char *name, void *value, size_t size)
+{
+	return 0;
+}
+
+static struct security_operations evm_security_ops = {
+	.bprm_check_security = evm_bprm_check_security,
+	.inode_setxattr = evm_inode_setxattr,
+	.inode_post_setxattr = evm_inode_post_setxattr,
+	.inode_free_security = evm_inode_free_security,
+	.inode_permission = evm_inode_permission,
+	.file_permission = evm_file_permission,
+	.file_free_security = evm_file_free,
+	.getprocattr = evm_getprocattr
+};
+
+static struct security_operations evm_install_ops = {
+	.inode_setxattr = evm_inode_setxattr,
+	.inode_post_setxattr = evm_inode_post_setxattr,
+	.file_free_security = evm_file_install_free,
+	.getprocattr = evm_getprocattr
+};
+
+void evm_init_mode(char *evm_mode, int *enforce, int *install)
+{
+	if (memcmp(evm_mode, "INSTALL", 7) == 0) {
+		*install = 1;
+		*enforce = 0;
+	} else if (memcmp(evm_mode, "LOGONLY", 7) == 0)
+		*enforce = 0;
+}
+
+void evm_enable_lsm_hooks(void)
+{
+	evm_idx = 1;
+	if (register_security(evm_install
+			      ? &evm_install_ops : &evm_security_ops,
+			      &evm_idx)) {
+		if (mod_reg_security("evm", evm_install
+				     ? &evm_install_ops :
+				     &evm_security_ops, &evm_idx)) {
+			printk(KERN_INFO "%s: security hooks registration "
+			       "failed\n", __FUNCTION__);
+			return;
+		}
+		secondary = 1;
+	}
+	dprintk(EVM_BASE, "%s: registered security hooks (evm_idx = %d)\n",
+		__FUNCTION__, evm_idx);
+}
+
+static int __init init_evm(void)
+{
+	int error;
+
+	if ((error = evm_init_crypto()) < 0) {
+		dprintk(EVM_BASE, "%s: crypto initialization failed \n",
+			__FUNCTION__);
+		return error;
+	} else
+		dprintk(EVM_BASE, "%s: crypto initialized\n",
+			__FUNCTION__);
+
+	evm_init_mode(evm_mode, &evm_enforce, &evm_install);
+	if (evm_install) {
+		dprintk(EVM_BASE, "%s: running install mode\n",
+			__FUNCTION__);
+	} else if (!evm_enforce)
+		dprintk(EVM_BASE, "%s: running logonly mode\n",
+			__FUNCTION__);
+
+	if ((error = evm_init_config()) < 0) {
+		dprintk(EVM_BASE,
+			"%s: default config initialization failed\n",
+			__FUNCTION__);
+		return error;
+	} else
+		dprintk(EVM_BASE,
+			"%s: default configuration initialized\n",
+			__FUNCTION__);
+	evm_init_secfs();
+
+	return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+	struct evm_xattr_config *config_data;
+	int config_datasize = 0;	/* number of extended attributes */
+	time_t config_version;
+
+	if (secondary) {
+		if (mod_unreg_security("evm",
+				       evm_install ? &evm_install_ops :
+				       &evm_security_ops))
+			dprintk(EVM_BASE,
+				"%s: unregistering security hooks "
+				"failed \n", __FUNCTION__);
+	} else
+	    if (unregister_security
+		(evm_install ? &evm_install_ops : &evm_security_ops)) {
+		dprintk(EVM_BASE,
+			"%s: unregistering security hooks failed\n",
+			__FUNCTION__);
+	}
+	evm_cleanup_secfs();
+	evm_get_config_info(&config_data, &config_datasize,
+			    &config_version);
+	evm_cleanup_config(config_data);
+
+	dprintk(EVM_BASE, "%s: %s completed\n", __FUNCTION__,
+		evm_install ? "install mode" : "");
+}
+
+module_init(init_evm);
+module_exit(cleanup_evm);
+
+EXPORT_SYMBOL_GPL(evm_analyze_cacheinfo);
+EXPORT_SYMBOL_GPL(sandbox_avail);
+EXPORT_SYMBOL_GPL(evm_idx);
+
+module_param(evm_mode, charp, 0444);
+MODULE_PARM_DESC(evm_mode, "EVM mode: NORMAL, LOGONLY, INSTALL");
+
+module_param(evm_debug, uint, 0);
+MODULE_PARM_DESC(evm_debug, "EVM debug level");
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_secfs.c linux-2.6.14.2.tc/security/evm/evm_secfs.c
--- linux-2.6.14.2.orig/security/evm/evm_secfs.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_secfs.c	2005-11-11 13:27:04.000000000 -0500
@@ -0,0 +1,163 @@
+/*
+ * evm securityfs support for config and debug control
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <asm/uaccess.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include "evm.h"
+
+static struct dentry *evm_dir, *evm_config;
+static struct dentry *evm_debug_dir, *evm_cache, *evm_crypto, *evm_xattr;
+
+static int evm_open_debug(struct inode *inode, struct file *file)
+{
+	if (inode->u.generic_ip)
+		file->private_data = inode->u.generic_ip;
+	return 0;
+}
+
+static void evm_disable_config(void)
+{
+	securityfs_remove(evm_config);
+}
+
+static ssize_t evm_read_debug(struct file *file, char __user * buf,
+			      size_t buflen, loff_t * ppos)
+{
+	ssize_t len;
+	char *page;
+
+	page = (char *) __get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	if (strcmp(file->private_data, "cache") == 0)
+		len = sprintf(page, "evm_debug: cache: %s\n",
+			      ((evm_debug & EVM_CACHE) ==
+			       EVM_CACHE) ? "ON" : "OFF");
+	else if (strcmp(file->private_data, "xattr") == 0)
+		len = sprintf(page, "evm_debug: xattr: %s\n",
+			      ((evm_debug & EVM_XATTR) ==
+			       EVM_XATTR) ? "ON" : "OFF");
+	else if (strcmp(file->private_data, "crypto") == 0)
+		len = sprintf(page, "evm_debug: crypto: %s\n",
+			      ((evm_debug & EVM_CRYPTO) ==
+			       EVM_CRYPTO) ? "ON" : "OFF");
+	else
+		len = sprintf(page, "unknown evm_debug option\n");
+
+	len = simple_read_from_buffer(buf, buflen, ppos, page, len);
+	free_page((unsigned long) page);
+	return len;
+}
+
+static ssize_t evm_write_debug(struct file *file, const char __user * buf,
+			       size_t buflen, loff_t * ppos)
+{
+	char flag;
+
+        if (copy_from_user(&flag, buf, 1))
+                return -EFAULT;
+
+	if (strcmp(file->private_data, "cache") == 0)
+		evm_debug = (flag == '0') ? evm_debug & ~EVM_CACHE :
+		    evm_debug | EVM_CACHE;
+	else if (strcmp(file->private_data, "xattr") == 0)
+		evm_debug = (flag == '0') ? evm_debug & ~EVM_XATTR :
+		    evm_debug | EVM_XATTR;
+	else if (strcmp(file->private_data, "crypto") == 0)
+		evm_debug = (flag == '0') ? evm_debug & ~EVM_CRYPTO :
+		    evm_debug | EVM_CRYPTO;
+	return buflen;
+}
+
+static ssize_t evm_write_secfs(struct file *file, const char __user * buf,
+			       size_t buflen, loff_t * ppos)
+{
+	size_t rc = 0, datalen;
+	char *data;
+
+	struct evm_xattr_config *evm_new_data = NULL;
+	int evm_new_datasize = 0;
+
+	datalen = buflen >= 4095 ? 4095 : buflen;
+
+	if ((data = (char *) kmalloc(datalen + 1, GFP_KERNEL)) == NULL)
+		rc = -ENOMEM;
+
+	if (copy_from_user(data, buf, datalen)) {
+		kfree(data);
+		return -EFAULT;
+	}
+
+	rc = datalen;
+	*(data + datalen) = ' ';
+	evm_new_data = evm_parse_config(data, datalen, &evm_new_datasize);
+	if (!evm_new_data
+	    || ((evm_update_config(evm_new_data, evm_new_datasize)) < 0)) {
+		printk(KERN_INFO "%s: invalid config file\n",
+		       __FUNCTION__);
+		rc = -ENOMEM;
+	} else {
+		evm_disable_config();
+		evm_enable_lsm_hooks();
+	}
+
+	if (!data)
+		kfree(data);
+	return rc;
+}
+
+static struct file_operations evm_ops = {
+	.write = evm_write_secfs,
+};
+
+static struct file_operations evm_debug_ops = {
+	.read = evm_read_debug,
+	.write = evm_write_debug,
+	.open = evm_open_debug,
+};
+
+void evm_init_secfs(void)
+{
+	if ((evm_dir = securityfs_create_dir("evm", NULL)) == NULL)
+		return;
+	evm_config = securityfs_create_file("config", S_IRUSR | S_IRGRP
+				    | S_IWUSR, evm_dir, NULL, &evm_ops);
+
+	if ((evm_debug_dir =
+	     securityfs_create_dir("debug", evm_dir)) == NULL)
+		return;
+	evm_cache = securityfs_create_file("cache", S_IRUSR | S_IRGRP,
+					   evm_debug_dir, "cache",
+					   &evm_debug_ops);
+	evm_crypto =
+	    securityfs_create_file("crypto", S_IRUSR | S_IRGRP,
+				   evm_debug_dir, "crypto",
+				   &evm_debug_ops);
+	evm_xattr =
+	    securityfs_create_file("xattr", S_IRUSR | S_IRGRP,
+				   evm_debug_dir, "xattr", &evm_debug_ops);
+	return;
+}
+
+
+void evm_cleanup_secfs(void)
+{
+	securityfs_remove(evm_xattr);
+	securityfs_remove(evm_crypto);
+	securityfs_remove(evm_cache);
+	securityfs_remove(evm_debug_dir);
+	securityfs_remove(evm_dir);
+}
diff -rpuN linux-2.6.14.2.orig/security/evm/evm_xattr.c linux-2.6.14.2.tc/security/evm/evm_xattr.c
--- linux-2.6.14.2.orig/security/evm/evm_xattr.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/evm_xattr.c	2005-11-11 13:46:26.000000000 -0500
@@ -0,0 +1,236 @@
+/*
+ * evm_xattr.c
+
+ * Extended attribute functions to used compare, set, and validate
+ * syntax of an extended attribute.
+ *
+ * Copyright (C) 2005 IBM Corporation
+ * Author: Mimi Zohar <zohar@private>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/evm.h>
+#include "evm.h"
+
+static unsigned int debug_level = EVM_XATTR | EVM_BASE;
+
+/*
+ * Compare an extended attribute value with a kernel value
+ */
+ssize_t evm_verify_xattr(struct dentry *dentry, char *kVal,
+			 char *xattrName, char *xattrVal, int xattrValSize)
+{
+	ssize_t error;
+	const unsigned char *debug_name = "???";
+
+	if (!kVal || !xattrName || !xattrVal)
+		return -EINVAL;
+	if (!dentry || !dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->getxattr)
+		return -EINVAL;
+
+	memset(xattrVal, 0, xattrValSize);
+	error =
+	    dentry->d_inode->i_op->getxattr(dentry, xattrName, xattrVal,
+					    xattrValSize);
+
+	if (dentry->d_name.name)
+		debug_name = dentry->d_name.name;
+
+	if (error > 0) {
+		if (memcmp(xattrVal, kVal, error - 2) != 0) {
+			error = -EPERM;
+			dprintk(debug_level,
+				"%s: %s(%s) verification failed\n",
+				__FUNCTION__, debug_name, xattrName);
+		} else {
+			error = 0;
+			dprintk(EVM_XATTR,
+				"%s: %s(%s) verification succeeded\n",
+				__FUNCTION__, debug_name, xattrName);
+		}
+	} else
+		dprintk(EVM_XATTR,
+			"%s: %s(%s) rc = %d no extended attribute\n",
+			__FUNCTION__, debug_name, xattrName, error);
+
+	return error;
+}
+
+static int evm_verify_xattrtype(evm_configtype_t type, void *value,
+				size_t size)
+{
+	int error = 0;
+
+	switch (type) {
+	case EVM_TYPE_SHA1:
+		if (size != SHA1_STR_SIZE + sizeof(time_t))
+			error = -EINVAL;
+		break;
+	case EVM_TYPE_MD5:
+		if (size != MD5_STR_SIZE + sizeof(time_t))
+			error = -EINVAL;
+		break;
+	case EVM_TYPE_ENUM:
+		break;
+	case EVM_TYPE_STRING:
+		if (size < sizeof(time_t))
+			error = -EINVAL;
+		break;
+	case EVM_TYPE_HMAC:	/* EVM reserved type */
+		error = -EPERM;
+		break;
+	default:
+		error = -EINVAL;
+		break;
+	}
+	return error;
+}
+
+/*
+ * For a process to modify any "security.evm." extended attributes
+ * included in the "security.evm.hmac", as defined in /etc/evm.config,
+ * the process must have system admin capabilities.  The ability to
+ * modify other extended attributes included in the hmac, are assumed
+ * to be controlled by the concerned kernel module's inode_setxattr().
+ * For example, slim permits a process to write the "security.slim.level"
+ * extended attribute up to the process' integrity level.
+
+ * All extended attributes included in the "security.evm.hmac", require
+ * a timestamp.  As inode_setxattr() can't change the length of the
+ * attribute (size is not defined as a ptr), we're dependent on the user
+ * application supplying the timestamp.  We only verify that it is not
+ * the future.
+
+ * Verify whatever else we can about the value, based on the extended
+ * attribute type.
+ */
+int evm_inode_setxattr(struct dentry *dentry, char *name, void *value,
+		       size_t size, int flags)
+{
+	int error = 0;
+	struct evm_xattr_config *config_p;
+
+	struct timespec now;
+	time_t secVal;
+	const unsigned char *debug_name = "???";
+	struct evm_xattr_config *config_data;
+	int config_datasize = 0;	/* number of extended attributes */
+	time_t config_version;
+
+	if (!dentry || !name || !value)
+		return -EINVAL;
+
+	now = CURRENT_TIME;
+	if (dentry && dentry->d_name.name)
+		debug_name = dentry->d_name.name;
+	evm_get_config_info(&config_data, &config_datasize,
+			    &config_version);
+	for_each_xattr(config_p, config_data, config_datasize) {
+		if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+			secVal = ntohl(*((time_t *) value));
+			if ((!capable(CAP_SYS_ADMIN)) && (evm_install)) {
+				if ((memcmp(name, "security.", 9) != 0))
+					error = -EPERM;
+			} else if ((!capable(CAP_SYS_ADMIN))
+				   && (memcmp(name, "security.evm.", 13) ==
+				       0))
+				error = -EPERM;
+			else if (secVal > now.tv_sec + 1) {
+				error = -EINVAL;
+				dprintk(EVM_XATTR, "timestamps fail %x %x\n",
+					(unsigned int)secVal,
+					(unsigned int)now.tv_sec);
+			}
+			if (error == 0)
+				error = evm_verify_xattrtype(config_p->
+					xattr_type, value, size);
+			break;
+		}
+	}
+	dprintk(EVM_XATTR, "%s: %s %s(%d - %s)\n", __FUNCTION__,
+		debug_name, error == 0 ? "succeeded" : "failed",
+		error, name);
+	return error;
+}
+
+
+/*
+ * After updating an extended attribute defined in /etc/evm.config,
+ * calculate and save the new hmac.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, char *name,
+			     void *value, size_t size, int flags)
+{
+	struct evm_xattr_config *config_p;
+	const unsigned char *debug_name = "???";
+	struct evm_xattr_config *config_data;
+	int config_datasize = 0;	/* number of extended attributes */
+	time_t config_version;
+
+	if (!dentry || !name || !value)
+		return;
+
+	if (!dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->setxattr)
+		return;
+
+	if (dentry && dentry->d_name.name)
+		debug_name = dentry->d_name.name;
+
+	evm_get_config_info(&config_data, &config_datasize,
+			    &config_version);
+	for_each_xattr(config_p, config_data, config_datasize) {
+		if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+			int rc;
+
+			rc = evm_update_hmac(dentry, flags);
+			dprintk(EVM_XATTR, "%s: %s hmac calculation %s\n",
+				__FUNCTION__, debug_name,
+				rc == 0 ? "succeeded" : "failed");
+			break;
+		}
+	}
+}
+
+/*
+ * Write an extended attribute included in /etc/evm.conf and update
+ * the security.evm.hmac.
+ */
+int evm_setxattr(struct dentry *dentry, char *name, void *value,
+		 size_t size, int flags)
+{
+	struct evm_xattr_config *config_p;
+	int rc = -1;
+	struct evm_xattr_config *config_data;
+	int config_datasize = 0;	/* number of extended attributes */
+	time_t config_version;
+
+	if (!dentry || !value || !name)
+		return -EINVAL;
+
+	if (!dentry->d_inode || !dentry->d_inode->i_op
+	    || !dentry->d_inode->i_op->setxattr)
+		return rc;
+
+	evm_get_config_info(&config_data, &config_datasize, &config_version);
+	for_each_xattr(config_p, config_data, config_datasize) {
+		if (memcmp(name, config_p->xattr_name, strlen(name)) == 0) {
+			rc = dentry->d_inode->i_op->setxattr(dentry, name,
+				     value, size, flags);
+			if (!rc)
+				rc = evm_update_hmac(dentry, flags);
+			break;
+		}
+	}
+	return rc;
+}
diff -rpuN linux-2.6.14.2.orig/security/evm/Kconfig linux-2.6.14.2.tc/security/evm/Kconfig
--- linux-2.6.14.2.orig/security/evm/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/Kconfig	2005-11-11 13:46:45.000000000 -0500
@@ -0,0 +1,27 @@
+
+#menu "Trusted Linux Client"
+
+config SECURITY_EVM
+	tristate "EVM support"
+	depends on SECURITY && SECURITY_STACKER && TCG_TPM
+	default m
+	help
+	  The Extended Verification Module implements a mandatory access control
+	  based on an extensible set of extended attributes, as defined in
+	  /etc/evm.conf, which are HMAC protected against modification
+	  using the TPM's KERNEL ROOT KEY.  Possible extended attributes
+	  include authenticity, integrity, and revision level.
+
+	  As a password is required to release the TPM's KERNEL ROOT KEY,
+	  choose M here to compile as a module.
+
+config SECURITY_SLIM
+	tristate "SLIM support"
+	depends on SECURITY_EVM
+	help
+	  The Simple Linux Integrity Module implements a modified low water-mark
+	  mandatory access control integrity model.
+
+source security/evm/slim/ima/Kconfig
+
+#endmenu
diff -rpuN linux-2.6.14.2.orig/security/evm/Makefile linux-2.6.14.2.tc/security/evm/Makefile
--- linux-2.6.14.2.orig/security/evm/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.14.2.tc/security/evm/Makefile	2005-11-11 13:27:04.000000000 -0500
@@ -0,0 +1,7 @@
+#
+# Makefile for kernel Extended Verificaion Module (EVM)
+#
+obj-$(CONFIG_SECURITY_EVM) += evm.o
+obj-$(CONFIG_SECURITY_SLIM) += slim/
+
+evm-objs := evm_init.o evm_config.o evm_crypto.o evm_cache.o evm_xattr.o evm_secfs.o



This archive was generated by hypermail 2.1.3 : Tue Nov 15 2005 - 06:14:31 PST