Răsfoiți Sursa

Merge branch 'next-evm' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/ima-2.6 into next

Conflicts:
	fs/attr.c

Resolve conflict manually.

Signed-off-by: James Morris <jmorris@namei.org>
James Morris 14 ani în urmă
părinte
comite
5a2f3a02ae

+ 23 - 0
Documentation/ABI/testing/evm

@@ -0,0 +1,23 @@
+What:		security/evm
+Date:		March 2011
+Contact:	Mimi Zohar <zohar@us.ibm.com>
+Description:
+		EVM protects a file's security extended attributes(xattrs)
+		against integrity attacks. The initial method maintains an
+		HMAC-sha1 value across the extended attributes, storing the
+		value as the extended attribute 'security.evm'.
+
+		EVM depends on the Kernel Key Retention System to provide it
+		with a trusted/encrypted key for the HMAC-sha1 operation.
+		The key is loaded onto the root's keyring using keyctl.  Until
+		EVM receives notification that the key has been successfully
+		loaded onto the keyring (echo 1 > <securityfs>/evm), EVM
+		can not create or validate the 'security.evm' xattr, but
+		returns INTEGRITY_UNKNOWN.  Loading the key and signaling EVM
+		should be done as early as possible.  Normally this is done
+		in the initramfs, which has already been measured as part
+		of the trusted boot.  For more information on creating and
+		loading existing trusted/encrypted keys, refer to:
+		Documentation/keys-trusted-encrypted.txt.  (A sample dracut
+		patch, which loads the trusted/encrypted key and enables
+		EVM, is available from http://linux-ima.sourceforge.net/#EVM.)

+ 6 - 0
Documentation/kernel-parameters.txt

@@ -48,6 +48,7 @@ parameter is applicable:
 	EDD	BIOS Enhanced Disk Drive Services (EDD) is enabled
 	EDD	BIOS Enhanced Disk Drive Services (EDD) is enabled
 	EFI	EFI Partitioning (GPT) is enabled
 	EFI	EFI Partitioning (GPT) is enabled
 	EIDE	EIDE/ATAPI support is enabled.
 	EIDE	EIDE/ATAPI support is enabled.
+	EVM	Extended Verification Module
 	FB	The frame buffer device is enabled.
 	FB	The frame buffer device is enabled.
 	GCOV	GCOV profiling is enabled.
 	GCOV	GCOV profiling is enabled.
 	HW	Appropriate hardware is enabled.
 	HW	Appropriate hardware is enabled.
@@ -758,6 +759,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 			This option is obsoleted by the "netdev=" option, which
 			This option is obsoleted by the "netdev=" option, which
 			has equivalent usage. See its documentation for details.
 			has equivalent usage. See its documentation for details.
 
 
+	evm=		[EVM]
+			Format: { "fix" }
+			Permit 'security.evm' to be updated regardless of
+			current integrity status.
+
 	failslab=
 	failslab=
 	fail_page_alloc=
 	fail_page_alloc=
 	fail_make_request=[KNL]
 	fail_make_request=[KNL]

+ 4 - 1
fs/attr.c

@@ -13,6 +13,7 @@
 #include <linux/fsnotify.h>
 #include <linux/fsnotify.h>
 #include <linux/fcntl.h>
 #include <linux/fcntl.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 
 
 /**
 /**
  * inode_change_ok - check if attribute changes to an inode are allowed
  * inode_change_ok - check if attribute changes to an inode are allowed
@@ -237,8 +238,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
 	else
 	else
 		error = simple_setattr(dentry, attr);
 		error = simple_setattr(dentry, attr);
 
 
-	if (!error)
+	if (!error) {
 		fsnotify_change(dentry, ia_valid);
 		fsnotify_change(dentry, ia_valid);
+		evm_inode_post_setattr(dentry, ia_valid);
+	}
 
 
 	return error;
 	return error;
 }
 }

+ 25 - 25
fs/btrfs/xattr.c

@@ -374,36 +374,36 @@ int btrfs_removexattr(struct dentry *dentry, const char *name)
 				XATTR_REPLACE);
 				XATTR_REPLACE);
 }
 }
 
 
-int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
-			      struct inode *inode, struct inode *dir,
-			      const struct qstr *qstr)
+int btrfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *suffix;
+	const struct xattr *xattr;
+	struct btrfs_trans_handle *trans = fs_info;
 	char *name;
 	char *name;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-					   &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
-	}
-
-	name = kmalloc(XATTR_SECURITY_PREFIX_LEN + strlen(suffix) + 1,
-		       GFP_NOFS);
-	if (!name) {
-		err = -ENOMEM;
-	} else {
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+			       strlen(xattr->name) + 1, GFP_NOFS);
+		if (!name) {
+			err = -ENOMEM;
+			break;
+		}
 		strcpy(name, XATTR_SECURITY_PREFIX);
 		strcpy(name, XATTR_SECURITY_PREFIX);
-		strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-		err = __btrfs_setxattr(trans, inode, name, value, len, 0);
+		strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+		err = __btrfs_setxattr(trans, inode, name,
+				       xattr->value, xattr->value_len, 0);
 		kfree(name);
 		kfree(name);
+		if (err < 0)
+			break;
 	}
 	}
-
-	kfree(suffix);
-	kfree(value);
 	return err;
 	return err;
 }
 }
+
+int btrfs_xattr_security_init(struct btrfs_trans_handle *trans,
+			      struct inode *inode, struct inode *dir,
+			      const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &btrfs_initxattrs, trans);
+}

+ 18 - 16
fs/ext2/xattr_security.c

@@ -46,28 +46,30 @@ ext2_xattr_security_set(struct dentry *dentry, const char *name,
 			      value, size, flags);
 			      value, size, flags);
 }
 }
 
 
-int
-ext2_init_security(struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
+				     xattr->name, xattr->value,
+				     xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext2_xattr_set(inode, EXT2_XATTR_INDEX_SECURITY,
-			     name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext2_init_security(struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext2_initxattrs, NULL);
+}
+
 const struct xattr_handler ext2_xattr_security_handler = {
 const struct xattr_handler ext2_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext2_xattr_security_list,
 	.list	= ext2_xattr_security_list,

+ 20 - 16
fs/ext3/xattr_security.c

@@ -48,28 +48,32 @@ ext3_xattr_security_set(struct dentry *dentry, const char *name,
 			      name, value, size, flags);
 			      name, value, size, flags);
 }
 }
 
 
-int
-ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext3_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	handle_t *handle = fs_info;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext3_xattr_set_handle(handle, inode,
+					    EXT3_XATTR_INDEX_SECURITY,
+					    xattr->name, xattr->value,
+					    xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext3_xattr_set_handle(handle, inode, EXT3_XATTR_INDEX_SECURITY,
-				    name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext3_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext3_initxattrs, handle);
+}
+
 const struct xattr_handler ext3_xattr_security_handler = {
 const struct xattr_handler ext3_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext3_xattr_security_list,
 	.list	= ext3_xattr_security_list,

+ 20 - 16
fs/ext4/xattr_security.c

@@ -48,28 +48,32 @@ ext4_xattr_security_set(struct dentry *dentry, const char *name,
 			      name, value, size, flags);
 			      name, value, size, flags);
 }
 }
 
 
-int
-ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
-		   const struct qstr *qstr)
+int ext4_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	handle_t *handle = fs_info;
+	int err = 0;
 
 
-	err = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ext4_xattr_set_handle(handle, inode,
+					    EXT4_XATTR_INDEX_SECURITY,
+					    xattr->name, xattr->value,
+					    xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	err = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_SECURITY,
-				    name, value, len, 0);
-	kfree(name);
-	kfree(value);
 	return err;
 	return err;
 }
 }
 
 
+int
+ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
+		   const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &ext4_initxattrs, handle);
+}
+
 const struct xattr_handler ext4_xattr_security_handler = {
 const struct xattr_handler ext4_xattr_security_handler = {
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.prefix	= XATTR_SECURITY_PREFIX,
 	.list	= ext4_xattr_security_list,
 	.list	= ext4_xattr_security_list,

+ 18 - 20
fs/gfs2/inode.c

@@ -624,31 +624,29 @@ fail:
 	return error;
 	return error;
 }
 }
 
 
-static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
-			      const struct qstr *qstr)
+int gfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		    void *fs_info)
 {
 {
-	int err;
-	size_t len;
-	void *value;
-	char *name;
-
-	err = security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
-					   &name, &value, &len);
-
-	if (err) {
-		if (err == -EOPNOTSUPP)
-			return 0;
-		return err;
+	const struct xattr *xattr;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = __gfs2_xattr_set(inode, xattr->name, xattr->value,
+				       xattr->value_len, 0,
+				       GFS2_EATYPE_SECURITY);
+		if (err < 0)
+			break;
 	}
 	}
-
-	err = __gfs2_xattr_set(&ip->i_inode, name, value, len, 0,
-			       GFS2_EATYPE_SECURITY);
-	kfree(value);
-	kfree(name);
-
 	return err;
 	return err;
 }
 }
 
 
+static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
+			      const struct qstr *qstr)
+{
+	return security_inode_init_security(&ip->i_inode, &dip->i_inode, qstr,
+					    &gfs2_initxattrs, NULL);
+}
+
 /**
 /**
  * gfs2_create_inode - Create a new inode
  * gfs2_create_inode - Create a new inode
  * @dir: The parent directory
  * @dir: The parent directory

+ 19 - 16
fs/jffs2/security.c

@@ -22,26 +22,29 @@
 #include <linux/security.h>
 #include <linux/security.h>
 #include "nodelist.h"
 #include "nodelist.h"
 
 
-/* ---- Initial Security Label Attachment -------------- */
-int jffs2_init_security(struct inode *inode, struct inode *dir,
-			const struct qstr *qstr)
+/* ---- Initial Security Label(s) Attachment callback --- */
+int jffs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
 {
 {
-	int rc;
-	size_t len;
-	void *value;
-	char *name;
+	const struct xattr *xattr;
+	int err = 0;
 
 
-	rc = security_inode_init_security(inode, dir, qstr, &name, &value, &len);
-	if (rc) {
-		if (rc == -EOPNOTSUPP)
-			return 0;
-		return rc;
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY,
+					xattr->name, xattr->value,
+					xattr->value_len, 0);
+		if (err < 0)
+			break;
 	}
 	}
-	rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
+	return err;
+}
 
 
-	kfree(name);
-	kfree(value);
-	return rc;
+/* ---- Initial Security Label(s) Attachment ----------- */
+int jffs2_init_security(struct inode *inode, struct inode *dir,
+			const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &jffs2_initxattrs, NULL);
 }
 }
 
 
 /* ---- XATTR Handler for "security.*" ----------------- */
 /* ---- XATTR Handler for "security.*" ----------------- */

+ 28 - 29
fs/jfs/xattr.c

@@ -1089,38 +1089,37 @@ int jfs_removexattr(struct dentry *dentry, const char *name)
 }
 }
 
 
 #ifdef CONFIG_JFS_SECURITY
 #ifdef CONFIG_JFS_SECURITY
-int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
-		      const struct qstr *qstr)
+int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		   void *fs_info)
 {
 {
-	int rc;
-	size_t len;
-	void *value;
-	char *suffix;
+	const struct xattr *xattr;
+	tid_t *tid = fs_info;
 	char *name;
 	char *name;
-
-	rc = security_inode_init_security(inode, dir, qstr, &suffix, &value,
-					  &len);
-	if (rc) {
-		if (rc == -EOPNOTSUPP)
-			return 0;
-		return rc;
-	}
-	name = kmalloc(XATTR_SECURITY_PREFIX_LEN + 1 + strlen(suffix),
-		       GFP_NOFS);
-	if (!name) {
-		rc = -ENOMEM;
-		goto kmalloc_failed;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		name = kmalloc(XATTR_SECURITY_PREFIX_LEN +
+			       strlen(xattr->name) + 1, GFP_NOFS);
+		if (!name) {
+			err = -ENOMEM;
+			break;
+		}
+		strcpy(name, XATTR_SECURITY_PREFIX);
+		strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
+
+		err = __jfs_setxattr(*tid, inode, name,
+				     xattr->value, xattr->value_len, 0);
+		kfree(name);
+		if (err < 0)
+			break;
 	}
 	}
-	strcpy(name, XATTR_SECURITY_PREFIX);
-	strcpy(name + XATTR_SECURITY_PREFIX_LEN, suffix);
-
-	rc = __jfs_setxattr(tid, inode, name, value, len, 0);
-
-	kfree(name);
-kmalloc_failed:
-	kfree(suffix);
-	kfree(value);
+	return err;
+}
 
 
-	return rc;
+int jfs_init_security(tid_t tid, struct inode *inode, struct inode *dir,
+		      const struct qstr *qstr)
+{
+	return security_inode_init_security(inode, dir, qstr,
+					    &jfs_initxattrs, &tid);
 }
 }
 #endif
 #endif

+ 24 - 14
fs/ocfs2/xattr.c

@@ -7185,20 +7185,9 @@ int ocfs2_init_security_and_acl(struct inode *dir,
 {
 {
 	int ret = 0;
 	int ret = 0;
 	struct buffer_head *dir_bh = NULL;
 	struct buffer_head *dir_bh = NULL;
-	struct ocfs2_security_xattr_info si = {
-		.enable = 1,
-	};
 
 
-	ret = ocfs2_init_security_get(inode, dir, qstr, &si);
+	ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
 	if (!ret) {
 	if (!ret) {
-		ret = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
-				      si.name, si.value, si.value_len,
-				      XATTR_CREATE);
-		if (ret) {
-			mlog_errno(ret);
-			goto leave;
-		}
-	} else if (ret != -EOPNOTSUPP) {
 		mlog_errno(ret);
 		mlog_errno(ret);
 		goto leave;
 		goto leave;
 	}
 	}
@@ -7255,6 +7244,22 @@ static int ocfs2_xattr_security_set(struct dentry *dentry, const char *name,
 			       name, value, size, flags);
 			       name, value, size, flags);
 }
 }
 
 
+int ocfs2_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		     void *fs_info)
+{
+	const struct xattr *xattr;
+	int err = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		err = ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_SECURITY,
+				      xattr->name, xattr->value,
+				      xattr->value_len, XATTR_CREATE);
+		if (err)
+			break;
+	}
+	return err;
+}
+
 int ocfs2_init_security_get(struct inode *inode,
 int ocfs2_init_security_get(struct inode *inode,
 			    struct inode *dir,
 			    struct inode *dir,
 			    const struct qstr *qstr,
 			    const struct qstr *qstr,
@@ -7263,8 +7268,13 @@ int ocfs2_init_security_get(struct inode *inode,
 	/* check whether ocfs2 support feature xattr */
 	/* check whether ocfs2 support feature xattr */
 	if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
 	if (!ocfs2_supports_xattr(OCFS2_SB(dir->i_sb)))
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
-	return security_inode_init_security(inode, dir, qstr, &si->name,
-					    &si->value, &si->value_len);
+	if (si)
+		return security_old_inode_init_security(inode, dir, qstr,
+							&si->name, &si->value,
+							&si->value_len);
+
+	return security_inode_init_security(inode, dir, qstr,
+					    &ocfs2_initxattrs, NULL);
 }
 }
 
 
 int ocfs2_init_security_set(handle_t *handle,
 int ocfs2_init_security_set(handle_t *handle,

+ 2 - 2
fs/reiserfs/xattr_security.c

@@ -66,8 +66,8 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode,
 	if (IS_PRIVATE(dir))
 	if (IS_PRIVATE(dir))
 		return 0;
 		return 0;
 
 
-	error = security_inode_init_security(inode, dir, qstr, &sec->name,
-					     &sec->value, &sec->length);
+	error = security_old_inode_init_security(inode, dir, qstr, &sec->name,
+						 &sec->value, &sec->length);
 	if (error) {
 	if (error) {
 		if (error == -EOPNOTSUPP)
 		if (error == -EOPNOTSUPP)
 			error = 0;
 			error = 0;

+ 62 - 1
fs/xattr.c

@@ -14,6 +14,7 @@
 #include <linux/mount.h>
 #include <linux/mount.h>
 #include <linux/namei.h>
 #include <linux/namei.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/evm.h>
 #include <linux/syscalls.h>
 #include <linux/syscalls.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/fsnotify.h>
 #include <linux/fsnotify.h>
@@ -166,6 +167,64 @@ out_noalloc:
 }
 }
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 EXPORT_SYMBOL_GPL(xattr_getsecurity);
 
 
+/*
+ * vfs_getxattr_alloc - allocate memory, if necessary, before calling getxattr
+ *
+ * Allocate memory, if not already allocated, or re-allocate correct size,
+ * before retrieving the extended attribute.
+ *
+ * Returns the result of alloc, if failed, or the getxattr operation.
+ */
+ssize_t
+vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
+		   size_t xattr_size, gfp_t flags)
+{
+	struct inode *inode = dentry->d_inode;
+	char *value = *xattr_value;
+	int error;
+
+	error = xattr_permission(inode, name, MAY_READ);
+	if (error)
+		return error;
+
+	if (!inode->i_op->getxattr)
+		return -EOPNOTSUPP;
+
+	error = inode->i_op->getxattr(dentry, name, NULL, 0);
+	if (error < 0)
+		return error;
+
+	if (!value || (error > xattr_size)) {
+		value = krealloc(*xattr_value, error + 1, flags);
+		if (!value)
+			return -ENOMEM;
+		memset(value, 0, error + 1);
+	}
+
+	error = inode->i_op->getxattr(dentry, name, value, error);
+	*xattr_value = value;
+	return error;
+}
+
+/* Compare an extended attribute value with the given value */
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+		  const char *value, size_t size, gfp_t flags)
+{
+	char *xattr_value = NULL;
+	int rc;
+
+	rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags);
+	if (rc < 0)
+		return rc;
+
+	if ((rc != size) || (memcmp(xattr_value, value, rc) != 0))
+		rc = -EINVAL;
+	else
+		rc = 0;
+	kfree(xattr_value);
+	return rc;
+}
+
 ssize_t
 ssize_t
 vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
 vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
 {
 {
@@ -243,8 +302,10 @@ vfs_removexattr(struct dentry *dentry, const char *name)
 	error = inode->i_op->removexattr(dentry, name);
 	error = inode->i_op->removexattr(dentry, name);
 	mutex_unlock(&inode->i_mutex);
 	mutex_unlock(&inode->i_mutex);
 
 
-	if (!error)
+	if (!error) {
 		fsnotify_xattr(dentry);
 		fsnotify_xattr(dentry);
+		evm_inode_post_removexattr(dentry, name);
+	}
 	return error;
 	return error;
 }
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 EXPORT_SYMBOL_GPL(vfs_removexattr);

+ 20 - 19
fs/xfs/linux-2.6/xfs_iops.c

@@ -94,37 +94,38 @@ xfs_mark_inode_dirty(
 		mark_inode_dirty(inode);
 		mark_inode_dirty(inode);
 }
 }
 
 
+
+int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
+		   void *fs_info)
+{
+	const struct xattr *xattr;
+	struct xfs_inode *ip = XFS_I(inode);
+	int error = 0;
+
+	for (xattr = xattr_array; xattr->name != NULL; xattr++) {
+		error = xfs_attr_set(ip, xattr->name, xattr->value,
+				     xattr->value_len, ATTR_SECURE);
+		if (error < 0)
+			break;
+	}
+	return error;
+}
+
 /*
 /*
  * Hook in SELinux.  This is not quite correct yet, what we really need
  * Hook in SELinux.  This is not quite correct yet, what we really need
  * here (as we do for default ACLs) is a mechanism by which creation of
  * here (as we do for default ACLs) is a mechanism by which creation of
  * these attrs can be journalled at inode creation time (along with the
  * these attrs can be journalled at inode creation time (along with the
  * inode, of course, such that log replay can't cause these to be lost).
  * inode, of course, such that log replay can't cause these to be lost).
  */
  */
+
 STATIC int
 STATIC int
 xfs_init_security(
 xfs_init_security(
 	struct inode	*inode,
 	struct inode	*inode,
 	struct inode	*dir,
 	struct inode	*dir,
 	const struct qstr *qstr)
 	const struct qstr *qstr)
 {
 {
-	struct xfs_inode *ip = XFS_I(inode);
-	size_t		length;
-	void		*value;
-	unsigned char	*name;
-	int		error;
-
-	error = security_inode_init_security(inode, dir, qstr, (char **)&name,
-					     &value, &length);
-	if (error) {
-		if (error == -EOPNOTSUPP)
-			return 0;
-		return -error;
-	}
-
-	error = xfs_attr_set(ip, name, value, length, ATTR_SECURE);
-
-	kfree(name);
-	kfree(value);
-	return error;
+	return security_inode_init_security(inode, dir, qstr,
+					    &xfs_initxattrs, NULL);
 }
 }
 
 
 static void
 static void

+ 92 - 0
include/linux/evm.h

@@ -0,0 +1,92 @@
+/*
+ * evm.h
+ *
+ * Copyright (c) 2009 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ */
+
+#ifndef _LINUX_EVM_H
+#define _LINUX_EVM_H
+
+#include <linux/integrity.h>
+#include <linux/xattr.h>
+
+struct integrity_iint_cache;
+
+#ifdef CONFIG_EVM
+extern enum integrity_status evm_verifyxattr(struct dentry *dentry,
+					     const char *xattr_name,
+					     void *xattr_value,
+					     size_t xattr_value_len,
+					     struct integrity_iint_cache *iint);
+extern int evm_inode_setattr(struct dentry *dentry, struct iattr *attr);
+extern void evm_inode_post_setattr(struct dentry *dentry, int ia_valid);
+extern int evm_inode_setxattr(struct dentry *dentry, const char *name,
+			      const void *value, size_t size);
+extern void evm_inode_post_setxattr(struct dentry *dentry,
+				    const char *xattr_name,
+				    const void *xattr_value,
+				    size_t xattr_value_len);
+extern int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+extern void evm_inode_post_removexattr(struct dentry *dentry,
+				       const char *xattr_name);
+extern int evm_inode_init_security(struct inode *inode,
+				   const struct xattr *xattr_array,
+				   struct xattr *evm);
+#else
+#ifdef CONFIG_INTEGRITY
+static inline enum integrity_status evm_verifyxattr(struct dentry *dentry,
+						    const char *xattr_name,
+						    void *xattr_value,
+						    size_t xattr_value_len,
+					struct integrity_iint_cache *iint)
+{
+	return INTEGRITY_UNKNOWN;
+}
+#endif
+
+static int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+	return;
+}
+
+static inline int evm_inode_setxattr(struct dentry *dentry, const char *name,
+				     const void *value, size_t size)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_setxattr(struct dentry *dentry,
+					   const char *xattr_name,
+					   const void *xattr_value,
+					   size_t xattr_value_len)
+{
+	return;
+}
+
+static inline int evm_inode_removexattr(struct dentry *dentry,
+					const char *xattr_name)
+{
+	return 0;
+}
+
+static inline void evm_inode_post_removexattr(struct dentry *dentry,
+					      const char *xattr_name)
+{
+	return;
+}
+
+static inline int evm_inode_init_security(struct inode *inode,
+					  const struct xattr *xattr_array,
+					  struct xattr *evm)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_EVM_H */
+#endif /* LINUX_EVM_H */

+ 0 - 13
include/linux/ima.h

@@ -15,8 +15,6 @@ struct linux_binprm;
 
 
 #ifdef CONFIG_IMA
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
 extern int ima_bprm_check(struct linux_binprm *bprm);
-extern int ima_inode_alloc(struct inode *inode);
-extern void ima_inode_free(struct inode *inode);
 extern int ima_file_check(struct file *file, int mask);
 extern int ima_file_check(struct file *file, int mask);
 extern void ima_file_free(struct file *file);
 extern void ima_file_free(struct file *file);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
@@ -27,16 +25,6 @@ static inline int ima_bprm_check(struct linux_binprm *bprm)
 	return 0;
 	return 0;
 }
 }
 
 
-static inline int ima_inode_alloc(struct inode *inode)
-{
-	return 0;
-}
-
-static inline void ima_inode_free(struct inode *inode)
-{
-	return;
-}
-
 static inline int ima_file_check(struct file *file, int mask)
 static inline int ima_file_check(struct file *file, int mask)
 {
 {
 	return 0;
 	return 0;
@@ -51,6 +39,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
 {
 	return 0;
 	return 0;
 }
 }
-
 #endif /* CONFIG_IMA_H */
 #endif /* CONFIG_IMA_H */
 #endif /* _LINUX_IMA_H */
 #endif /* _LINUX_IMA_H */

+ 38 - 0
include/linux/integrity.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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, version 2 of the License.
+ */
+
+#ifndef _LINUX_INTEGRITY_H
+#define _LINUX_INTEGRITY_H
+
+#include <linux/fs.h>
+
+enum integrity_status {
+	INTEGRITY_PASS = 0,
+	INTEGRITY_FAIL,
+	INTEGRITY_NOLABEL,
+	INTEGRITY_UNKNOWN,
+};
+
+/* List of EVM protected security xattrs */
+#ifdef CONFIG_INTEGRITY
+extern int integrity_inode_alloc(struct inode *inode);
+extern void integrity_inode_free(struct inode *inode);
+
+#else
+static inline int integrity_inode_alloc(struct inode *inode)
+{
+	return 0;
+}
+
+static inline void integrity_inode_free(struct inode *inode)
+{
+	return;
+}
+#endif /* CONFIG_INTEGRITY_H */
+#endif /* _LINUX_INTEGRITY_H */

+ 12 - 5
include/linux/security.h

@@ -36,6 +36,7 @@
 #include <linux/key.h>
 #include <linux/key.h>
 #include <linux/xfrm.h>
 #include <linux/xfrm.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include <net/flow.h>
 #include <net/flow.h>
 
 
 /* Maximum number of letters for an LSM name string */
 /* Maximum number of letters for an LSM name string */
@@ -147,6 +148,10 @@ extern int mmap_min_addr_handler(struct ctl_table *table, int write,
 				 void __user *buffer, size_t *lenp, loff_t *ppos);
 				 void __user *buffer, size_t *lenp, loff_t *ppos);
 #endif
 #endif
 
 
+/* security_inode_init_security callback function to write xattrs */
+typedef int (*initxattrs) (struct inode *inode,
+			   const struct xattr *xattr_array, void *fs_data);
+
 #ifdef CONFIG_SECURITY
 #ifdef CONFIG_SECURITY
 
 
 struct security_mnt_opts {
 struct security_mnt_opts {
@@ -1704,8 +1709,11 @@ int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
 int security_inode_alloc(struct inode *inode);
 int security_inode_alloc(struct inode *inode);
 void security_inode_free(struct inode *inode);
 void security_inode_free(struct inode *inode);
 int security_inode_init_security(struct inode *inode, struct inode *dir,
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-				 const struct qstr *qstr, char **name,
-				 void **value, size_t *len);
+				 const struct qstr *qstr,
+				 initxattrs initxattrs, void *fs_data);
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+				     const struct qstr *qstr, char **name,
+				     void **value, size_t *len);
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
 int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 			 struct dentry *new_dentry);
 			 struct dentry *new_dentry);
@@ -2034,9 +2042,8 @@ static inline void security_inode_free(struct inode *inode)
 static inline int security_inode_init_security(struct inode *inode,
 static inline int security_inode_init_security(struct inode *inode,
 						struct inode *dir,
 						struct inode *dir,
 						const struct qstr *qstr,
 						const struct qstr *qstr,
-						char **name,
-						void **value,
-						size_t *len)
+						initxattrs initxattrs,
+						void *fs_data)
 {
 {
 	return -EOPNOTSUPP;
 	return -EOPNOTSUPP;
 }
 }

+ 13 - 1
include/linux/xattr.h

@@ -30,6 +30,9 @@
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 
 
 /* Security namespace */
 /* Security namespace */
+#define XATTR_EVM_SUFFIX "evm"
+#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
+
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
 
@@ -67,6 +70,12 @@ struct xattr_handler {
 		   size_t size, int flags, int handler_flags);
 		   size_t size, int flags, int handler_flags);
 };
 };
 
 
+struct xattr {
+	char *name;
+	void *value;
+	size_t value_len;
+};
+
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
 ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
 ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
@@ -78,7 +87,10 @@ ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer,
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
 int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
 int generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags);
 int generic_removexattr(struct dentry *dentry, const char *name);
 int generic_removexattr(struct dentry *dentry, const char *name);
-
+ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
+			   char **xattr_value, size_t size, gfp_t flags);
+int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
+		  const char *value, size_t size, gfp_t flags);
 #endif  /*  __KERNEL__  */
 #endif  /*  __KERNEL__  */
 
 
 #endif	/* _LINUX_XATTR_H */
 #endif	/* _LINUX_XATTR_H */

+ 2 - 2
mm/shmem.c

@@ -1458,7 +1458,7 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
 	inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
 	inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE);
 	if (inode) {
 	if (inode) {
 		error = security_inode_init_security(inode, dir,
 		error = security_inode_init_security(inode, dir,
-						     &dentry->d_name, NULL,
+						     &dentry->d_name,
 						     NULL, NULL);
 						     NULL, NULL);
 		if (error) {
 		if (error) {
 			if (error != -EOPNOTSUPP) {
 			if (error != -EOPNOTSUPP) {
@@ -1598,7 +1598,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 	if (!inode)
 	if (!inode)
 		return -ENOSPC;
 		return -ENOSPC;
 
 
-	error = security_inode_init_security(inode, dir, &dentry->d_name, NULL,
+	error = security_inode_init_security(inode, dir, &dentry->d_name,
 					     NULL, NULL);
 					     NULL, NULL);
 	if (error) {
 	if (error) {
 		if (error != -EOPNOTSUPP) {
 		if (error != -EOPNOTSUPP) {

+ 1 - 1
security/Kconfig

@@ -186,7 +186,7 @@ source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/apparmor/Kconfig
 
 
-source security/integrity/ima/Kconfig
+source security/integrity/Kconfig
 
 
 choice
 choice
 	prompt "Default security module"
 	prompt "Default security module"

+ 2 - 2
security/Makefile

@@ -24,5 +24,5 @@ obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/built-in.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 
 # Object integrity file lists
 # Object integrity file lists
-subdir-$(CONFIG_IMA)			+= integrity/ima
-obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
+subdir-$(CONFIG_INTEGRITY)		+= integrity
+obj-$(CONFIG_INTEGRITY)			+= integrity/built-in.o

+ 7 - 0
security/integrity/Kconfig

@@ -0,0 +1,7 @@
+#
+config INTEGRITY
+	def_bool y
+	depends on IMA || EVM
+
+source security/integrity/ima/Kconfig
+source security/integrity/evm/Kconfig

+ 12 - 0
security/integrity/Makefile

@@ -0,0 +1,12 @@
+#
+# Makefile for caching inode integrity data (iint)
+#
+
+obj-$(CONFIG_INTEGRITY) += integrity.o
+
+integrity-y := iint.o
+
+subdir-$(CONFIG_IMA)			+= ima
+obj-$(CONFIG_IMA)			+= ima/built-in.o
+subdir-$(CONFIG_EVM)			+= evm
+obj-$(CONFIG_EVM)			+= evm/built-in.o

+ 12 - 0
security/integrity/evm/Kconfig

@@ -0,0 +1,12 @@
+config EVM
+	boolean "EVM support"
+	depends on SECURITY && KEYS && ENCRYPTED_KEYS
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	default n
+	help
+	  EVM protects a file's security extended attributes against
+	  integrity attacks.
+
+	  If you are unsure how to answer this question, answer N.

+ 6 - 0
security/integrity/evm/Makefile

@@ -0,0 +1,6 @@
+#
+# Makefile for building the Extended Verification Module(EVM)
+#
+obj-$(CONFIG_EVM) += evm.o
+
+evm-y := evm_main.o evm_crypto.o evm_secfs.o

+ 38 - 0
security/integrity/evm/evm.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * 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, version 2 of the License.
+ *
+ * File: evm.h
+ *
+ */
+#include <linux/xattr.h>
+#include <linux/security.h>
+#include "../integrity.h"
+
+extern int evm_initialized;
+extern char *evm_hmac;
+
+extern struct crypto_shash *hmac_tfm;
+
+/* List of EVM protected security xattrs */
+extern char *evm_config_xattrnames[];
+
+extern int evm_init_key(void);
+extern int evm_update_evmxattr(struct dentry *dentry,
+			       const char *req_xattr_name,
+			       const char *req_xattr_value,
+			       size_t req_xattr_value_len);
+extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+			 const char *req_xattr_value,
+			 size_t req_xattr_value_len, char *digest);
+extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
+			 char *hmac_val);
+extern int evm_init_secfs(void);
+extern void evm_cleanup_secfs(void);

+ 216 - 0
security/integrity/evm/evm_crypto.c

@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * 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, version 2 of the License.
+ *
+ * File: evm_crypto.c
+ *	 Using root's kernel master key (kmk), calculate the HMAC
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <keys/encrypted-type.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+#define EVMKEY "evm-key"
+#define MAX_KEY_SIZE 128
+static unsigned char evmkey[MAX_KEY_SIZE];
+static int evmkey_len = MAX_KEY_SIZE;
+
+struct crypto_shash *hmac_tfm;
+
+static struct shash_desc *init_desc(void)
+{
+	int rc;
+	struct shash_desc *desc;
+
+	if (hmac_tfm == NULL) {
+		hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC);
+		if (IS_ERR(hmac_tfm)) {
+			pr_err("Can not allocate %s (reason: %ld)\n",
+			       evm_hmac, PTR_ERR(hmac_tfm));
+			rc = PTR_ERR(hmac_tfm);
+			hmac_tfm = NULL;
+			return ERR_PTR(rc);
+		}
+	}
+
+	desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm),
+			GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->tfm = hmac_tfm;
+	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len);
+	if (rc)
+		goto out;
+	rc = crypto_shash_init(desc);
+out:
+	if (rc) {
+		kfree(desc);
+		return ERR_PTR(rc);
+	}
+	return desc;
+}
+
+/* Protect against 'cutting & pasting' security.evm xattr, include inode
+ * specific info.
+ *
+ * (Additional directory/file metadata needs to be added for more complete
+ * protection.)
+ */
+static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
+			  char *digest)
+{
+	struct h_misc {
+		unsigned long ino;
+		__u32 generation;
+		uid_t uid;
+		gid_t gid;
+		umode_t mode;
+	} hmac_misc;
+
+	memset(&hmac_misc, 0, sizeof hmac_misc);
+	hmac_misc.ino = inode->i_ino;
+	hmac_misc.generation = inode->i_generation;
+	hmac_misc.uid = inode->i_uid;
+	hmac_misc.gid = inode->i_gid;
+	hmac_misc.mode = inode->i_mode;
+	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+	crypto_shash_final(desc, digest);
+}
+
+/*
+ * Calculate the HMAC value across the set of protected security xattrs.
+ *
+ * Instead of retrieving the requested xattr, for performance, calculate
+ * the hmac using the requested xattr value. Don't alloc/free memory for
+ * each xattr, but attempt to re-use the previously allocated memory.
+ */
+int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
+		  const char *req_xattr_value, size_t req_xattr_value_len,
+		  char *digest)
+{
+	struct inode *inode = dentry->d_inode;
+	struct shash_desc *desc;
+	char **xattrname;
+	size_t xattr_size = 0;
+	char *xattr_value = NULL;
+	int error;
+	int size;
+
+	if (!inode->i_op || !inode->i_op->getxattr)
+		return -EOPNOTSUPP;
+	desc = init_desc();
+	if (IS_ERR(desc))
+		return PTR_ERR(desc);
+
+	error = -ENODATA;
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((req_xattr_name && req_xattr_value)
+		    && !strcmp(*xattrname, req_xattr_name)) {
+			error = 0;
+			crypto_shash_update(desc, (const u8 *)req_xattr_value,
+					     req_xattr_value_len);
+			continue;
+		}
+		size = vfs_getxattr_alloc(dentry, *xattrname,
+					  &xattr_value, xattr_size, GFP_NOFS);
+		if (size == -ENOMEM) {
+			error = -ENOMEM;
+			goto out;
+		}
+		if (size < 0)
+			continue;
+
+		error = 0;
+		xattr_size = size;
+		crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
+	}
+	hmac_add_misc(desc, inode, digest);
+
+out:
+	kfree(xattr_value);
+	kfree(desc);
+	return error;
+}
+
+/*
+ * Calculate the hmac and update security.evm xattr
+ *
+ * Expects to be called with i_mutex locked.
+ */
+int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
+			const char *xattr_value, size_t xattr_value_len)
+{
+	struct inode *inode = dentry->d_inode;
+	struct evm_ima_xattr_data xattr_data;
+	int rc = 0;
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc == 0) {
+		xattr_data.type = EVM_XATTR_HMAC;
+		rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM,
+					   &xattr_data,
+					   sizeof(xattr_data), 0);
+	}
+	else if (rc == -ENODATA)
+		rc = inode->i_op->removexattr(dentry, XATTR_NAME_EVM);
+	return rc;
+}
+
+int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
+		  char *hmac_val)
+{
+	struct shash_desc *desc;
+
+	desc = init_desc();
+	if (IS_ERR(desc)) {
+		printk(KERN_INFO "init_desc failed\n");
+		return PTR_ERR(desc);
+	}
+
+	crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
+	hmac_add_misc(desc, inode, hmac_val);
+	kfree(desc);
+	return 0;
+}
+
+/*
+ * Get the key from the TPM for the SHA1-HMAC
+ */
+int evm_init_key(void)
+{
+	struct key *evm_key;
+	struct encrypted_key_payload *ekp;
+	int rc = 0;
+
+	evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
+	if (IS_ERR(evm_key))
+		return -ENOENT;
+
+	down_read(&evm_key->sem);
+	ekp = evm_key->payload.data;
+	if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
+		rc = -EINVAL;
+		goto out;
+	}
+	memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
+out:
+	/* burn the original key contents */
+	memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
+	up_read(&evm_key->sem);
+	key_put(evm_key);
+	return rc;
+}

+ 384 - 0
security/integrity/evm/evm_main.c

@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2005-2010 IBM Corporation
+ *
+ * Author:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * 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, version 2 of the License.
+ *
+ * File: evm_main.c
+ *	implements evm_inode_setxattr, evm_inode_post_setxattr,
+ *	evm_inode_removexattr, and evm_verifyxattr
+ */
+
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/xattr.h>
+#include <linux/integrity.h>
+#include <linux/evm.h>
+#include <crypto/hash.h>
+#include "evm.h"
+
+int evm_initialized;
+
+char *evm_hmac = "hmac(sha1)";
+
+char *evm_config_xattrnames[] = {
+#ifdef CONFIG_SECURITY_SELINUX
+	XATTR_NAME_SELINUX,
+#endif
+#ifdef CONFIG_SECURITY_SMACK
+	XATTR_NAME_SMACK,
+#endif
+	XATTR_NAME_CAPS,
+	NULL
+};
+
+static int evm_fixmode;
+static int __init evm_set_fixmode(char *str)
+{
+	if (strncmp(str, "fix", 3) == 0)
+		evm_fixmode = 1;
+	return 0;
+}
+__setup("evm=", evm_set_fixmode);
+
+/*
+ * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr
+ *
+ * Compute the HMAC on the dentry's protected set of extended attributes
+ * and compare it against the stored security.evm xattr.
+ *
+ * For performance:
+ * - use the previoulsy retrieved xattr value and length to calculate the
+ *   HMAC.)
+ * - cache the verification result in the iint, when available.
+ *
+ * Returns integrity status
+ */
+static enum integrity_status evm_verify_hmac(struct dentry *dentry,
+					     const char *xattr_name,
+					     char *xattr_value,
+					     size_t xattr_value_len,
+					     struct integrity_iint_cache *iint)
+{
+	struct evm_ima_xattr_data xattr_data;
+	enum integrity_status evm_status;
+	int rc;
+
+	if (iint && iint->evm_status == INTEGRITY_PASS)
+		return iint->evm_status;
+
+	/* if status is not PASS, try to check again - against -ENOMEM */
+
+	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
+			   xattr_value_len, xattr_data.digest);
+	if (rc < 0)
+		goto err_out;
+
+	xattr_data.type = EVM_XATTR_HMAC;
+	rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data,
+			   sizeof xattr_data, GFP_NOFS);
+	if (rc < 0)
+		goto err_out;
+	evm_status = INTEGRITY_PASS;
+	goto out;
+
+err_out:
+	switch (rc) {
+	case -ENODATA:		/* file not labelled */
+		evm_status = INTEGRITY_NOLABEL;
+		break;
+	default:
+		evm_status = INTEGRITY_FAIL;
+	}
+out:
+	if (iint)
+		iint->evm_status = evm_status;
+	return evm_status;
+}
+
+static int evm_protected_xattr(const char *req_xattr_name)
+{
+	char **xattrname;
+	int namelen;
+	int found = 0;
+
+	namelen = strlen(req_xattr_name);
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+		if ((strlen(*xattrname) == namelen)
+		    && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
+			found = 1;
+			break;
+		}
+		if (strncmp(req_xattr_name,
+			    *xattrname + XATTR_SECURITY_PREFIX_LEN,
+			    strlen(req_xattr_name)) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	return found;
+}
+
+/**
+ * evm_verifyxattr - verify the integrity of the requested xattr
+ * @dentry: object of the verify xattr
+ * @xattr_name: requested xattr
+ * @xattr_value: requested xattr value
+ * @xattr_value_len: requested xattr value length
+ *
+ * Calculate the HMAC for the given dentry and verify it against the stored
+ * security.evm xattr. For performance, use the xattr value and length
+ * previously retrieved to calculate the HMAC.
+ *
+ * Returns the xattr integrity status.
+ *
+ * This function requires the caller to lock the inode's i_mutex before it
+ * is executed.
+ */
+enum integrity_status evm_verifyxattr(struct dentry *dentry,
+				      const char *xattr_name,
+				      void *xattr_value, size_t xattr_value_len,
+				      struct integrity_iint_cache *iint)
+{
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return INTEGRITY_UNKNOWN;
+
+	if (!iint) {
+		iint = integrity_iint_find(dentry->d_inode);
+		if (!iint)
+			return INTEGRITY_UNKNOWN;
+	}
+	return evm_verify_hmac(dentry, xattr_name, xattr_value,
+				 xattr_value_len, iint);
+}
+EXPORT_SYMBOL_GPL(evm_verifyxattr);
+
+/*
+ * evm_protect_xattr - protect the EVM extended attribute
+ *
+ * Prevent security.evm from being modified or removed.
+ */
+static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+	}
+	return 0;
+}
+
+/*
+ * evm_verify_current_integrity - verify the dentry's metadata integrity
+ * @dentry: pointer to the affected dentry
+ *
+ * Verify and return the dentry's metadata integrity. The exceptions are
+ * before EVM is initialized or in 'fix' mode.
+ */
+static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !S_ISREG(inode->i_mode) || evm_fixmode)
+		return 0;
+	return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+}
+
+/**
+ * evm_inode_setxattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+		       const void *xattr_value, size_t xattr_value_len)
+{
+
+	enum integrity_status evm_status;
+	int ret;
+
+	ret = evm_protect_xattr(dentry, xattr_name, xattr_value,
+				xattr_value_len);
+	if (ret)
+		return ret;
+	evm_status = evm_verify_current_integrity(dentry);
+	return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_removexattr - protect the EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
+ * the current value is valid.
+ */
+int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	enum integrity_status evm_status;
+	int ret;
+
+	ret = evm_protect_xattr(dentry, xattr_name, NULL, 0);
+	if (ret)
+		return ret;
+	evm_status = evm_verify_current_integrity(dentry);
+	return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_post_setxattr - update 'security.evm' to reflect the changes
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ * @xattr_value: pointer to the new extended attribute value
+ * @xattr_value_len: pointer to the new extended attribute value length
+ *
+ * Update the HMAC stored in 'security.evm' to reflect the change.
+ *
+ * No need to take the i_mutex lock here, as this function is called from
+ * __vfs_setxattr_noperm().  The caller of which has taken the inode's
+ * i_mutex lock.
+ */
+void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
+			     const void *xattr_value, size_t xattr_value_len)
+{
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return;
+
+	evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
+	return;
+}
+
+/**
+ * evm_inode_post_removexattr - update 'security.evm' after removing the xattr
+ * @dentry: pointer to the affected dentry
+ * @xattr_name: pointer to the affected extended attribute name
+ *
+ * Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
+ */
+void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+	struct inode *inode = dentry->d_inode;
+
+	if (!evm_initialized || !evm_protected_xattr(xattr_name))
+		return;
+
+	mutex_lock(&inode->i_mutex);
+	evm_update_evmxattr(dentry, xattr_name, NULL, 0);
+	mutex_unlock(&inode->i_mutex);
+	return;
+}
+
+/**
+ * evm_inode_setattr - prevent updating an invalid EVM extended attribute
+ * @dentry: pointer to the affected dentry
+ */
+int evm_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	unsigned int ia_valid = attr->ia_valid;
+	enum integrity_status evm_status;
+
+	if (ia_valid & ~(ATTR_MODE | ATTR_UID | ATTR_GID))
+		return 0;
+	evm_status = evm_verify_current_integrity(dentry);
+	return evm_status == INTEGRITY_PASS ? 0 : -EPERM;
+}
+
+/**
+ * evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
+ *
+ * For now, update the HMAC stored in 'security.evm' to reflect UID/GID
+ * changes.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+{
+	if (!evm_initialized)
+		return;
+
+	if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
+		evm_update_evmxattr(dentry, NULL, NULL, 0);
+	return;
+}
+
+/*
+ * evm_inode_init_security - initializes security.evm
+ */
+int evm_inode_init_security(struct inode *inode,
+				 const struct xattr *lsm_xattr,
+				 struct xattr *evm_xattr)
+{
+	struct evm_ima_xattr_data *xattr_data;
+	int rc;
+
+	if (!evm_initialized || !evm_protected_xattr(lsm_xattr->name))
+		return -EOPNOTSUPP;
+
+	xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS);
+	if (!xattr_data)
+		return -ENOMEM;
+
+	xattr_data->type = EVM_XATTR_HMAC;
+	rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
+	if (rc < 0)
+		goto out;
+
+	evm_xattr->value = xattr_data;
+	evm_xattr->value_len = sizeof(*xattr_data);
+	evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS);
+	return 0;
+out:
+	kfree(xattr_data);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(evm_inode_init_security);
+
+static int __init init_evm(void)
+{
+	int error;
+
+	error = evm_init_secfs();
+	if (error < 0) {
+		printk(KERN_INFO "EVM: Error registering secfs\n");
+		goto err;
+	}
+err:
+	return error;
+}
+
+static void __exit cleanup_evm(void)
+{
+	evm_cleanup_secfs();
+	if (hmac_tfm)
+		crypto_free_shash(hmac_tfm);
+}
+
+/*
+ * evm_display_config - list the EVM protected security extended attributes
+ */
+static int __init evm_display_config(void)
+{
+	char **xattrname;
+
+	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
+		printk(KERN_INFO "EVM: %s\n", *xattrname);
+	return 0;
+}
+
+pure_initcall(evm_display_config);
+late_initcall(init_evm);
+
+MODULE_DESCRIPTION("Extended Verification Module");
+MODULE_LICENSE("GPL");

+ 108 - 0
security/integrity/evm/evm_secfs.c

@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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, version 2 of the License.
+ *
+ * File: evm_secfs.c
+ *	- Used to signal when key is on keyring
+ *	- Get the key and enable EVM
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include "evm.h"
+
+static struct dentry *evm_init_tpm;
+
+/**
+ * evm_read_key - read() for <securityfs>/evm
+ *
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t evm_read_key(struct file *filp, char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	char temp[80];
+	ssize_t rc;
+
+	if (*ppos != 0)
+		return 0;
+
+	sprintf(temp, "%d", evm_initialized);
+	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+	return rc;
+}
+
+/**
+ * evm_write_key - write() for <securityfs>/evm
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Used to signal that key is on the kernel key ring.
+ * - get the integrity hmac key from the kernel key ring
+ * - create list of hmac protected extended attributes
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t evm_write_key(struct file *file, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	char temp[80];
+	int i, error;
+
+	if (!capable(CAP_SYS_ADMIN) || evm_initialized)
+		return -EPERM;
+
+	if (count >= sizeof(temp) || count == 0)
+		return -EINVAL;
+
+	if (copy_from_user(temp, buf, count) != 0)
+		return -EFAULT;
+
+	temp[count] = '\0';
+
+	if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
+		return -EINVAL;
+
+	error = evm_init_key();
+	if (!error) {
+		evm_initialized = 1;
+		pr_info("EVM: initialized\n");
+	} else
+		pr_err("EVM: initialization failed\n");
+	return count;
+}
+
+static const struct file_operations evm_key_ops = {
+	.read		= evm_read_key,
+	.write		= evm_write_key,
+};
+
+int __init evm_init_secfs(void)
+{
+	int error = 0;
+
+	evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
+					      NULL, NULL, &evm_key_ops);
+	if (!evm_init_tpm || IS_ERR(evm_init_tpm))
+		error = -EFAULT;
+	return error;
+}
+
+void __exit evm_cleanup_secfs(void)
+{
+	if (evm_init_tpm)
+		securityfs_remove(evm_init_tpm);
+}

+ 171 - 0
security/integrity/iint.c

@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ * File: integrity_iint.c
+ *	- implements the integrity hooks: integrity_inode_alloc,
+ *	  integrity_inode_free
+ *	- cache integrity information associated with an inode
+ *	  using a rbtree tree.
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/rbtree.h>
+#include "integrity.h"
+
+static struct rb_root integrity_iint_tree = RB_ROOT;
+static DEFINE_SPINLOCK(integrity_iint_lock);
+static struct kmem_cache *iint_cache __read_mostly;
+
+int iint_initialized;
+
+/*
+ * __integrity_iint_find - return the iint associated with an inode
+ */
+static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+	struct rb_node *n = integrity_iint_tree.rb_node;
+
+	assert_spin_locked(&integrity_iint_lock);
+
+	while (n) {
+		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
+
+		if (inode < iint->inode)
+			n = n->rb_left;
+		else if (inode > iint->inode)
+			n = n->rb_right;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+
+	return iint;
+}
+
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return NULL;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	spin_unlock(&integrity_iint_lock);
+
+	return iint;
+}
+
+static void iint_free(struct integrity_iint_cache *iint)
+{
+	iint->version = 0;
+	iint->flags = 0UL;
+	kmem_cache_free(iint_cache, iint);
+}
+
+/**
+ * integrity_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ */
+int integrity_inode_alloc(struct inode *inode)
+{
+	struct rb_node **p;
+	struct rb_node *new_node, *parent = NULL;
+	struct integrity_iint_cache *new_iint, *test_iint;
+	int rc;
+
+	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
+	if (!new_iint)
+		return -ENOMEM;
+
+	new_iint->inode = inode;
+	new_node = &new_iint->rb_node;
+
+	mutex_lock(&inode->i_mutex);	/* i_flags */
+	spin_lock(&integrity_iint_lock);
+
+	p = &integrity_iint_tree.rb_node;
+	while (*p) {
+		parent = *p;
+		test_iint = rb_entry(parent, struct integrity_iint_cache,
+				     rb_node);
+		rc = -EEXIST;
+		if (inode < test_iint->inode)
+			p = &(*p)->rb_left;
+		else if (inode > test_iint->inode)
+			p = &(*p)->rb_right;
+		else
+			goto out_err;
+	}
+
+	inode->i_flags |= S_IMA;
+	rb_link_node(new_node, parent, p);
+	rb_insert_color(new_node, &integrity_iint_tree);
+
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+
+	return 0;
+out_err:
+	spin_unlock(&integrity_iint_lock);
+	mutex_unlock(&inode->i_mutex);	/* i_flags */
+	iint_free(new_iint);
+
+	return rc;
+}
+
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+	struct integrity_iint_cache *iint;
+
+	if (!IS_IMA(inode))
+		return;
+
+	spin_lock(&integrity_iint_lock);
+	iint = __integrity_iint_find(inode);
+	rb_erase(&iint->rb_node, &integrity_iint_tree);
+	spin_unlock(&integrity_iint_lock);
+
+	iint_free(iint);
+}
+
+static void init_once(void *foo)
+{
+	struct integrity_iint_cache *iint = foo;
+
+	memset(iint, 0, sizeof *iint);
+	iint->version = 0;
+	iint->flags = 0UL;
+	mutex_init(&iint->mutex);
+	iint->evm_status = INTEGRITY_UNKNOWN;
+}
+
+static int __init integrity_iintcache_init(void)
+{
+	iint_cache =
+	    kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
+			      0, SLAB_PANIC, init_once);
+	iint_initialized = 1;
+	return 0;
+}
+security_initcall(integrity_iintcache_init);

+ 1 - 0
security/integrity/ima/Kconfig

@@ -3,6 +3,7 @@
 config IMA
 config IMA
 	bool "Integrity Measurement Architecture(IMA)"
 	bool "Integrity Measurement Architecture(IMA)"
 	depends on SECURITY
 	depends on SECURITY
+	select INTEGRITY
 	select SECURITYFS
 	select SECURITYFS
 	select CRYPTO
 	select CRYPTO
 	select CRYPTO_HMAC
 	select CRYPTO_HMAC

+ 1 - 1
security/integrity/ima/Makefile

@@ -6,4 +6,4 @@
 obj-$(CONFIG_IMA) += ima.o
 obj-$(CONFIG_IMA) += ima.o
 
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-	 ima_policy.o ima_iint.o ima_audit.o
+	 ima_policy.o ima_audit.o

+ 9 - 20
security/integrity/ima/ima.h

@@ -24,11 +24,13 @@
 #include <linux/tpm.h>
 #include <linux/tpm.h>
 #include <linux/audit.h>
 #include <linux/audit.h>
 
 
+#include "../integrity.h"
+
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 
 
 /* digest size for IMA, fits SHA1 or MD5 */
 /* digest size for IMA, fits SHA1 or MD5 */
-#define IMA_DIGEST_SIZE		20
+#define IMA_DIGEST_SIZE		SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX	255
 #define IMA_EVENT_NAME_LEN_MAX	255
 
 
 #define IMA_HASH_BITS 9
 #define IMA_HASH_BITS 9
@@ -96,34 +98,21 @@ static inline unsigned long ima_hash_key(u8 *digest)
 	return hash_long(*digest, IMA_HASH_BITS);
 	return hash_long(*digest, IMA_HASH_BITS);
 }
 }
 
 
-/* iint cache flags */
-#define IMA_MEASURED		0x01
-
-/* integrity data associated with an inode */
-struct ima_iint_cache {
-	struct rb_node rb_node; /* rooted in ima_iint_tree */
-	struct inode *inode;	/* back pointer to inode in question */
-	u64 version;		/* track inode changes */
-	unsigned char flags;
-	u8 digest[IMA_DIGEST_SIZE];
-	struct mutex mutex;	/* protects: version, flags, digest */
-};
-
 /* LIM API function definitions */
 /* LIM API function definitions */
 int ima_must_measure(struct inode *inode, int mask, int function);
 int ima_must_measure(struct inode *inode, int mask, int function);
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file);
+void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
 			   const unsigned char *filename);
 			   const unsigned char *filename);
 int ima_store_template(struct ima_template_entry *entry, int violation,
 int ima_store_template(struct ima_template_entry *entry, int violation,
 		       struct inode *inode);
 		       struct inode *inode);
-void ima_template_show(struct seq_file *m, void *e,
-		       enum ima_show_type show);
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
 
 
 /* rbtree tree calls to lookup, insert, delete
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  * integrity data associated with an inode.
  */
  */
-struct ima_iint_cache *ima_iint_insert(struct inode *inode);
-struct ima_iint_cache *ima_iint_find(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
 
 /* IMA policy related functions */
 /* IMA policy related functions */
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
 enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };

+ 4 - 3
security/integrity/ima/ima_api.c

@@ -126,7 +126,8 @@ int ima_must_measure(struct inode *inode, int mask, int function)
  *
  *
  * Return 0 on success, error code otherwise
  * Return 0 on success, error code otherwise
  */
  */
-int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+int ima_collect_measurement(struct integrity_iint_cache *iint,
+			    struct file *file)
 {
 {
 	int result = -EEXIST;
 	int result = -EEXIST;
 
 
@@ -156,8 +157,8 @@ int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
  *
  *
  * Must be called with iint->mutex held.
  * Must be called with iint->mutex held.
  */
  */
-void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
-			   const unsigned char *filename)
+void ima_store_measurement(struct integrity_iint_cache *iint,
+			   struct file *file, const unsigned char *filename)
 {
 {
 	const char *op = "add_template_measure";
 	const char *op = "add_template_measure";
 	const char *audit_cause = "ENOMEM";
 	const char *audit_cause = "ENOMEM";

+ 0 - 169
security/integrity/ima/ima_iint.c

@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2008 IBM Corporation
- *
- * Authors:
- * Mimi Zohar <zohar@us.ibm.com>
- *
- * 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, version 2 of the
- * License.
- *
- * File: ima_iint.c
- * 	- implements the IMA hooks: ima_inode_alloc, ima_inode_free
- *	- cache integrity information associated with an inode
- *	  using a rbtree tree.
- */
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include "ima.h"
-
-static struct rb_root ima_iint_tree = RB_ROOT;
-static DEFINE_SPINLOCK(ima_iint_lock);
-static struct kmem_cache *iint_cache __read_mostly;
-
-int iint_initialized = 0;
-
-/*
- * __ima_iint_find - return the iint associated with an inode
- */
-static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-	struct rb_node *n = ima_iint_tree.rb_node;
-
-	assert_spin_locked(&ima_iint_lock);
-
-	while (n) {
-		iint = rb_entry(n, struct ima_iint_cache, rb_node);
-
-		if (inode < iint->inode)
-			n = n->rb_left;
-		else if (inode > iint->inode)
-			n = n->rb_right;
-		else
-			break;
-	}
-	if (!n)
-		return NULL;
-
-	return iint;
-}
-
-/*
- * ima_iint_find - return the iint associated with an inode
- */
-struct ima_iint_cache *ima_iint_find(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return NULL;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	spin_unlock(&ima_iint_lock);
-
-	return iint;
-}
-
-static void iint_free(struct ima_iint_cache *iint)
-{
-	iint->version = 0;
-	iint->flags = 0UL;
-	kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * ima_inode_alloc - allocate an iint associated with an inode
- * @inode: pointer to the inode
- */
-int ima_inode_alloc(struct inode *inode)
-{
-	struct rb_node **p;
-	struct rb_node *new_node, *parent = NULL;
-	struct ima_iint_cache *new_iint, *test_iint;
-	int rc;
-
-	new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
-	if (!new_iint)
-		return -ENOMEM;
-
-	new_iint->inode = inode;
-	new_node = &new_iint->rb_node;
-
-	mutex_lock(&inode->i_mutex); /* i_flags */
-	spin_lock(&ima_iint_lock);
-
-	p = &ima_iint_tree.rb_node;
-	while (*p) {
-		parent = *p;
-		test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
-
-		rc = -EEXIST;
-		if (inode < test_iint->inode)
-			p = &(*p)->rb_left;
-		else if (inode > test_iint->inode)
-			p = &(*p)->rb_right;
-		else
-			goto out_err;
-	}
-
-	inode->i_flags |= S_IMA;
-	rb_link_node(new_node, parent, p);
-	rb_insert_color(new_node, &ima_iint_tree);
-
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-
-	return 0;
-out_err:
-	spin_unlock(&ima_iint_lock);
-	mutex_unlock(&inode->i_mutex); /* i_flags */
-	iint_free(new_iint);
-
-	return rc;
-}
-
-/**
- * ima_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void ima_inode_free(struct inode *inode)
-{
-	struct ima_iint_cache *iint;
-
-	if (!IS_IMA(inode))
-		return;
-
-	spin_lock(&ima_iint_lock);
-	iint = __ima_iint_find(inode);
-	rb_erase(&iint->rb_node, &ima_iint_tree);
-	spin_unlock(&ima_iint_lock);
-
-	iint_free(iint);
-}
-
-static void init_once(void *foo)
-{
-	struct ima_iint_cache *iint = foo;
-
-	memset(iint, 0, sizeof *iint);
-	iint->version = 0;
-	iint->flags = 0UL;
-	mutex_init(&iint->mutex);
-}
-
-static int __init ima_iintcache_init(void)
-{
-	iint_cache =
-	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
-			      SLAB_PANIC, init_once);
-	iint_initialized = 1;
-	return 0;
-}
-security_initcall(ima_iintcache_init);

+ 6 - 6
security/integrity/ima/ima_main.c

@@ -82,7 +82,7 @@ out:
 				  "open_writers");
 				  "open_writers");
 }
 }
 
 
-static void ima_check_last_writer(struct ima_iint_cache *iint,
+static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode,
 				  struct inode *inode,
 				  struct file *file)
 				  struct file *file)
 {
 {
@@ -105,12 +105,12 @@ static void ima_check_last_writer(struct ima_iint_cache *iint,
 void ima_file_free(struct file *file)
 void ima_file_free(struct file *file)
 {
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 
 
 	if (!iint_initialized || !S_ISREG(inode->i_mode))
 	if (!iint_initialized || !S_ISREG(inode->i_mode))
 		return;
 		return;
 
 
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint)
 	if (!iint)
 		return;
 		return;
 
 
@@ -121,7 +121,7 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 			       int mask, int function)
 			       int mask, int function)
 {
 {
 	struct inode *inode = file->f_dentry->d_inode;
 	struct inode *inode = file->f_dentry->d_inode;
-	struct ima_iint_cache *iint;
+	struct integrity_iint_cache *iint;
 	int rc = 0;
 	int rc = 0;
 
 
 	if (!ima_initialized || !S_ISREG(inode->i_mode))
 	if (!ima_initialized || !S_ISREG(inode->i_mode))
@@ -131,9 +131,9 @@ static int process_measurement(struct file *file, const unsigned char *filename,
 	if (rc != 0)
 	if (rc != 0)
 		return rc;
 		return rc;
 retry:
 retry:
-	iint = ima_iint_find(inode);
+	iint = integrity_iint_find(inode);
 	if (!iint) {
 	if (!iint) {
-		rc = ima_inode_alloc(inode);
+		rc = integrity_inode_alloc(inode);
 		if (!rc || rc == -EEXIST)
 		if (!rc || rc == -EEXIST)
 			goto retry;
 			goto retry;
 		return rc;
 		return rc;

+ 47 - 0
security/integrity/integrity.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009-2010 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * 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, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/integrity.h>
+#include <crypto/sha.h>
+
+/* iint cache flags */
+#define IMA_MEASURED		0x01
+
+enum evm_ima_xattr_type {
+	IMA_XATTR_DIGEST = 0x01,
+	EVM_XATTR_HMAC,
+	EVM_IMA_XATTR_DIGSIG,
+};
+
+struct evm_ima_xattr_data {
+	u8 type;
+	u8 digest[SHA1_DIGEST_SIZE];
+}  __attribute__((packed));
+
+/* integrity data associated with an inode */
+struct integrity_iint_cache {
+	struct rb_node rb_node; /* rooted in integrity_iint_tree */
+	struct inode *inode;	/* back pointer to inode in question */
+	u64 version;		/* track inode changes */
+	unsigned char flags;
+	u8 digest[SHA1_DIGEST_SIZE];
+	struct mutex mutex;	/* protects: version, flags, digest */
+	enum integrity_status evm_status;
+};
+
+/* rbtree tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode);

+ 64 - 7
security/security.c

@@ -16,7 +16,11 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/security.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/ima.h>
 #include <linux/ima.h>
+#include <linux/evm.h>
+
+#define MAX_LSM_EVM_XATTR	2
 
 
 /* Boot-time LSM user choice */
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
@@ -334,20 +338,57 @@ int security_inode_alloc(struct inode *inode)
 
 
 void security_inode_free(struct inode *inode)
 void security_inode_free(struct inode *inode)
 {
 {
-	ima_inode_free(inode);
+	integrity_inode_free(inode);
 	security_ops->inode_free_security(inode);
 	security_ops->inode_free_security(inode);
 }
 }
 
 
 int security_inode_init_security(struct inode *inode, struct inode *dir,
 int security_inode_init_security(struct inode *inode, struct inode *dir,
-				 const struct qstr *qstr, char **name,
-				 void **value, size_t *len)
+				 const struct qstr *qstr,
+				 const initxattrs initxattrs, void *fs_data)
+{
+	struct xattr new_xattrs[MAX_LSM_EVM_XATTR + 1];
+	struct xattr *lsm_xattr, *evm_xattr, *xattr;
+	int ret;
+
+	if (unlikely(IS_PRIVATE(inode)))
+		return -EOPNOTSUPP;
+
+	memset(new_xattrs, 0, sizeof new_xattrs);
+	if (!initxattrs)
+		return security_ops->inode_init_security(inode, dir, qstr,
+							 NULL, NULL, NULL);
+	lsm_xattr = new_xattrs;
+	ret = security_ops->inode_init_security(inode, dir, qstr,
+						&lsm_xattr->name,
+						&lsm_xattr->value,
+						&lsm_xattr->value_len);
+	if (ret)
+		goto out;
+
+	evm_xattr = lsm_xattr + 1;
+	ret = evm_inode_init_security(inode, lsm_xattr, evm_xattr);
+	if (ret)
+		goto out;
+	ret = initxattrs(inode, new_xattrs, fs_data);
+out:
+	for (xattr = new_xattrs; xattr->name != NULL; xattr++) {
+		kfree(xattr->name);
+		kfree(xattr->value);
+	}
+	return (ret == -EOPNOTSUPP) ? 0 : ret;
+}
+EXPORT_SYMBOL(security_inode_init_security);
+
+int security_old_inode_init_security(struct inode *inode, struct inode *dir,
+				     const struct qstr *qstr, char **name,
+				     void **value, size_t *len)
 {
 {
 	if (unlikely(IS_PRIVATE(inode)))
 	if (unlikely(IS_PRIVATE(inode)))
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 	return security_ops->inode_init_security(inode, dir, qstr, name, value,
 	return security_ops->inode_init_security(inode, dir, qstr, name, value,
 						 len);
 						 len);
 }
 }
-EXPORT_SYMBOL(security_inode_init_security);
+EXPORT_SYMBOL(security_old_inode_init_security);
 
 
 #ifdef CONFIG_SECURITY_PATH
 #ifdef CONFIG_SECURITY_PATH
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
 int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
@@ -523,9 +564,14 @@ int security_inode_permission(struct inode *inode, int mask)
 
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_setattr(dentry, attr);
+	ret = security_ops->inode_setattr(dentry, attr);
+	if (ret)
+		return ret;
+	return evm_inode_setattr(dentry, attr);
 }
 }
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 EXPORT_SYMBOL_GPL(security_inode_setattr);
 
 
@@ -539,9 +585,14 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
 int security_inode_setxattr(struct dentry *dentry, const char *name,
 int security_inode_setxattr(struct dentry *dentry, const char *name,
 			    const void *value, size_t size, int flags)
 			    const void *value, size_t size, int flags)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_setxattr(dentry, name, value, size, flags);
+	ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+	if (ret)
+		return ret;
+	return evm_inode_setxattr(dentry, name, value, size);
 }
 }
 
 
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
@@ -550,6 +601,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return;
 		return;
 	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
 	security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+	evm_inode_post_setxattr(dentry, name, value, size);
 }
 }
 
 
 int security_inode_getxattr(struct dentry *dentry, const char *name)
 int security_inode_getxattr(struct dentry *dentry, const char *name)
@@ -568,9 +620,14 @@ int security_inode_listxattr(struct dentry *dentry)
 
 
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 int security_inode_removexattr(struct dentry *dentry, const char *name)
 {
 {
+	int ret;
+
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 	if (unlikely(IS_PRIVATE(dentry->d_inode)))
 		return 0;
 		return 0;
-	return security_ops->inode_removexattr(dentry, name);
+	ret = security_ops->inode_removexattr(dentry, name);
+	if (ret)
+		return ret;
+	return evm_inode_removexattr(dentry, name);
 }
 }
 
 
 int security_inode_need_killpriv(struct dentry *dentry)
 int security_inode_need_killpriv(struct dentry *dentry)