All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] integrity
@ 2009-01-29 22:23 Mimi Zohar
  2009-01-29 22:23 ` [PATCH 1/6] integrity: IMA hooks Mimi Zohar
                   ` (6 more replies)
  0 siblings, 7 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn

This patchset contains the IMA integrity patches. Based on
comments on the mailing list, both the LIM ops registration,
which provides support for multiple integrity modules, and
template ops registration, which provides template support,
have been removed. As part of this change, the integrity 
hooks have been renamed to ima.

Other changes made since the last integrity posting, are based
on comments made by: Christoph Hellwig, Dave Hansen, Paul McKenney, 
Serge Hallyn, Matt Helsley, and Sukadev Bhattiprolu. Listed here  
are major changes. Additional changes are listed in the patch 
descriptions.
- added slab for integrity information(iint) associated with an inode
- added a local read and write count in iint
- lots of code refactoring 
- addressed locking, in general, and rcu-locking issues
- removed caching of measurement policy results
- cleanup of the policy parsing

New to the integrity patchset, based on Serge Hallyn and Dave
Hansen's suggestion, is a method to detect an imbalance between
the number of calls to ima_path_check() and ima_file_free(),
which could indicate that a file was accessed without first being
measured.

The integrity-tpm-internal-interface.patch will be posted separately
by Rajiv Andrade.

Mimi Zohar (6):
  integrity: IMA hooks
  integrity: IMA as an integrity service provider
  integrity: IMA display
  integrity: IMA policy
  integrity: IMA policy open
  Integrity: IMA file free imbalance

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/6] integrity: IMA hooks
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

This patch replaces the generic integrity hooks, for which IMA registered
itself, with IMA integrity hooks in the appropriate places directly
in the fs directory.

- renamed integrity hooks to ima
- additional ima_path_check() in open_exec() and __link_path_walk()
  required for maintaining readcount
- removed Kconfig additions/changes

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index d8362cf..40c51e9 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -44,6 +44,7 @@ parameter is applicable:
 	FB	The frame buffer device is enabled.
 	HW	Appropriate hardware is enabled.
 	IA-64	IA-64 architecture is enabled.
+	IMA     Integrity measurement architecture is enabled.
 	IOSCHED	More than one I/O scheduler is enabled.
 	IP_PNP	IP DHCP, BOOTP, or RARP is enabled.
 	ISAPNP	ISA PnP code is enabled.
diff --git a/fs/exec.c b/fs/exec.c
index 0dd60a0..febfd8e 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mount.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
@@ -127,6 +128,9 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
 				 MAY_READ | MAY_EXEC | MAY_OPEN);
 	if (error)
 		goto exit;
+	error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+	if (error)
+		goto exit;
 
 	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
 	error = PTR_ERR(file);
@@ -674,6 +678,9 @@ struct file *open_exec(const char *name)
 	err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
 	if (err)
 		goto out_path_put;
+	err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN);
+	if (err)
+		goto out_path_put;
 
 	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
 	if (IS_ERR(file))
@@ -1168,6 +1175,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
 	retval = security_bprm_check(bprm);
 	if (retval)
 		return retval;
+	retval = ima_bprm_check(bprm);
+	if (retval)
+		return retval;
 
 	/* kernel module loader fixup */
 	/* so we don't try to load run modprobe in kernel space. */
diff --git a/fs/file_table.c b/fs/file_table.c
index bbeeac6..da806ac 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/eventpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/mount.h>
@@ -279,6 +280,7 @@ void __fput(struct file *file)
 	if (file->f_op && file->f_op->release)
 		file->f_op->release(inode, file);
 	security_file_free(file);
+	ima_file_free(file);
 	if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
 		cdev_put(inode->i_cdev);
 	fops_put(file->f_op);
diff --git a/fs/inode.c b/fs/inode.c
index 913ab2d..40e37c0 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -17,6 +17,7 @@
 #include <linux/hash.h>
 #include <linux/swap.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
@@ -147,13 +148,13 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_cdev = NULL;
 	inode->i_rdev = 0;
 	inode->dirtied_when = 0;
-	if (security_inode_alloc(inode)) {
-		if (inode->i_sb->s_op->destroy_inode)
-			inode->i_sb->s_op->destroy_inode(inode);
-		else
-			kmem_cache_free(inode_cachep, (inode));
-		return NULL;
-	}
+
+	if (security_inode_alloc(inode))
+		goto out_free_inode;
+
+	/* allocate and initialize an i_integrity */
+	if (ima_inode_alloc(inode))
+		goto out_free_security;
 
 	spin_lock_init(&inode->i_lock);
 	lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
@@ -189,6 +190,15 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_mapping = mapping;
 
 	return inode;
+
+out_free_security:
+	security_inode_free(inode);
+out_free_inode:
+	if (inode->i_sb->s_op->destroy_inode)
+		inode->i_sb->s_op->destroy_inode(inode);
+	else
+		kmem_cache_free(inode_cachep, (inode));
+	return NULL;
 }
 EXPORT_SYMBOL(inode_init_always);
 
diff --git a/fs/namei.c b/fs/namei.c
index bbc15c2..f9bfc8d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
@@ -850,6 +851,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 		if (err == -EAGAIN)
 			err = inode_permission(nd->path.dentry->d_inode,
 					       MAY_EXEC);
+ 		if (!err)
+			err = ima_path_check(&nd->path, MAY_EXEC);
  		if (err)
 			break;
 
@@ -1509,6 +1512,11 @@ int may_open(struct path *path, int acc_mode, int flag)
 	error = inode_permission(inode, acc_mode);
 	if (error)
 		return error;
+
+	error = ima_path_check(path,
+			       acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+	if (error)
+		return error;
 	/*
 	 * An append-only file must be opened in append mode for writing.
 	 */
diff --git a/include/linux/ima.h b/include/linux/ima.h
new file mode 100644
index 0000000..4ed1e4d
--- /dev/null
+++ b/include/linux/ima.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include <linux/fs.h>
+
+#ifndef _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+static inline int ima_bprm_check(struct linux_binprm *bprm)
+{
+	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_path_check(struct path *path, int mask)
+{
+	return 0;
+}
+
+static inline void ima_file_free(struct file *file)
+{
+	return;
+}
+
+static inline int ima_file_mmap(struct file *file, unsigned long prot)
+{
+	return 0;
+}
+#endif /* _LINUX_IMA_H */
diff --git a/mm/mmap.c b/mm/mmap.c
index 8d95902..d36a587 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/hugetlb.h>
 #include <linux/profile.h>
 #include <linux/module.h>
@@ -1049,6 +1050,9 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 	error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
 	if (error)
 		return error;
+	error = ima_file_mmap(file, prot);
+	if (error)
+		return error;
 
 	return mmap_region(file, addr, len, flags, vm_flags, pgoff,
 			   accountable);
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
  2009-01-29 22:23 ` [PATCH 1/6] integrity: IMA hooks Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-01-30  0:07   ` James Morris
                     ` (2 more replies)
  2009-01-29 22:23 ` [PATCH 3/6] integrity: IMA display Mimi Zohar
                   ` (4 subsequent siblings)
  6 siblings, 3 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

IMA provides hardware (TPM) based measurement and attestation for
file measurements. As the Trusted Computing (TPM) model requires,
IMA measures all files before they are accessed in any way (on the
integrity_bprm_check, integrity_path_check and integrity_file_mmap
hooks), and commits the measurements to the TPM. Once added to the
TPM, measurements can not be removed.

In addition, IMA maintains a list of these file measurements, which
can be used to validate the aggregate value stored in the TPM.  The
TPM can sign these measurements, and thus the system can prove, to
itself and to a third party, the system's integrity in a way that
cannot be circumvented by malicious or compromised software.

- removed LIM hooks and API registration; IMA is now called directly
- added slab for integrity information(iint) associated with an inode
- added a local read and write count in iint
- lots of code refactoring (i.e. calculating the boot aggregate,
  flagging openwriters/ToMToU)
- statically defined and initialized variables
- addressed the credential merge changes
- addressed locking issues in general (i.e. ima_file_free())
  and addressed the rcu-locking problem in ima_iint_delete() in particular
- removed caching of measurement policy results
- removed Kconfig prompt for: pcr index, informational audit messages

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
---
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 40c51e9..8cc40a1 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -901,6 +901,15 @@ and is between 256 and 4096 characters. It is defined in the file
 	ihash_entries=	[KNL]
 			Set number of hash buckets for inode cache.
 
+	ima_audit=	[IMA]
+			Format: { "0" | "1" }
+			0 -- integrity auditing messages. (Default)
+			1 -- enable informational integrity auditing messages.
+
+	ima_hash=	[IMA]
+			Formt: { "sha1" | "md5" }
+			default: "sha1"
+
 	in2000=		[HW,SCSI]
 			See header of drivers/scsi/in2000.c.
 
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 67e5dbf..930939a 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -125,6 +125,11 @@
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
 
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4ed1e4d..dcc3664 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -12,6 +12,15 @@
 #ifndef _LINUX_IMA_H
 #define _LINUX_IMA_H
 
+#ifdef CONFIG_IMA
+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_path_check(struct path *path, int mask);
+extern void ima_file_free(struct file *file);
+extern int ima_file_mmap(struct file *file, unsigned long prot);
+
+#else
 static inline int ima_bprm_check(struct linux_binprm *bprm)
 {
 	return 0;
@@ -41,4 +50,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
 	return 0;
 }
+#endif /* CONFIG_IMA_H */
 #endif /* _LINUX_IMA_H */
diff --git a/security/Kconfig b/security/Kconfig
index 9438535..bf129f8 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -55,7 +55,8 @@ config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	help
 	  This will build the securityfs filesystem.  It is currently used by
-	  the TPM bios character driver.  It is not used by SELinux or SMACK.
+	  the TPM bios character driver and IMA, an integrity provider.  It is
+	  not used by SELinux or SMACK.
 
 	  If you are unsure how to answer this question, answer N.
 
@@ -135,5 +136,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
 source security/selinux/Kconfig
 source security/smack/Kconfig
 
+source security/integrity/ima/Kconfig
+
 endmenu
 
diff --git a/security/Makefile b/security/Makefile
index c05c127..595536c 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
+
+# Object integrity file lists
+subdir-$(CONFIG_IMA)			+= integrity/ima
+obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
new file mode 100644
index 0000000..2a761c8
--- /dev/null
+++ b/security/integrity/ima/Kconfig
@@ -0,0 +1,49 @@
+# IBM Integrity Measurement Architecture
+#
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on ACPI
+	select SECURITYFS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select TCG_TPM
+	select TCG_TIS
+	help
+	  The Trusted Computing Group(TCG) runtime Integrity
+	  Measurement Architecture(IMA) maintains a list of hash
+	  values of executables and other sensitive system files,
+	  as they are read or executed. If an attacker manages
+	  to change the contents of an important system file
+	  being measured, we can tell.
+
+	  If your system has a TPM chip, then IMA also maintains
+	  an aggregate integrity value over this list inside the
+	  TPM hardware, so that the TPM can prove to a third party
+	  whether or not critical system files have been modified.
+	  Read <http://www.usenix.org/events/sec04/tech/sailer.html>
+	  to learn more about IMA.
+	  If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+	int
+	depends on IMA
+	range 8 14
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.
+
+config IMA_AUDIT
+	bool
+	depends on IMA
+	default y
+	help
+	  This option adds a kernel parameter 'ima_audit', which
+	  allows informational auditing messages to be enabled
+	  at boot.  If this option is selected, informational integrity
+	  auditing messages can be enabled with 'ima_audit=1' on
+	  the kernel command line.
+
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
new file mode 100644
index 0000000..9d6bf97
--- /dev/null
+++ b/security/integrity/ima/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+obj-$(CONFIG_IMA) += ima.o
+
+ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+	 ima_policy.o ima_iint.o ima_audit.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
new file mode 100644
index 0000000..04a397e
--- /dev/null
+++ b/security/integrity/ima/ima.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * 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.h
+ *	internal Integrity Measurement Architecture (IMA) definitions
+ */
+
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+#include <linux/audit.h>
+
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
+
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE		20
+#define IMA_EVENT_NAME_LEN_MAX	255
+
+#define IMA_HASH_BITS 9
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+/* set during initialization */
+extern int ima_initialized;
+extern int ima_used_chip;
+extern char *ima_hash;
+
+/* IMA inode template definition */
+struct ima_template_data {
+	u8 digest[IMA_DIGEST_SIZE];	/* sha1/md5 measurement hash */
+	char file_name[IMA_EVENT_NAME_LEN_MAX + 1];	/* name + \0 */
+};
+
+#define IMA_TEMPLATE_NAME_LEN_MAX 20
+struct ima_template_entry {
+	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */
+	char *template_name;
+	int template_len;
+	struct ima_template_data template;
+};
+
+struct ima_queue_entry {
+	struct hlist_node hnext;	/* place in hash collision list */
+	struct list_head later;		/* place in ima_measurements list */
+	struct ima_template_entry *entry;
+};
+extern struct list_head ima_measurements;	/* list of all measurements */
+
+/* declarations */
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+			 const unsigned char *fname, const char *op,
+			 const char *cause, int result, int info);
+
+/* Internal IMA function definitions */
+void ima_iintcache_init(void);
+int ima_init(void);
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+			   const char *op, struct inode *inode);
+int ima_calc_hash(struct file *file, char *digest);
+int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_boot_aggregate(char *digest);
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       const char *op, const char *cause);
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct ima_h_table {
+	atomic_long_t len;	/* number of stored measurements in the list */
+	atomic_long_t violations;
+	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long ima_hash_key(u8 *digest)
+{
+	return hash_long(*digest, IMA_HASH_BITS);
+}
+
+/* iint cache flags */
+#define IMA_MEASURED		1
+
+/* integrity data associated with an inode */
+struct ima_iint_cache {
+	u64 version;		/* track inode changes */
+	unsigned long flags;
+	u8 digest[IMA_DIGEST_SIZE];
+	struct mutex mutex;	/* protects: version, flags, digest */
+	long readcount;		/* measured files readcount */
+	long writecount;	/* measured files writecount */
+	struct kref refcount;	/* ima_iint_cache reference count */
+	struct rcu_head rcu;
+};
+
+/* LIM API function definitions */
+int ima_must_measure(struct ima_iint_cache *iint, 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,
+			   const unsigned char *filename);
+int ima_store_template(struct ima_template_data *data, int violation,
+		       struct inode *inode);
+
+/* radix tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
+void ima_iint_delete(struct inode *inode);
+void iint_free(struct kref *kref);
+void iint_rcu_free(struct rcu_head *rcu);
+
+/* IMA policy related functions */
+enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+void ima_init_policy(void);
+void ima_update_policy(void);
+#endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
new file mode 100644
index 0000000..7c1db11
--- /dev/null
+++ b/security/integrity/ima/ima_api.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * File: ima_api.c
+ *	Implements must_measure, collect_measurement, store_measurement,
+ *	and store_template.
+ */
+#include <linux/module.h>
+
+#include "ima.h"
+static char *IMA_TEMPLATE_NAME = "ima";
+
+/*
+ * ima_store_template - store ima template measurements
+ *
+ * Calculate the hash of a template entry, add the template entry
+ * to an ordered list of measurement entries maintained inside the kernel,
+ * and also update the aggregate integrity value (maintained inside the
+ * configured TPM PCR) over the hashes of the current list of measurement
+ * entries.
+ *
+ * Applications retrieve the current kernel-held measurement list through
+ * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
+ * TPM PCR (called quote) can be retrieved using a TPM user space library
+ * and is used to validate the measurement list.
+ *
+ * Returns 0 on success, error code otherwise
+ */
+int ima_store_template(struct ima_template_data *template,
+		       int violation, struct inode *inode)
+{
+	struct ima_template_entry *entry;
+	const char *op = "add_template_measure";
+	const char *audit_cause = "ENOMEM";
+	int result = -ENOMEM;
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		goto err_out;
+
+	memcpy(&entry->template, template, sizeof(*template));
+	memset(&entry->digest, 0, sizeof(entry->digest));
+	entry->template_name = IMA_TEMPLATE_NAME;
+	entry->template_len = sizeof(*template);
+
+	if (!violation) {
+		result = ima_calc_template_hash(entry->template_len,
+						template, entry->digest);
+		if (result < 0) {
+			kfree(entry);
+			audit_cause = "hashing_error";
+			goto err_out;
+		}
+	}
+	result = ima_add_template_entry(entry, violation, op, inode);
+	if (result < 0)
+		kfree(entry);
+	return result;
+
+err_out:
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+			    op, audit_cause, result, 0);
+
+	return result;
+}
+
+/*
+ * ima_add_violation - add violation to measurement list.
+ *
+ * Violations are flagged in the measurement list with zero hash values.
+ * By extending the PCR with 0xFF's instead of with zeroes, the PCR
+ * value is invalidated.
+ */
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       const char *op, const char *cause)
+{
+	struct ima_template_data template;
+	int violation = 1;
+	int result;
+
+	/* can overflow, only indicator */
+	atomic_long_inc(&ima_htable.violations);
+
+	memset(&template, 0, sizeof(template));
+	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+	result = ima_store_template(&template, violation, inode);
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+			    op, cause, result, 0);
+}
+
+/**
+ * ima_must_measure - measure decision based on policy.
+ * @inode: pointer to inode to measure
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ *
+ * The policy is defined in terms of keypairs:
+ * 		subj=, obj=, type=, func=, mask=, fsmagic=
+ *	subj,obj, and type: are LSM specific.
+ * 	func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ * 	mask: contains the permission mask
+ *	fsmagic: hex value
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 to measure. Return 1 if already measured.
+ * For matching a DONT_MEASURE policy, no policy, or other
+ * error, return an error code.
+*/
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+		     int mask, int function)
+{
+	int must_measure;
+
+	if (iint->flags & IMA_MEASURED)
+		return 1;
+
+	must_measure = ima_match_policy(inode, function, mask);
+	return must_measure ? 0 : -EACCES;
+}
+
+/*
+ * ima_collect_measurement - collect file measurement
+ *
+ * Calculate the file hash, if it doesn't already exist,
+ * storing the measurement and i_version in the iint.
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+{
+	int result = -EEXIST;
+
+	if (!(iint->flags & IMA_MEASURED)) {
+		u64 i_version = file->f_dentry->d_inode->i_version;
+
+		memset(iint->digest, 0, IMA_DIGEST_SIZE);
+		result = ima_calc_hash(file, iint->digest);
+		if (!result)
+			iint->version = i_version;
+	}
+	return result;
+}
+
+/*
+ * ima_store_measurement - store file measurement
+ *
+ * Create an "ima" template and then store the template by calling
+ * ima_store_template.
+ *
+ * We only get here if the inode has not already been measured,
+ * but the measurement could already exist:
+ * 	- multiple copies of the same file on either the same or
+ *	  different filesystems.
+ *	- the inode was previously flushed as well as the iint info,
+ *	  containing the hashing info.
+ *
+ * Must be called with iint->mutex held.
+ */
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+			   const unsigned char *filename)
+{
+
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_template_data template;
+	int violation = 0;
+	int result;
+
+	memset(&template, 0, sizeof(template));
+	memcpy(template.digest, iint->digest, IMA_DIGEST_SIZE);
+	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+
+	result = ima_store_template(&template, violation, inode);
+	if (!result)
+		iint->flags |= IMA_MEASURED;
+}
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
new file mode 100644
index 0000000..8a0f1e2
--- /dev/null
+++ b/security/integrity/ima/ima_audit.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * File: integrity_audit.c
+ * 	Audit calls for the integrity subsystem
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include "ima.h"
+
+static int ima_audit;
+
+#ifdef CONFIG_IMA_AUDIT
+
+/* ima_audit_setup - enable informational auditing messages */
+static int __init ima_audit_setup(char *str)
+{
+	unsigned long audit;
+	int rc;
+	char *op;
+
+	rc = strict_strtoul(str, 0, &audit);
+	if (rc || audit > 1)
+		printk(KERN_INFO "ima: invalid ima_audit value\n");
+	else
+		ima_audit = audit;
+	op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled";
+	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0);
+	return 1;
+}
+__setup("ima_audit=", ima_audit_setup);
+#endif
+
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+			 const unsigned char *fname, const char *op,
+			 const char *cause, int result, int audit_info)
+{
+	struct audit_buffer *ab;
+
+	if (!ima_audit && audit_info == 1) /* Skip informational messages */
+		return;
+
+	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
+	audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u",
+			 current->pid, current->cred->uid,
+			 audit_get_loginuid(current));
+	audit_log_task_context(ab);
+	switch (audit_msgno) {
+	case AUDIT_INTEGRITY_DATA:
+	case AUDIT_INTEGRITY_METADATA:
+	case AUDIT_INTEGRITY_PCR:
+		audit_log_format(ab, " op=%s cause=%s", op, cause);
+		break;
+	case AUDIT_INTEGRITY_HASH:
+		audit_log_format(ab, " op=%s hash=%s", op, cause);
+		break;
+	case AUDIT_INTEGRITY_STATUS:
+	default:
+		audit_log_format(ab, " op=%s", op);
+	}
+	audit_log_format(ab, " comm=");
+	audit_log_untrustedstring(ab, current->comm);
+	if (fname) {
+		audit_log_format(ab, " name=");
+		audit_log_untrustedstring(ab, fname);
+	}
+	if (inode)
+		audit_log_format(ab, " dev=%s ino=%lu",
+				 inode->i_sb->s_id, inode->i_ino);
+	audit_log_format(ab, " res=%d", result);
+	audit_log_end(ab);
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
new file mode 100644
index 0000000..c2a46e4
--- /dev/null
+++ b/security/integrity/ima/ima_crypto.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 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: ima_crypto.c
+ * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ */
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+static int init_desc(struct hash_desc *desc)
+{
+	int rc;
+
+	desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(desc->tfm)) {
+		pr_info("failed to load %s transform: %ld\n",
+			ima_hash, PTR_ERR(desc->tfm));
+		rc = PTR_ERR(desc->tfm);
+		return rc;
+	}
+	desc->flags = 0;
+	rc = crypto_hash_init(desc);
+	if (rc)
+		crypto_free_hash(desc->tfm);
+	return rc;
+}
+
+/*
+ * Calculate the MD5/SHA1 file digest
+ */
+int ima_calc_hash(struct file *file, char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg[1];
+	loff_t i_size;
+	char *rbuf;
+	int rc, offset = 0;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!rbuf) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	i_size = i_size_read(file->f_dentry->d_inode);
+	while (offset < i_size) {
+		int rbuf_len;
+
+		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+		if (rbuf_len < 0) {
+			rc = rbuf_len;
+			break;
+		}
+		offset += rbuf_len;
+		sg_set_buf(sg, rbuf, rbuf_len);
+
+		rc = crypto_hash_update(&desc, sg, rbuf_len);
+		if (rc)
+			break;
+	}
+	kfree(rbuf);
+	if (!rc)
+		rc = crypto_hash_final(&desc, digest);
+out:
+	crypto_free_hash(desc.tfm);
+	return rc;
+}
+
+/*
+ * Calculate the hash of a given template
+ */
+int ima_calc_template_hash(int template_len, void *template, char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg[1];
+	int rc;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	sg_set_buf(sg, template, template_len);
+	rc = crypto_hash_update(&desc, sg, template_len);
+	if (!rc)
+		rc = crypto_hash_final(&desc, digest);
+	crypto_free_hash(desc.tfm);
+	return rc;
+}
+
+static void ima_pcrread(int idx, u8 *pcr)
+{
+	if (!ima_used_chip)
+		return;
+
+	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+		pr_err("Error Communicating to TPM chip\n");
+}
+
+/*
+ * Calculate the boot aggregate hash
+ */
+int ima_calc_boot_aggregate(char *digest)
+{
+	struct hash_desc desc;
+	struct scatterlist sg;
+	u8 pcr_i[IMA_DIGEST_SIZE];
+	int rc, i;
+
+	rc = init_desc(&desc);
+	if (rc != 0)
+		return rc;
+
+	/* cumulative sha1 over tpm registers 0-7 */
+	for (i = TPM_PCR0; i < TPM_PCR8; i++) {
+		ima_pcrread(i, pcr_i);
+		/* now accumulate with current aggregate */
+		sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
+		rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+	}
+	if (!rc)
+		crypto_hash_final(&desc, digest);
+	crypto_free_hash(desc.tfm);
+	return rc;
+}
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
new file mode 100644
index 0000000..750db3c
--- /dev/null
+++ b/security/integrity/ima/ima_iint.c
@@ -0,0 +1,185 @@
+/*
+ * 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 radix tree.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "ima.h"
+
+#define ima_iint_delete ima_inode_free
+
+RADIX_TREE(ima_iint_store, GFP_ATOMIC);
+DEFINE_SPINLOCK(ima_iint_lock);
+
+static struct kmem_cache *iint_cache __read_mostly;
+
+/* ima_iint_find_get - return the iint associated with an inode
+ *
+ * ima_iint_find_get gets a reference to the iint. Caller must
+ * remember to put the iint reference.
+ */
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	rcu_read_lock();
+	iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
+	if (!iint)
+		goto out;
+	kref_get(&iint->refcount);
+out:
+	rcu_read_unlock();
+	return iint;
+}
+
+/* Allocate memory for the iint associated with the inode
+ * from the iint_cache slab, initialize the iint, and
+ * insert it into the radix tree.
+ *
+ * On success return a pointer to the iint; on failure return NULL.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode)
+{
+	struct ima_iint_cache *iint = NULL;
+	int rc = 0;
+
+	if (!ima_initialized)
+		return iint;
+	iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+	if (!iint)
+		return iint;
+
+	rc = radix_tree_preload(GFP_KERNEL);
+	if (rc < 0)
+		goto out;
+
+	spin_lock(&ima_iint_lock);
+	rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
+	spin_unlock(&ima_iint_lock);
+out:
+	if (rc < 0) {
+		kmem_cache_free(iint_cache, iint);
+		if (rc == -EEXIST) {
+			iint = radix_tree_lookup(&ima_iint_store,
+						 (unsigned long)inode);
+		} else
+			iint = NULL;
+	}
+	radix_tree_preload_end();
+	return iint;
+}
+
+/**
+ * ima_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ *
+ * Return 0 on success, 1 on failure.
+ */
+int ima_inode_alloc(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized)
+		return 0;
+
+	iint = ima_iint_insert(inode);
+	if (!iint)
+		return 1;
+	return 0;
+}
+
+/* ima_iint_find_insert_get - get the iint associated with an inode
+ *
+ * Most insertions are done at inode_alloc, except those allocated
+ * before late_initcall. When the iint does not exist, allocate it,
+ * initialize and insert it, and increment the iint refcount.
+ *
+ * (Can't initialize at security_initcall before any inodes are
+ * allocated, got to wait at least until proc_init.)
+ *
+ *  Return the iint.
+ */
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
+{
+	struct ima_iint_cache *iint = NULL;
+
+	iint = ima_iint_find_get(inode);
+	if (iint)
+		return iint;
+
+	iint = ima_iint_insert(inode);
+	if (iint)
+		kref_get(&iint->refcount);
+
+	return iint;
+}
+
+/* iint_free - called when the iint refcount goes to zero */
+void iint_free(struct kref *kref)
+{
+	struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
+						   refcount);
+	iint->version = 0;
+	iint->flags = 0UL;
+	kref_set(&iint->refcount, 1);
+	kmem_cache_free(iint_cache, iint);
+}
+
+void iint_rcu_free(struct rcu_head *rcu_head)
+{
+	struct ima_iint_cache *iint = container_of(rcu_head,
+						   struct ima_iint_cache, rcu);
+	kref_put(&iint->refcount, iint_free);
+}
+
+/**
+ * ima_iint_delete - called on integrity_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void ima_iint_delete(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized)
+		return;
+	spin_lock(&ima_iint_lock);
+	iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+	spin_unlock(&ima_iint_lock);
+	if (iint)
+		call_rcu(&iint->rcu, iint_rcu_free);
+}
+
+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);
+	iint->readcount = 0;
+	iint->writecount = 0;
+	kref_set(&iint->refcount, 1);
+}
+
+void ima_iintcache_init(void)
+{
+	iint_cache =
+	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
+			      SLAB_PANIC, init_once);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
new file mode 100644
index 0000000..e681a40
--- /dev/null
+++ b/security/integrity/ima/ima_init.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer      <sailer@watson.ibm.com>
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * 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_init.c
+ *             initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+/* Add the boot aggregate to the IMA measurement list and extend
+ * the PCR register.
+ *
+ * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
+ * assuming a TPM chip exists, and zeroes if the TPM chip does not
+ * exist.  Add the boot aggregate measurement to the measurement
+ * list and extend the PCR register.
+ *
+ * If a tpm chip does not exist, indicate the core root of trust is
+ * not hardware based by invalidating the aggregate PCR value.
+ * (The aggregate PCR value is invalidated by adding one value to
+ * the measurement list and extending the aggregate PCR value with
+ * a different value.) Violations add a zero entry to the measurement
+ * list and extend the aggregate PCR value with ff...ff's.
+ */
+static void ima_add_boot_aggregate(void)
+{
+	struct ima_template_data template;
+	int violation = 1;
+	int result;
+
+	memset(&template, 0, sizeof(template));
+	strncpy(template.file_name, boot_aggregate_name,
+		IMA_EVENT_NAME_LEN_MAX);
+
+	if (ima_used_chip) {
+		violation = 0;
+		ima_calc_boot_aggregate(template.digest);
+	}
+	result = ima_store_template(&template, violation, NULL);
+}
+
+int ima_init(void)
+{
+	int rc;
+
+	ima_used_chip = 0;
+	rc = tpm_pcr_read(TPM_ANY_NUM, 0, NULL);
+	if (rc == 0)
+		ima_used_chip = 1;
+
+	if (!ima_used_chip)
+		pr_info("No TPM chip found, activating TPM-bypass!\n");
+
+	ima_add_boot_aggregate();	/* boot aggregate must be first entry */
+	ima_init_policy();
+	return 0;
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
new file mode 100644
index 0000000..53cee4c
--- /dev/null
+++ b/security/integrity/ima/ima_main.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Serge Hallyn <serue@us.ibm.com>
+ * Kylene Hall <kylene@us.ibm.com>
+ * 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_main.c
+ *             implements the IMA hooks: ima_bprm_check, ima_file_mmap,
+ *             and ima_path_check.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+
+#include "ima.h"
+
+int ima_initialized;
+
+char *ima_hash = "sha1";
+static int __init hash_setup(char *str)
+{
+	const char *op = "hash_setup";
+	const char *hash = "sha1";
+	int result = 0;
+	int audit_info = 0;
+
+	if (strncmp(str, "md5", 3) == 0) {
+		hash = "md5";
+		ima_hash = str;
+	} else if (strncmp(str, "sha1", 4) != 0) {
+		hash = "invalid_hash_type";
+		result = 1;
+	}
+	integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
+			    result, audit_info);
+	return 1;
+}
+__setup("ima_hash=", hash_setup);
+
+/**
+ * ima_file_free - called on __fput()
+ * @file: pointer to file structure being freed
+ *
+ * Flag files that changed, based on i_version;
+ * and decrement the iint readcount/writecount.
+ */
+void ima_file_free(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return;
+	iint = ima_iint_find_get(inode);
+	if (!iint)
+		return;
+
+	mutex_lock(&iint->mutex);
+	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+		iint->readcount--;
+
+	if (file->f_mode & FMODE_WRITE) {
+		iint->writecount--;
+		if (iint->writecount == 0) {
+			if (iint->version != inode->i_version)
+				iint->flags &= ~IMA_MEASURED;
+		}
+	}
+	mutex_unlock(&iint->mutex);
+	kref_put(&iint->refcount, iint_free);
+}
+
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+				 struct ima_iint_cache *iint,
+				 struct inode *inode,
+				 const unsigned char *filename)
+{
+	switch (error) {
+	case TOMTOU:
+		if (iint->readcount > 0)
+			ima_add_violation(inode, filename, "invalid_pcr",
+					  "ToMToU");
+		break;
+	case OPEN_WRITERS:
+		if (iint->writecount > 0)
+			ima_add_violation(inode, filename, "invalid_pcr",
+					  "open_writers");
+		break;
+	}
+}
+
+static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
+				const unsigned char *filename)
+{
+	int rc = 0;
+
+	if (IS_ERR(file)) {
+		pr_info("%s dentry_open failed\n", filename);
+		return rc;
+	}
+	iint->readcount++;
+
+	rc = ima_collect_measurement(iint, file);
+	if (!rc)
+		ima_store_measurement(iint, file, filename);
+	return rc;
+}
+
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @path: contains a pointer to the path to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure the file being open for readonly, based on the
+ * ima_must_measure() policy decision.
+ *
+ * Keep read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ * 	- Opening a file for write when already open for read,
+ *	  results in a time of measure, time of use (ToMToU) error.
+ *	- Opening a file for read when already open for write,
+ * 	  could result in a file measurement error.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_path_check(struct path *path, int mask)
+{
+	struct inode *inode = path->dentry->d_inode;
+	struct ima_iint_cache *iint;
+	struct file *file = NULL;
+	int rc;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return 0;
+	iint = ima_iint_find_insert_get(inode);
+	if (!iint)
+		return 0;
+
+	mutex_lock(&iint->mutex);
+	if ((mask & MAY_WRITE) || (mask == 0))
+		iint->writecount++;
+	else if (mask & (MAY_READ | MAY_EXEC))
+		iint->readcount++;
+
+	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
+	if (rc < 0)
+		goto out;
+
+	if ((mask & MAY_WRITE) || (mask == 0))
+		ima_read_write_check(TOMTOU, iint, inode,
+				     path->dentry->d_name.name);
+
+	if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
+		goto out;
+
+	ima_read_write_check(OPEN_WRITERS, iint, inode,
+			     path->dentry->d_name.name);
+	if (!(iint->flags & IMA_MEASURED)) {
+		struct dentry *dentry = dget(path->dentry);
+		struct vfsmount *mnt = mntget(path->mnt);
+
+		file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
+		rc = get_path_measurement(iint, file, dentry->d_name.name);
+	}
+out:
+	mutex_unlock(&iint->mutex);
+	if (file)
+		fput(file);
+	kref_put(&iint->refcount, iint_free);
+	return 0;
+}
+
+static int process_measurement(struct file *file, const unsigned char *filename,
+			       int mask, int function)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+	int rc;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return 0;
+	iint = ima_iint_find_insert_get(inode);
+	if (!iint)
+		return -ENOMEM;
+
+	mutex_lock(&iint->mutex);
+	rc = ima_must_measure(iint, inode, mask, function);
+	if (rc != 0)
+		goto out;
+
+	rc = ima_collect_measurement(iint, file);
+	if (!rc)
+		ima_store_measurement(iint, file, filename);
+out:
+	mutex_unlock(&iint->mutex);
+	kref_put(&iint->refcount, iint_free);
+	return rc;
+}
+
+/**
+ * ima_file_mmap - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured (May be NULL)
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Measure files being mmapped executable based on the ima_must_measure()
+ * policy decision.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_file_mmap(struct file *file, unsigned long prot)
+{
+	int rc;
+
+	if (!file)
+		return 0;
+	if (prot & PROT_EXEC)
+		rc = process_measurement(file, file->f_dentry->d_name.name,
+					 MAY_EXEC, FILE_MMAP);
+	return 0;
+}
+
+/**
+ * ima_bprm_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_bprm_check(struct linux_binprm *bprm)
+{
+	int rc;
+
+	rc = process_measurement(bprm->file, bprm->filename,
+				 MAY_EXEC, BPRM_CHECK);
+	return 0;
+}
+
+static int __init init_ima(void)
+{
+	int error;
+
+	ima_iintcache_init();
+	error = ima_init();
+	ima_initialized = 1;
+	return error;
+}
+
+late_initcall(init_ima);	/* Start IMA after the TPM is available */
+
+MODULE_DESCRIPTION("Integrity Measurement Architecture");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
new file mode 100644
index 0000000..7c3d1ff
--- /dev/null
+++ b/security/integrity/ima/ima_policy.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * ima_policy.c
+ * 	- initialize default measure policy rules
+ *
+ */
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/audit.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+
+#include "ima.h"
+
+/* flags definitions */
+#define IMA_FUNC 	0x0001
+#define IMA_MASK 	0x0002
+#define IMA_FSMAGIC	0x0004
+#define IMA_UID		0x0008
+
+enum ima_action { DONT_MEASURE, MEASURE };
+
+struct ima_measure_rule_entry {
+	struct list_head list;
+	enum ima_action action;
+	unsigned int flags;
+	enum ima_hooks func;
+	int mask;
+	unsigned long fsmagic;
+	uid_t uid;
+};
+
+static struct ima_measure_rule_entry default_rules[] = {
+	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
+	 .flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
+	 .flags = IMA_FSMAGIC},
+	{.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
+	{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+	 .flags = IMA_FUNC | IMA_MASK},
+	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+	 .flags = IMA_FUNC | IMA_MASK},
+	{.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}
+};
+
+static LIST_HEAD(measure_default_rules);
+static struct list_head *ima_measure;
+
+/**
+ * ima_match_rules - determine whether an inode matches the measure rule.
+ * @rule: a pointer to a rule
+ * @inode: a pointer to an inode
+ * @func: LIM hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Returns true on rule match, false on failure.
+ */
+static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+			    struct inode *inode, enum ima_hooks func, int mask)
+{
+	struct task_struct *tsk = current;
+
+	if ((rule->flags & IMA_FUNC) && rule->func != func)
+		return false;
+	if ((rule->flags & IMA_MASK) && rule->mask != mask)
+		return false;
+	if ((rule->flags & IMA_FSMAGIC)
+	    && rule->fsmagic != inode->i_sb->s_magic)
+		return false;
+	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
+		return false;
+	return true;
+}
+
+/**
+ * ima_match_policy - decision based on LSM and other conditions
+ * @inode: pointer to an inode for which the policy decision is being made
+ * @func: IMA hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
+ * conditions.
+ *
+ * (There is no need for locking when walking the policy list,
+ * as elements in the list are never deleted, nor does the list
+ * change.)
+ */
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+{
+	struct ima_measure_rule_entry *entry;
+
+	list_for_each_entry(entry, ima_measure, list) {
+		bool rc;
+
+		rc = ima_match_rules(entry, inode, func, mask);
+		if (rc)
+			return entry->action;
+	}
+	return 0;
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules.
+ *
+ * (Could use the default_rules directly, but in policy patch
+ * ima_measure points to either the measure_default_rules or the
+ * the new measure_policy_rules.)
+ */
+void ima_init_policy(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(default_rules); i++)
+		list_add_tail(&default_rules[i].list, &measure_default_rules);
+	ima_measure = &measure_default_rules;
+}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
new file mode 100644
index 0000000..7cb518f
--- /dev/null
+++ b/security/integrity/ima/ima_queue.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * 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_queue.c
+ *       Implements queues that store template measurements and
+ *       maintains aggregate over the stored measurements
+ *       in the pre-configured TPM PCR (if available).
+ *       The measurement list is append-only. No entry is
+ *       ever removed or changed during the boot-cycle.
+ */
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include "ima.h"
+
+LIST_HEAD(ima_measurements);	/* list of all measurements */
+
+/* key: inode (before secure-hashing a file) */
+struct ima_h_table ima_htable = {
+	.len = ATOMIC_LONG_INIT(0),
+	.violations = ATOMIC_LONG_INIT(0),
+	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+};
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+static DEFINE_MUTEX(ima_extend_list_mutex);
+
+/* lookup up the digest value in the hash table, and return the entry */
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+{
+	struct ima_queue_entry *qe, *ret = NULL;
+	unsigned int key;
+	struct hlist_node *pos;
+
+	key = ima_hash_key(digest_value);
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
+		if (memcmp(qe->entry->digest, digest_value, 20) == 0) {
+			ret = qe;
+			break;
+		}
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+/* ima_add_template_entry helper function:
+ * - Add template entry to measurement list and hash table.
+ *
+ * (Called with ima_extend_list_mutex held.)
+ */
+static int ima_add_digest_entry(struct ima_template_entry *entry)
+{
+	struct ima_queue_entry *qe;
+	unsigned int key;
+
+	qe = kmalloc(sizeof(*qe), GFP_KERNEL);
+	if (qe == NULL) {
+		pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+		return -ENOMEM;
+	}
+	qe->entry = entry;
+
+	INIT_LIST_HEAD(&qe->later);
+	list_add_tail_rcu(&qe->later, &ima_measurements);
+
+	atomic_long_inc(&ima_htable.len);
+	key = ima_hash_key(entry->digest);
+	hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+	return 0;
+}
+
+static int ima_pcr_extend(const u8 *hash)
+{
+	int result = 0;
+
+	if (!ima_used_chip)
+		return result;
+
+	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+	if (result != 0)
+		pr_err("Error Communicating to TPM chip\n");
+	return result;
+}
+
+/* Add template entry to the measurement list and hash table,
+ * and extend the pcr.
+ */
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+			   const char *op, struct inode *inode)
+{
+	u8 digest[IMA_DIGEST_SIZE];
+	const char *audit_cause = "hash_added";
+	int audit_info = 1;
+	int result = 0;
+
+	mutex_lock(&ima_extend_list_mutex);
+	if (!violation) {
+		memcpy(digest, entry->digest, sizeof digest);
+		if (ima_lookup_digest_entry(digest)) {
+			audit_cause = "hash_exists";
+			goto out;
+		}
+	}
+
+	result = ima_add_digest_entry(entry);
+	if (result < 0) {
+		audit_cause = "ENOMEM";
+		audit_info = 0;
+		goto out;
+	}
+
+	if (violation)		/* invalidate pcr */
+		memset(digest, 0xff, sizeof digest);
+
+	result = ima_pcr_extend(digest);
+	if (result != 0) {
+		audit_cause = "TPM error";
+		audit_info = 0;
+	}
+out:
+	mutex_unlock(&ima_extend_list_mutex);
+	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+			    op, audit_cause, result, audit_info);
+	return result;
+}
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/6] integrity: IMA display
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
  2009-01-29 22:23 ` [PATCH 1/6] integrity: IMA hooks Mimi Zohar
  2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-01-30  9:18   ` James Morris
  2009-01-29 22:23 ` [PATCH 4/6] integrity: IMA policy Mimi Zohar
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Make the measurement lists available through securityfs.
- replaced deprecated list_for_each_rcu() with list_for_each_entry_rcu()
- refactoring added: ima_putc, ima_print_display

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 9d6bf97..787c4cb 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -5,5 +5,5 @@
 
 obj-$(CONFIG_IMA) += ima.o
 
-ima-y := 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
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 04a397e..236b74e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -68,6 +68,9 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
 /* Internal IMA function definitions */
 void ima_iintcache_init(void);
 int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode);
 int ima_calc_hash(struct file *file, char *digest);
@@ -116,6 +119,8 @@ void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
 			   const unsigned char *filename);
 int ima_store_template(struct ima_template_data *data, int violation,
 		       struct inode *inode);
+void ima_template_show(struct seq_file *m, void *e,
+		       enum ima_show_type show);
 
 /* radix tree calls to lookup, insert, delete
  * integrity data associated with an inode.
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
new file mode 100644
index 0000000..5044e4c
--- /dev/null
+++ b/security/integrity/ima/ima_fs.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Reiner Sailer <sailer@us.ibm.com>
+ * 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_fs.c
+ *	implemenents security file system for reporting
+ *	current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+
+#include "ima.h"
+
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+				     loff_t *ppos, atomic_long_t *val)
+{
+	char tmpbuf[TMPBUFLEN];
+	ssize_t len;
+
+	len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+					  char __user *buf,
+					  size_t count, loff_t *ppos)
+{
+	return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+	.read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+					   char __user *buf,
+					   size_t count, loff_t *ppos)
+{
+	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+	.read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+	loff_t l = *pos;
+	struct ima_queue_entry *qe;
+
+	/* we need a lock since pos could point beyond last element */
+	rcu_read_lock();
+	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+		if (!l--) {
+			rcu_read_unlock();
+			return qe;
+		}
+	}
+	rcu_read_unlock();
+	return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct ima_queue_entry *qe = v;
+
+	/* lock protects when reading beyond last element
+	 * against concurrent list-extension
+	 */
+	rcu_read_lock();
+	qe = list_entry(rcu_dereference(qe->later.next),
+			struct ima_queue_entry, later);
+	rcu_read_unlock();
+	(*pos)++;
+
+	return (&qe->later == &ima_measurements) ? NULL : qe;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static void ima_putc(struct seq_file *m, void *data, int datalen)
+{
+	while (datalen--)
+		seq_putc(m, *(char *)data++);
+}
+
+/* print format:
+ *       32bit-le=pcr#
+ *       char[20]=template digest
+ *       32bit-le=template name size
+ *       char[n]=template name
+ *       eventdata[n]=template specific data
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+	/* the list never shrinks, so we don't need a lock here */
+	struct ima_queue_entry *qe = v;
+	struct ima_template_entry *e;
+	int namelen;
+	u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+
+	/* get entry */
+	e = qe->entry;
+	if (e == NULL)
+		return -1;
+
+	/*
+	 * 1st: PCRIndex
+	 * PCR used is always the same (config option) in
+	 * little-endian format
+	 */
+	ima_putc(m, &pcr, sizeof pcr);
+
+	/* 2nd: template digest */
+	ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+
+	/* 3rd: template name size */
+	namelen = strlen(e->template_name);
+	ima_putc(m, &namelen, sizeof namelen);
+
+	/* 4th:  template name */
+	ima_putc(m, e->template_name, namelen);
+
+	/* 5th:  template specific data */
+	ima_template_show(m, (struct ima_template_data *)&e->template,
+			  IMA_SHOW_BINARY);
+	return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+	.start = ima_measurements_start,
+	.next = ima_measurements_next,
+	.stop = ima_measurements_stop,
+	.show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+	.open = ima_measurements_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static void ima_print_digest(struct seq_file *m, u8 *digest)
+{
+	int i;
+
+	for (i = 0; i < IMA_DIGEST_SIZE; i++)
+		seq_printf(m, "%02x", *(digest + i));
+}
+
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
+{
+	struct ima_template_data *entry = e;
+	int namelen;
+
+	switch (show) {
+	case IMA_SHOW_ASCII:
+		ima_print_digest(m, entry->digest);
+		seq_printf(m, " %s\n", entry->file_name);
+		break;
+	case IMA_SHOW_BINARY:
+		ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
+
+		namelen = strlen(entry->file_name);
+		ima_putc(m, &namelen, sizeof namelen);
+		ima_putc(m, entry->file_name, namelen);
+	default:
+		break;
+	}
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+	/* the list never shrinks, so we don't need a lock here */
+	struct ima_queue_entry *qe = v;
+	struct ima_template_entry *e;
+
+	/* get entry */
+	e = qe->entry;
+	if (e == NULL)
+		return -1;
+
+	/* 1st: PCR used (config option) */
+	seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+	/* 2nd: SHA1 template hash */
+	ima_print_digest(m, e->digest);
+
+	/* 3th:  template name */
+	seq_printf(m, " %s ", e->template_name);
+
+	/* 4th:  template specific data */
+	ima_template_show(m, (struct ima_template_data *)&e->template,
+			  IMA_SHOW_ASCII);
+	return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+	.start = ima_measurements_start,
+	.next = ima_measurements_next,
+	.stop = ima_measurements_stop,
+	.show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+	.open = ima_ascii_measurements_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = seq_release,
+};
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+
+int ima_fs_init(void)
+{
+	ima_dir = securityfs_create_dir("ima", NULL);
+	if (!ima_dir || IS_ERR(ima_dir))
+		return -1;
+
+	binary_runtime_measurements =
+	    securityfs_create_file("binary_runtime_measurements",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_measurements_ops);
+	if (!binary_runtime_measurements || IS_ERR(binary_runtime_measurements))
+		goto out;
+
+	ascii_runtime_measurements =
+	    securityfs_create_file("ascii_runtime_measurements",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_ascii_measurements_ops);
+	if (!ascii_runtime_measurements || IS_ERR(ascii_runtime_measurements))
+		goto out;
+
+	runtime_measurements_count =
+	    securityfs_create_file("runtime_measurements_count",
+				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_measurements_count_ops);
+	if (!runtime_measurements_count || IS_ERR(runtime_measurements_count))
+		goto out;
+
+	violations =
+	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+				   ima_dir, NULL, &ima_htable_violations_ops);
+	if (!violations || IS_ERR(violations))
+		goto out;
+
+	return 0;
+
+out:
+	securityfs_remove(runtime_measurements_count);
+	securityfs_remove(ascii_runtime_measurements);
+	securityfs_remove(binary_runtime_measurements);
+	securityfs_remove(ima_dir);
+	return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+	securityfs_remove(violations);
+	securityfs_remove(runtime_measurements_count);
+	securityfs_remove(ascii_runtime_measurements);
+	securityfs_remove(binary_runtime_measurements);
+	securityfs_remove(ima_dir);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index e681a40..8faac85 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -69,5 +69,11 @@ int ima_init(void)
 
 	ima_add_boot_aggregate();	/* boot aggregate must be first entry */
 	ima_init_policy();
-	return 0;
+
+	return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+	ima_fs_cleanup();
 }
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 53cee4c..871e356 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -274,6 +274,11 @@ static int __init init_ima(void)
 	return error;
 }
 
+static void __exit cleanup_ima(void)
+{
+	ima_cleanup();
+}
+
 late_initcall(init_ima);	/* Start IMA after the TPM is available */
 
 MODULE_DESCRIPTION("Integrity Measurement Architecture");
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/6] integrity: IMA policy
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
                   ` (2 preceding siblings ...)
  2009-01-29 22:23 ` [PATCH 3/6] integrity: IMA display Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-02-02 23:40   ` Serge E. Hallyn
  2009-01-29 22:23 ` [PATCH 5/6] integrity: IMA policy open Mimi Zohar
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Support for a user loadable policy through securityfs
with support for LSM specific policy data.

Based on comments made by: Matt Helsley, Serge Hallyn
- replaced policy parsing code with version using strsep and match_token
- only replace default policy with a valid policy

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
---
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
new file mode 100644
index 0000000..6434f0d
--- /dev/null
+++ b/Documentation/ABI/testing/ima_policy
@@ -0,0 +1,61 @@
+What:		security/ima/policy
+Date:		May 2008
+Contact:	Mimi Zohar <zohar@us.ibm.com>
+Description:
+		The Trusted Computing Group(TCG) runtime Integrity
+		Measurement Architecture(IMA) maintains a list of hash
+		values of executables and other sensitive system files
+		loaded into the run-time of this system.  At runtime,
+		the policy can be constrained based on LSM specific data.
+		Policies are loaded into the securityfs file ima/policy
+		by opening the file, writing the rules one at a time and
+		then closing the file.  The new policy takes effect after
+		the file ima/policy is closed.
+
+		rule format: action [condition ...]
+
+		action: measure | dont_measure
+		condition:= base | lsm
+			base:	[[func=] [mask=] [fsmagic=] [uid=]]
+			lsm:	[[subj_user=] [subj_role=] [subj_type=]
+				 [obj_user=] [obj_role=] [obj_type=]]
+
+		base: 	func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+			fsmagic:= hex value
+			uid:= decimal value
+		lsm:  	are LSM specific
+
+		default policy:
+			# PROC_SUPER_MAGIC
+			dont_measure fsmagic=0x9fa0
+			# SYSFS_MAGIC
+			dont_measure fsmagic=0x62656572
+			# DEBUGFS_MAGIC
+			dont_measure fsmagic=0x64626720
+			# TMPFS_MAGIC
+			dont_measure fsmagic=0x01021994
+			# SECURITYFS_MAGIC
+			dont_measure fsmagic=0x73636673
+
+			measure func=BPRM_CHECK
+			measure func=FILE_MMAP mask=MAY_EXEC
+			measure func=INODE_PERM mask=MAY_READ uid=0
+
+		The default policy measures all executables in bprm_check,
+		all files mmapped executable in file_mmap, and all files
+		open for read by root in inode_permission.
+
+		Examples of LSM specific definitions:
+
+		SELinux:
+			# SELINUX_MAGIC
+			dont_measure fsmagic=0xF97CFF8C
+
+			dont_measure obj_type=var_log_t
+			dont_measure obj_type=auditd_log_t
+			measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+			measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+		Smack:
+			measure subj_user=_ func=INODE_PERM mask=MAY_READ
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 2a761c8..3d2b6ee 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -47,3 +47,9 @@ config IMA_AUDIT
 	  auditing messages can be enabled with 'ima_audit=1' on
 	  the kernel command line.
 
+config IMA_LSM_RULES
+	bool
+	depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK)
+	default y
+	help
+	  Disabling this option will disregard LSM based policy rules
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 236b74e..5b72cdb 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -138,4 +138,28 @@ enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
 int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
 void ima_init_policy(void);
 void ima_update_policy(void);
+int ima_parse_add_rule(char *);
+void ima_delete_rules(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+					    void **lsmrule)
+{
+	return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+					     void *lsmrule,
+					     struct audit_context *actx)
+{
+	return -EINVAL;
+}
+#endif /* CONFIG_IMA_LSM_RULES */
 #endif
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5044e4c..752a344 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -19,9 +19,11 @@
 #include <linux/seq_file.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
+#include <linux/parser.h>
 
 #include "ima.h"
 
+static int valid_policy = 1;
 #define TMPBUFLEN 12
 static ssize_t ima_show_htable_value(char __user *buf, size_t count,
 				     loff_t *ppos, atomic_long_t *val)
@@ -237,11 +239,66 @@ static struct file_operations ima_ascii_measurements_ops = {
 	.release = seq_release,
 };
 
+static ssize_t ima_write_policy(struct file *file, const char __user *buf,
+				size_t datalen, loff_t *ppos)
+{
+	char *data;
+	int rc;
+
+	if (datalen >= PAGE_SIZE)
+		return -ENOMEM;
+	if (*ppos != 0) {
+		/* No partial writes. */
+		return -EINVAL;
+	}
+	data = kmalloc(datalen + 1, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, buf, datalen)) {
+		kfree(data);
+		return -EFAULT;
+	}
+	*(data + datalen) = '\0';
+	rc = ima_parse_add_rule(data);
+	if (rc < 0) {
+		datalen = -EINVAL;
+		valid_policy = 0;
+	}
+
+	kfree(data);
+	return datalen;
+}
+
 static struct dentry *ima_dir;
 static struct dentry *binary_runtime_measurements;
 static struct dentry *ascii_runtime_measurements;
 static struct dentry *runtime_measurements_count;
 static struct dentry *violations;
+static struct dentry *ima_policy;
+
+/*
+ * ima_release_policy - start using the new measure policy rules.
+ *
+ * Initially, ima_measure points to the default policy rules, now
+ * point to the new policy rules, and remove the securityfs policy file.
+ */
+static int ima_release_policy(struct inode *inode, struct file *file)
+{
+	if (!valid_policy) {
+		ima_delete_rules();
+		return 0;
+	}
+	ima_update_policy();
+	securityfs_remove(ima_policy);
+	ima_policy = NULL;
+	return 0;
+}
+
+static struct file_operations ima_measure_policy_ops = {
+	.write = ima_write_policy,
+	.release = ima_release_policy
+};
 
 int ima_fs_init(void)
 {
@@ -276,13 +333,20 @@ int ima_fs_init(void)
 	if (!violations || IS_ERR(violations))
 		goto out;
 
-	return 0;
+	ima_policy = securityfs_create_file("policy",
+					    S_IRUSR | S_IRGRP | S_IWUSR,
+					    ima_dir, NULL,
+					    &ima_measure_policy_ops);
+	if (!ima_policy || IS_ERR(ima_policy))
+		goto out;
 
+	return 0;
 out:
 	securityfs_remove(runtime_measurements_count);
 	securityfs_remove(ascii_runtime_measurements);
 	securityfs_remove(binary_runtime_measurements);
 	securityfs_remove(ima_dir);
+	securityfs_remove(ima_policy);
 	return -1;
 }
 
@@ -293,4 +357,5 @@ void __exit ima_fs_cleanup(void)
 	securityfs_remove(ascii_runtime_measurements);
 	securityfs_remove(binary_runtime_measurements);
 	securityfs_remove(ima_dir);
+	securityfs_remove(ima_policy);
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 7c3d1ff..c10e6f6 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -15,6 +15,7 @@
 #include <linux/audit.h>
 #include <linux/security.h>
 #include <linux/magic.h>
+#include <linux/parser.h>
 
 #include "ima.h"
 
@@ -24,7 +25,12 @@
 #define IMA_FSMAGIC	0x0004
 #define IMA_UID		0x0008
 
-enum ima_action { DONT_MEASURE, MEASURE };
+enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+
+#define MAX_LSM_RULES 6
+enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
+	LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
+};
 
 struct ima_measure_rule_entry {
 	struct list_head list;
@@ -34,8 +40,15 @@ struct ima_measure_rule_entry {
 	int mask;
 	unsigned long fsmagic;
 	uid_t uid;
+	struct {
+		void *rule;	/* LSM file metadata specific */
+		int type;	/* audit type */
+	} lsm[MAX_LSM_RULES];
 };
 
+/* Without LSM specific knowledge, the default policy can only be
+ * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ */
 static struct ima_measure_rule_entry default_rules[] = {
 	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
 	 .flags = IMA_FSMAGIC},
@@ -54,8 +67,11 @@ static struct ima_measure_rule_entry default_rules[] = {
 };
 
 static LIST_HEAD(measure_default_rules);
+static LIST_HEAD(measure_policy_rules);
 static struct list_head *ima_measure;
 
+static DEFINE_MUTEX(ima_measure_mutex);
+
 /**
  * ima_match_rules - determine whether an inode matches the measure rule.
  * @rule: a pointer to a rule
@@ -69,6 +85,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
 			    struct inode *inode, enum ima_hooks func, int mask)
 {
 	struct task_struct *tsk = current;
+	int i;
 
 	if ((rule->flags & IMA_FUNC) && rule->func != func)
 		return false;
@@ -79,6 +96,39 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
 		return false;
 	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
 		return false;
+	for (i = 0; i < MAX_LSM_RULES; i++) {
+		int rc;
+		u32 osid, sid;
+
+		if (!rule->lsm[i].rule)
+			continue;
+
+		switch (i) {
+		case LSM_OBJ_USER:
+		case LSM_OBJ_ROLE:
+		case LSM_OBJ_TYPE:
+			security_inode_getsecid(inode, &osid);
+			rc = security_filter_rule_match(osid,
+							rule->lsm[i].type,
+							AUDIT_EQUAL,
+							rule->lsm[i].rule,
+							NULL);
+			break;
+		case LSM_SUBJ_USER:
+		case LSM_SUBJ_ROLE:
+		case LSM_SUBJ_TYPE:
+			security_task_getsecid(tsk, &sid);
+			rc = security_filter_rule_match(sid,
+							rule->lsm[i].type,
+							AUDIT_EQUAL,
+							rule->lsm[i].rule,
+							NULL);
+		default:
+			break;
+		}
+		if (!rc)
+			return false;
+	}
 	return true;
 }
 
@@ -112,9 +162,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
 /**
  * ima_init_policy - initialize the default measure rules.
  *
- * (Could use the default_rules directly, but in policy patch
  * ima_measure points to either the measure_default_rules or the
- * the new measure_policy_rules.)
+ * the new measure_policy_rules.
  */
 void ima_init_policy(void)
 {
@@ -124,3 +173,240 @@ void ima_init_policy(void)
 		list_add_tail(&default_rules[i].list, &measure_default_rules);
 	ima_measure = &measure_default_rules;
 }
+
+/**
+ * ima_update_policy - update default_rules with new measure rules
+ *
+ * Called on file .release to update the default rules with a complete new
+ * policy.  Once updated, the policy is locked, no additional rules can be
+ * added to the policy.
+ */
+void ima_update_policy(void)
+{
+	const char *op = "policy_update";
+	const char *cause = "already exists";
+	int result = 1;
+	int audit_info = 0;
+
+	if (ima_measure == &measure_default_rules) {
+		ima_measure = &measure_policy_rules;
+		cause = "complete";
+		result = 0;
+	}
+	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+			    NULL, op, cause, result, audit_info);
+}
+
+enum {
+	Opt_err = -1,
+	Opt_measure = 1, Opt_dont_measure,
+	Opt_obj_user, Opt_obj_role, Opt_obj_type,
+	Opt_subj_user, Opt_subj_role, Opt_subj_type,
+	Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
+};
+
+static match_table_t policy_tokens = {
+	{Opt_measure, "measure"},
+	{Opt_dont_measure, "dont_measure"},
+	{Opt_obj_user, "obj_user=%s"},
+	{Opt_obj_role, "obj_role=%s"},
+	{Opt_obj_type, "obj_type=%s"},
+	{Opt_subj_user, "subj_user=%s"},
+	{Opt_subj_role, "subj_role=%s"},
+	{Opt_subj_type, "subj_type=%s"},
+	{Opt_func, "func=%s"},
+	{Opt_mask, "mask=%s"},
+	{Opt_fsmagic, "fsmagic=%s"},
+	{Opt_uid, "uid=%s"},
+	{Opt_err, NULL}
+};
+
+static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+			     char *args, int lsm_rule, int audit_type)
+{
+	int result;
+
+	entry->lsm[lsm_rule].type = audit_type;
+	result = security_filter_rule_init(entry->lsm[lsm_rule].type,
+					   AUDIT_EQUAL, args,
+					   &entry->lsm[lsm_rule].rule);
+	return result;
+}
+
+static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+{
+	struct audit_buffer *ab;
+	char *p;
+	int result = 0;
+
+	ab = audit_log_start(current->audit_context, GFP_KERNEL,
+			     AUDIT_INTEGRITY_STATUS);
+
+	entry->action = -1;
+	while ((p = strsep(&rule, " \n")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		unsigned long lnum;
+
+		if (result < 0)
+			break;
+		if (!*p)
+			continue;
+		token = match_token(p, policy_tokens, args);
+		switch (token) {
+		case Opt_measure:
+			audit_log_format(ab, "%s ", "measure");
+			entry->action = MEASURE;
+			break;
+		case Opt_dont_measure:
+			audit_log_format(ab, "%s ", "dont_measure");
+			entry->action = DONT_MEASURE;
+			break;
+		case Opt_func:
+			audit_log_format(ab, "func=%s ", args[0].from);
+			if (strcmp(args[0].from, "PATH_CHECK") == 0)
+				entry->func = PATH_CHECK;
+			else if (strcmp(args[0].from, "FILE_MMAP") == 0)
+				entry->func = FILE_MMAP;
+			else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
+				entry->func = BPRM_CHECK;
+			else
+				result = -EINVAL;
+			if (!result)
+				entry->flags |= IMA_FUNC;
+			break;
+		case Opt_mask:
+			audit_log_format(ab, "mask=%s ", args[0].from);
+			if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
+				entry->mask = MAY_EXEC;
+			else if (strcmp(args[0].from, "MAY_WRITE") == 0)
+				entry->mask = MAY_WRITE;
+			else if (strcmp(args[0].from, "MAY_READ") == 0)
+				entry->mask = MAY_READ;
+			else if (strcmp(args[0].from, "MAY_APPEND") == 0)
+				entry->mask = MAY_APPEND;
+			else
+				result = -EINVAL;
+			if (!result)
+				entry->flags |= IMA_MASK;
+			break;
+		case Opt_fsmagic:
+			audit_log_format(ab, "fsmagic=%s ", args[0].from);
+			result = strict_strtoul(args[0].from, 16,
+						&entry->fsmagic);
+			if (!result)
+				entry->flags |= IMA_FSMAGIC;
+			break;
+		case Opt_uid:
+			audit_log_format(ab, "uid=%s ", args[0].from);
+			result = strict_strtoul(args[0].from, 10, &lnum);
+			if (!result) {
+				entry->uid = (uid_t) lnum;
+				if (entry->uid != lnum)
+					result = -EINVAL;
+				else
+					entry->flags |= IMA_UID;
+			}
+			break;
+		case Opt_obj_user:
+			audit_log_format(ab, "obj_user=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_USER,
+						   AUDIT_OBJ_USER);
+			break;
+		case Opt_obj_role:
+			audit_log_format(ab, "obj_role=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_ROLE,
+						   AUDIT_OBJ_ROLE);
+			break;
+		case Opt_obj_type:
+			audit_log_format(ab, "obj_type=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_OBJ_TYPE,
+						   AUDIT_OBJ_TYPE);
+			break;
+		case Opt_subj_user:
+			audit_log_format(ab, "subj_user=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_USER,
+						   AUDIT_SUBJ_USER);
+			break;
+		case Opt_subj_role:
+			audit_log_format(ab, "subj_role=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_ROLE,
+						   AUDIT_SUBJ_ROLE);
+			break;
+		case Opt_subj_type:
+			audit_log_format(ab, "subj_type=%s ", args[0].from);
+			result = ima_lsm_rule_init(entry, args[0].from,
+						   LSM_SUBJ_TYPE,
+						   AUDIT_SUBJ_TYPE);
+			break;
+		case Opt_err:
+			printk(KERN_INFO "%s: unknown token: %s\n",
+			       __FUNCTION__, p);
+			break;
+		}
+	}
+	if (entry->action == UNKNOWN)
+		result = -EINVAL;
+
+	audit_log_format(ab, "res=%d", result);
+	audit_log_end(ab);
+	return result;
+}
+
+/**
+ * ima_parse_add_rule - add a rule to measure_policy_rules
+ * @rule - ima measurement policy rule
+ *
+ * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Returns 0 on success, an error code on failure.
+ */
+int ima_parse_add_rule(char *rule)
+{
+	const char *op = "add_rule";
+	struct ima_measure_rule_entry *entry;
+	int result = 0;
+	int audit_info = 0;
+
+	/* Prevent installed policy from changing */
+	if (ima_measure != &measure_default_rules) {
+		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+				    NULL, op, "already exists",
+				    -EACCES, audit_info);
+		return -EACCES;
+	}
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&entry->list);
+
+	result = ima_parse_rule(rule, entry);
+	if (!result) {
+		mutex_lock(&ima_measure_mutex);
+		list_add_tail(&entry->list, &measure_policy_rules);
+		mutex_unlock(&ima_measure_mutex);
+	}
+	return result;
+}
+
+/* ima_delete_rules called to cleanup invalid policy */
+void ima_delete_rules()
+{
+	struct ima_measure_rule_entry *entry, *tmp;
+
+	mutex_lock(&ima_measure_mutex);
+	list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+		list_del(&entry->list);
+		kfree(entry);
+	}
+	mutex_unlock(&ima_measure_mutex);
+}
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 5/6] integrity: IMA policy open
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
                   ` (3 preceding siblings ...)
  2009-01-29 22:23 ` [PATCH 4/6] integrity: IMA policy Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-01-29 22:23 ` [PATCH 6/6] Integrity: IMA file free imbalance Mimi Zohar
  2009-02-06  0:00 ` [PATCH 0/6] integrity James Morris
  6 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Sequentialize access to the policy file
- permit multiple attempts to replace default policy with a valid policy

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 752a344..dde803d 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -277,16 +277,30 @@ static struct dentry *runtime_measurements_count;
 static struct dentry *violations;
 static struct dentry *ima_policy;
 
+static atomic_t policy_opencount = ATOMIC_INIT(1);
+/*
+ * ima_open_policy: sequentialize access to the policy file
+ */
+int ima_open_policy(struct inode * inode, struct file * filp)
+{
+	if (atomic_dec_and_test(&policy_opencount))
+		return 0;
+	return -EBUSY;
+}
+
 /*
  * ima_release_policy - start using the new measure policy rules.
  *
  * Initially, ima_measure points to the default policy rules, now
- * point to the new policy rules, and remove the securityfs policy file.
+ * point to the new policy rules, and remove the securityfs policy file,
+ * assuming a valid policy.
  */
 static int ima_release_policy(struct inode *inode, struct file *file)
 {
 	if (!valid_policy) {
 		ima_delete_rules();
+		valid_policy = 1;
+		atomic_set(&policy_opencount, 1);
 		return 0;
 	}
 	ima_update_policy();
@@ -296,6 +310,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 }
 
 static struct file_operations ima_measure_policy_ops = {
+	.open = ima_open_policy,
 	.write = ima_write_policy,
 	.release = ima_release_policy
 };
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 6/6] Integrity: IMA file free imbalance
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
                   ` (4 preceding siblings ...)
  2009-01-29 22:23 ` [PATCH 5/6] integrity: IMA policy open Mimi Zohar
@ 2009-01-29 22:23 ` Mimi Zohar
  2009-02-02 23:47   ` Serge E. Hallyn
  2009-02-06  0:00 ` [PATCH 0/6] integrity James Morris
  6 siblings, 1 reply; 27+ messages in thread
From: Mimi Zohar @ 2009-01-29 22:23 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

The number of calls to ima_path_check()/ima_file_free()
should be balanced.  An extra call to fput(), indicates
the file could have been accessed without first being
measured.

Although f_count is incremented/decremented in places other
than fget/fput, like fget_light/fput_light and get_file, the
current task must already hold a file refcnt.  The call to
__fput() is delayed until the refcnt becomes 0, resulting
in ima_file_free() flagging any changes.

- add hook to increment opencount for IPC shared memory(SYSV)
  and shmat files

Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
---
diff --git a/include/linux/ima.h b/include/linux/ima.h
index dcc3664..6db30a3 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -19,6 +19,7 @@ extern void ima_inode_free(struct inode *inode);
 extern int ima_path_check(struct path *path, int mask);
 extern void ima_file_free(struct file *file);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
+extern void ima_shm_check(struct file *file);
 
 #else
 static inline int ima_bprm_check(struct linux_binprm *bprm)
@@ -50,5 +51,10 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
 	return 0;
 }
+
+static inline void ima_shm_check(struct file *file)
+{
+	return;
+}
 #endif /* CONFIG_IMA_H */
 #endif /* _LINUX_IMA_H */
diff --git a/ipc/shm.c b/ipc/shm.c
index a9e09ad..5604bc8 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -39,6 +39,7 @@
 #include <linux/nsproxy.h>
 #include <linux/mount.h>
 #include <linux/ipc_namespace.h>
+#include <linux/ima.h>
 
 #include <asm/uaccess.h>
 
@@ -381,6 +382,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 	error = PTR_ERR(file);
 	if (IS_ERR(file))
 		goto no_file;
+	ima_shm_check(file);
 
 	id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
 	if (id < 0) {
@@ -881,6 +883,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
 	file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
 	if (!file)
 		goto out_free;
+	ima_shm_check(file);
 
 	file->private_data = sfd;
 	file->f_mapping = shp->shm_file->f_mapping;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 5b72cdb..ed953fb 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -98,6 +98,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
 
 /* iint cache flags */
 #define IMA_MEASURED		1
+#define IMA_IINT_DUMP_STACK	512
 
 /* integrity data associated with an inode */
 struct ima_iint_cache {
@@ -107,6 +108,7 @@ struct ima_iint_cache {
 	struct mutex mutex;	/* protects: version, flags, digest */
 	long readcount;		/* measured files readcount */
 	long writecount;	/* measured files writecount */
+	long opencount;		/* opens reference count */
 	struct kref refcount;	/* ima_iint_cache reference count */
 	struct rcu_head rcu;
 };
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index 750db3c..1f035e8 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -126,6 +126,7 @@ struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
 
 	return iint;
 }
+EXPORT_SYMBOL_GPL(ima_iint_find_insert_get);
 
 /* iint_free - called when the iint refcount goes to zero */
 void iint_free(struct kref *kref)
@@ -134,6 +135,21 @@ void iint_free(struct kref *kref)
 						   refcount);
 	iint->version = 0;
 	iint->flags = 0UL;
+	if (iint->readcount != 0) {
+		printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+		       iint->readcount);
+		iint->readcount = 0;
+	}
+	if (iint->writecount != 0) {
+		printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+		       iint->writecount);
+		iint->writecount = 0;
+	}
+	if (iint->opencount != 0) {
+		printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+		       iint->opencount);
+		iint->opencount = 0;
+	}
 	kref_set(&iint->refcount, 1);
 	kmem_cache_free(iint_cache, iint);
 }
@@ -174,6 +190,7 @@ static void init_once(void *foo)
 	mutex_init(&iint->mutex);
 	iint->readcount = 0;
 	iint->writecount = 0;
+	iint->opencount = 0;
 	kref_set(&iint->refcount, 1);
 }
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 871e356..751a5d2 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -66,6 +66,19 @@ void ima_file_free(struct file *file)
 		return;
 
 	mutex_lock(&iint->mutex);
+	if (iint->opencount <= 0) {
+		printk(KERN_INFO
+		       "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n",
+		       __FUNCTION__, file->f_dentry->d_name.name,
+		       iint->readcount, iint->writecount,
+		       iint->opencount, atomic_long_read(&file->f_count));
+		if (!(iint->flags & IMA_IINT_DUMP_STACK)) {
+			dump_stack();
+			iint->flags |= IMA_IINT_DUMP_STACK;
+		}
+	}
+	iint->opencount--;
+
 	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
 		iint->readcount--;
 
@@ -119,6 +132,7 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
 		pr_info("%s dentry_open failed\n", filename);
 		return rc;
 	}
+	iint->opencount++;
 	iint->readcount++;
 
 	rc = ima_collect_measurement(iint, file);
@@ -159,6 +173,7 @@ int ima_path_check(struct path *path, int mask)
 		return 0;
 
 	mutex_lock(&iint->mutex);
+	iint->opencount++;
 	if ((mask & MAY_WRITE) || (mask == 0))
 		iint->writecount++;
 	else if (mask & (MAY_READ | MAY_EXEC))
@@ -219,6 +234,20 @@ out:
 	return rc;
 }
 
+static void opencount_get(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (!ima_initialized || !S_ISREG(inode->i_mode))
+		return;
+	iint = ima_iint_find_insert_get(inode);
+	mutex_lock(&iint->mutex);
+	if (iint)
+		iint->opencount++;
+	mutex_unlock(&iint->mutex);
+}
+
 /**
  * ima_file_mmap - based on policy, collect/store measurement.
  * @file: pointer to the file to be measured (May be NULL)
@@ -242,6 +271,18 @@ int ima_file_mmap(struct file *file, unsigned long prot)
 	return 0;
 }
 
+/*
+ * ima_shm_check - IPC shm and shmat create/fput a file
+ *
+ * Maintain the opencount for these files to prevent unnecessary
+ * imbalance messages.
+ */
+void ima_shm_check(struct file *file)
+{
+	opencount_get(file);
+	return;
+}
+
 /**
  * ima_bprm_check - based on policy, collect/store measurement.
  * @bprm: contains the linux_binprm structure
-- 
1.5.6.6


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
@ 2009-01-30  0:07   ` James Morris
  2009-01-30 19:29     ` Mimi Zohar
  2009-01-30  9:04   ` James Morris
  2009-02-02 23:02   ` Serge E. Hallyn
  2 siblings, 1 reply; 27+ messages in thread
From: James Morris @ 2009-01-30  0:07 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Thu, 29 Jan 2009, Mimi Zohar wrote:

> +++ b/security/integrity/ima/Kconfig
> @@ -0,0 +1,49 @@
> +# IBM Integrity Measurement Architecture
> +#
> +config IMA
> +	bool "Integrity Measurement Architecture(IMA)"
> +	depends on ACPI
> +	select SECURITYFS
> +	select CRYPTO
> +	select CRYPTO_HMAC
> +	select CRYPTO_MD5
> +	select CRYPTO_SHA1
> +	select TCG_TPM
> +	select TCG_TIS
> +	help
> +	  The Trusted Computing Group(TCG) runtime Integrity
> +	  Measurement Architecture(IMA) maintains a list of hash
> +	  values of executables and other sensitive system files,
> +	  as they are read or executed. If an attacker manages
> +	  to change the contents of an important system file
> +	  being measured, we can tell.

Out of interest, do you know if the TCG has analyzed their use of SHA-1 
in light of various attacks on the algorithm over the last few years?

The IETF has published analysis and recommendations relating to the use of 
SHA-1 (and other cryptographic hashes) with IP protocols:

http://www.ietf.org/rfc/rfc4894.txt
http://www.ietf.org/rfc/rfc4270.txt

It would be useful to know if similar analysis has been performed for TPM.


- James
-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
  2009-01-30  0:07   ` James Morris
@ 2009-01-30  9:04   ` James Morris
  2009-01-30 13:13     ` Mimi Zohar
  2009-02-02 23:02   ` Serge E. Hallyn
  2 siblings, 1 reply; 27+ messages in thread
From: James Morris @ 2009-01-30  9:04 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Thu, 29 Jan 2009, Mimi Zohar wrote:

> +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
> +{
> +	struct ima_queue_entry *qe, *ret = NULL;
> +	unsigned int key;
> +	struct hlist_node *pos;
> +
> +	key = ima_hash_key(digest_value);
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
> +		if (memcmp(qe->entry->digest, digest_value, 20) == 0) {

Minor issue: '20' should be IMA_DIGEST_SIZE, right?


- James
-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/6] integrity: IMA display
  2009-01-29 22:23 ` [PATCH 3/6] integrity: IMA display Mimi Zohar
@ 2009-01-30  9:18   ` James Morris
  2009-01-30 13:14     ` Mimi Zohar
  0 siblings, 1 reply; 27+ messages in thread
From: James Morris @ 2009-01-30  9:18 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Thu, 29 Jan 2009, Mimi Zohar wrote:

> +int ima_fs_init(void)
> +{
> +	ima_dir = securityfs_create_dir("ima", NULL);
> +	if (!ima_dir || IS_ERR(ima_dir))
> +		return -1;
> +
> +	binary_runtime_measurements =
> +	    securityfs_create_file("binary_runtime_measurements",
> +				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> +				   &ima_measurements_ops);
> +	if (!binary_runtime_measurements || IS_ERR(binary_runtime_measurements))
> +		goto out;

You should not be checking for NULL returns from securityfs_create_file(), 
because it does not return NULL (the documentation is wrong).


- James
-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-30  9:04   ` James Morris
@ 2009-01-30 13:13     ` Mimi Zohar
  0 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-30 13:13 UTC (permalink / raw)
  To: James Morris
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Fri, 2009-01-30 at 20:04 +1100, James Morris wrote:
> On Thu, 29 Jan 2009, Mimi Zohar wrote:
> 
> > +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
> > +{
> > +	struct ima_queue_entry *qe, *ret = NULL;
> > +	unsigned int key;
> > +	struct hlist_node *pos;
> > +
> > +	key = ima_hash_key(digest_value);
> > +	rcu_read_lock();
> > +	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
> > +		if (memcmp(qe->entry->digest, digest_value, 20) == 0) {
> 
> Minor issue: '20' should be IMA_DIGEST_SIZE, right?
> 
> 
> - James

Right.

Mimi


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/6] integrity: IMA display
  2009-01-30  9:18   ` James Morris
@ 2009-01-30 13:14     ` Mimi Zohar
  2009-02-02 23:14       ` Serge E. Hallyn
  0 siblings, 1 reply; 27+ messages in thread
From: Mimi Zohar @ 2009-01-30 13:14 UTC (permalink / raw)
  To: James Morris
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Fri, 2009-01-30 at 20:18 +1100, James Morris wrote:
> On Thu, 29 Jan 2009, Mimi Zohar wrote:
> 
> > +int ima_fs_init(void)
> > +{
> > +	ima_dir = securityfs_create_dir("ima", NULL);
> > +	if (!ima_dir || IS_ERR(ima_dir))
> > +		return -1;
> > +
> > +	binary_runtime_measurements =
> > +	    securityfs_create_file("binary_runtime_measurements",
> > +				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> > +				   &ima_measurements_ops);
> > +	if (!binary_runtime_measurements || IS_ERR(binary_runtime_measurements))
> > +		goto out;
> 
> You should not be checking for NULL returns from securityfs_create_file(), 
> because it does not return NULL (the documentation is wrong).
> 
> 
> - James

Thanks!

Mimi


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-30  0:07   ` James Morris
@ 2009-01-30 19:29     ` Mimi Zohar
  0 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-01-30 19:29 UTC (permalink / raw)
  To: James Morris
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn, Mimi Zohar

On Fri, 2009-01-30 at 11:07 +1100, James Morris wrote:
> On Thu, 29 Jan 2009, Mimi Zohar wrote:
> 
> > +++ b/security/integrity/ima/Kconfig
> > @@ -0,0 +1,49 @@
> > +# IBM Integrity Measurement Architecture
> > +#
> > +config IMA
> > +	bool "Integrity Measurement Architecture(IMA)"
> > +	depends on ACPI
> > +	select SECURITYFS
> > +	select CRYPTO
> > +	select CRYPTO_HMAC
> > +	select CRYPTO_MD5
> > +	select CRYPTO_SHA1
> > +	select TCG_TPM
> > +	select TCG_TIS
> > +	help
> > +	  The Trusted Computing Group(TCG) runtime Integrity
> > +	  Measurement Architecture(IMA) maintains a list of hash
> > +	  values of executables and other sensitive system files,
> > +	  as they are read or executed. If an attacker manages
> > +	  to change the contents of an important system file
> > +	  being measured, we can tell.
> 
> Out of interest, do you know if the TCG has analyzed their use of SHA-1 
> in light of various attacks on the algorithm over the last few years?
> 
> The IETF has published analysis and recommendations relating to the use of 
> SHA-1 (and other cryptographic hashes) with IP protocols:
> 
> http://www.ietf.org/rfc/rfc4894.txt
> http://www.ietf.org/rfc/rfc4270.txt
> 
> It would be useful to know if similar analysis has been performed for TPM.
> 
> 
> - James

Sorry, the TCG does not have a postion paper on this.  We do not see
the current SHA-1 collision weakness being applicable to how the TPM
is currently being used by IMA.  If, however, it does becomes an issue,
we could replace the SHA-1 template measurement with SHA-256.

Mimi


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
  2009-01-30  0:07   ` James Morris
  2009-01-30  9:04   ` James Morris
@ 2009-02-02 23:02   ` Serge E. Hallyn
  2009-02-03  2:09     ` Mimi Zohar
  2009-02-03 13:36     ` david safford
  2 siblings, 2 replies; 27+ messages in thread
From: Serge E. Hallyn @ 2009-02-02 23:02 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> IMA provides hardware (TPM) based measurement and attestation for
> file measurements. As the Trusted Computing (TPM) model requires,
> IMA measures all files before they are accessed in any way (on the
> integrity_bprm_check, integrity_path_check and integrity_file_mmap
> hooks), and commits the measurements to the TPM. Once added to the
> TPM, measurements can not be removed.
> 
> In addition, IMA maintains a list of these file measurements, which
> can be used to validate the aggregate value stored in the TPM.  The
> TPM can sign these measurements, and thus the system can prove, to
> itself and to a third party, the system's integrity in a way that
> cannot be circumvented by malicious or compromised software.
> 
> - removed LIM hooks and API registration; IMA is now called directly
> - added slab for integrity information(iint) associated with an inode
> - added a local read and write count in iint
> - lots of code refactoring (i.e. calculating the boot aggregate,
>   flagging openwriters/ToMToU)
> - statically defined and initialized variables
> - addressed the credential merge changes
> - addressed locking issues in general (i.e. ima_file_free())
>   and addressed the rcu-locking problem in ima_iint_delete() in particular
> - removed caching of measurement policy results
> - removed Kconfig prompt for: pcr index, informational audit messages
> 
> Signed-off-by: Mimi Zohar <zohar@us.ibm.com>

I notice there is no MAINTAINERS entry for IMA?

A few comments below.  Aside from my comments in ima_bprm_check and
ima_path_check,

Acked-by: Serge Hallyn <serue@us.ibm.com>

> ---
> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
> index 40c51e9..8cc40a1 100644
> --- a/Documentation/kernel-parameters.txt
> +++ b/Documentation/kernel-parameters.txt
> @@ -901,6 +901,15 @@ and is between 256 and 4096 characters. It is defined in the file
>  	ihash_entries=	[KNL]
>  			Set number of hash buckets for inode cache.
>  
> +	ima_audit=	[IMA]
> +			Format: { "0" | "1" }
> +			0 -- integrity auditing messages. (Default)
> +			1 -- enable informational integrity auditing messages.
> +
> +	ima_hash=	[IMA]
> +			Formt: { "sha1" | "md5" }
> +			default: "sha1"
> +
>  	in2000=		[HW,SCSI]
>  			See header of drivers/scsi/in2000.c.
>  
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 67e5dbf..930939a 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -125,6 +125,11 @@
>  #define AUDIT_LAST_KERN_ANOM_MSG    1799
>  #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
>  #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
> +#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
> +#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
> +#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
> +#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
> +#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
>  
>  #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
>  
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 4ed1e4d..dcc3664 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -12,6 +12,15 @@
>  #ifndef _LINUX_IMA_H
>  #define _LINUX_IMA_H
>  
> +#ifdef CONFIG_IMA
> +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_path_check(struct path *path, int mask);
> +extern void ima_file_free(struct file *file);
> +extern int ima_file_mmap(struct file *file, unsigned long prot);
> +
> +#else
>  static inline int ima_bprm_check(struct linux_binprm *bprm)
>  {
>  	return 0;
> @@ -41,4 +50,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
>  {
>  	return 0;
>  }
> +#endif /* CONFIG_IMA_H */
>  #endif /* _LINUX_IMA_H */
> diff --git a/security/Kconfig b/security/Kconfig
> index 9438535..bf129f8 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -55,7 +55,8 @@ config SECURITYFS
>  	bool "Enable the securityfs filesystem"
>  	help
>  	  This will build the securityfs filesystem.  It is currently used by
> -	  the TPM bios character driver.  It is not used by SELinux or SMACK.
> +	  the TPM bios character driver and IMA, an integrity provider.  It is
> +	  not used by SELinux or SMACK.
>  
>  	  If you are unsure how to answer this question, answer N.
>  
> @@ -135,5 +136,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
>  source security/selinux/Kconfig
>  source security/smack/Kconfig
>  
> +source security/integrity/ima/Kconfig
> +
>  endmenu
>  
> diff --git a/security/Makefile b/security/Makefile
> index c05c127..595536c 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
>  obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
>  obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
>  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
> +
> +# Object integrity file lists
> +subdir-$(CONFIG_IMA)			+= integrity/ima
> +obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> new file mode 100644
> index 0000000..2a761c8
> --- /dev/null
> +++ b/security/integrity/ima/Kconfig
> @@ -0,0 +1,49 @@
> +# IBM Integrity Measurement Architecture
> +#
> +config IMA
> +	bool "Integrity Measurement Architecture(IMA)"
> +	depends on ACPI
> +	select SECURITYFS
> +	select CRYPTO
> +	select CRYPTO_HMAC
> +	select CRYPTO_MD5
> +	select CRYPTO_SHA1
> +	select TCG_TPM
> +	select TCG_TIS
> +	help
> +	  The Trusted Computing Group(TCG) runtime Integrity
> +	  Measurement Architecture(IMA) maintains a list of hash
> +	  values of executables and other sensitive system files,
> +	  as they are read or executed. If an attacker manages
> +	  to change the contents of an important system file
> +	  being measured, we can tell.
> +
> +	  If your system has a TPM chip, then IMA also maintains
> +	  an aggregate integrity value over this list inside the
> +	  TPM hardware, so that the TPM can prove to a third party
> +	  whether or not critical system files have been modified.
> +	  Read <http://www.usenix.org/events/sec04/tech/sailer.html>
> +	  to learn more about IMA.
> +	  If unsure, say N.
> +
> +config IMA_MEASURE_PCR_IDX
> +	int
> +	depends on IMA
> +	range 8 14
> +	default 10
> +	help
> +	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
> +	  that IMA uses to maintain the integrity aggregate of the
> +	  measurement list.  If unsure, use the default 10.
> +
> +config IMA_AUDIT
> +	bool
> +	depends on IMA
> +	default y
> +	help
> +	  This option adds a kernel parameter 'ima_audit', which
> +	  allows informational auditing messages to be enabled
> +	  at boot.  If this option is selected, informational integrity
> +	  auditing messages can be enabled with 'ima_audit=1' on
> +	  the kernel command line.
> +
> diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
> new file mode 100644
> index 0000000..9d6bf97
> --- /dev/null
> +++ b/security/integrity/ima/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
> +# Measurement Architecture(IMA).
> +#
> +
> +obj-$(CONFIG_IMA) += ima.o
> +
> +ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
> +	 ima_policy.o ima_iint.o ima_audit.o
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> new file mode 100644
> index 0000000..04a397e
> --- /dev/null
> +++ b/security/integrity/ima/ima.h
> @@ -0,0 +1,136 @@
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * 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.h
> + *	internal Integrity Measurement Architecture (IMA) definitions
> + */
> +
> +#ifndef __LINUX_IMA_H
> +#define __LINUX_IMA_H
> +
> +#include <linux/types.h>
> +#include <linux/crypto.h>
> +#include <linux/security.h>
> +#include <linux/hash.h>
> +#include <linux/tpm.h>
> +#include <linux/audit.h>
> +
> +enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
> +enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
> +
> +/* digest size for IMA, fits SHA1 or MD5 */
> +#define IMA_DIGEST_SIZE		20
> +#define IMA_EVENT_NAME_LEN_MAX	255
> +
> +#define IMA_HASH_BITS 9
> +#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
> +
> +/* set during initialization */
> +extern int ima_initialized;
> +extern int ima_used_chip;
> +extern char *ima_hash;
> +
> +/* IMA inode template definition */
> +struct ima_template_data {
> +	u8 digest[IMA_DIGEST_SIZE];	/* sha1/md5 measurement hash */
> +	char file_name[IMA_EVENT_NAME_LEN_MAX + 1];	/* name + \0 */
> +};
> +
> +#define IMA_TEMPLATE_NAME_LEN_MAX 20

No longer used

> +struct ima_template_entry {
> +	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */
> +	char *template_name;
> +	int template_len;
> +	struct ima_template_data template;
> +};
> +
> +struct ima_queue_entry {
> +	struct hlist_node hnext;	/* place in hash collision list */
> +	struct list_head later;		/* place in ima_measurements list */
> +	struct ima_template_entry *entry;
> +};
> +extern struct list_head ima_measurements;	/* list of all measurements */
> +
> +/* declarations */
> +void integrity_audit_msg(int audit_msgno, struct inode *inode,
> +			 const unsigned char *fname, const char *op,
> +			 const char *cause, int result, int info);
> +
> +/* Internal IMA function definitions */
> +void ima_iintcache_init(void);
> +int ima_init(void);
> +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> +			   const char *op, struct inode *inode);
> +int ima_calc_hash(struct file *file, char *digest);
> +int ima_calc_template_hash(int template_len, void *template, char *digest);
> +int ima_calc_boot_aggregate(char *digest);
> +void ima_add_violation(struct inode *inode, const unsigned char *filename,
> +		       const char *op, const char *cause);
> +
> +/*
> + * used to protect h_table and sha_table
> + */
> +extern spinlock_t ima_queue_lock;
> +
> +struct ima_h_table {
> +	atomic_long_t len;	/* number of stored measurements in the list */
> +	atomic_long_t violations;
> +	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
> +};
> +extern struct ima_h_table ima_htable;
> +
> +static inline unsigned long ima_hash_key(u8 *digest)
> +{
> +	return hash_long(*digest, IMA_HASH_BITS);
> +}
> +
> +/* iint cache flags */
> +#define IMA_MEASURED		1
> +
> +/* integrity data associated with an inode */
> +struct ima_iint_cache {
> +	u64 version;		/* track inode changes */
> +	unsigned long flags;
> +	u8 digest[IMA_DIGEST_SIZE];
> +	struct mutex mutex;	/* protects: version, flags, digest */
> +	long readcount;		/* measured files readcount */
> +	long writecount;	/* measured files writecount */
> +	struct kref refcount;	/* ima_iint_cache reference count */
> +	struct rcu_head rcu;
> +};
> +
> +/* LIM API function definitions */
> +int ima_must_measure(struct ima_iint_cache *iint, 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,
> +			   const unsigned char *filename);
> +int ima_store_template(struct ima_template_data *data, int violation,
> +		       struct inode *inode);
> +
> +/* radix tree calls to lookup, insert, delete
> + * integrity data associated with an inode.
> + */
> +struct ima_iint_cache *ima_iint_insert(struct inode *inode);
> +struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
> +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
> +void ima_iint_delete(struct inode *inode);
> +void iint_free(struct kref *kref);
> +void iint_rcu_free(struct rcu_head *rcu);
> +
> +/* IMA policy related functions */
> +enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
> +
> +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
> +void ima_init_policy(void);
> +void ima_update_policy(void);
> +#endif
> diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
> new file mode 100644
> index 0000000..7c1db11
> --- /dev/null
> +++ b/security/integrity/ima/ima_api.c
> @@ -0,0 +1,185 @@
> +/*
> + * Copyright (C) 2008 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.
> + *
> + * File: ima_api.c
> + *	Implements must_measure, collect_measurement, store_measurement,
> + *	and store_template.
> + */
> +#include <linux/module.h>
> +
> +#include "ima.h"
> +static char *IMA_TEMPLATE_NAME = "ima";
> +
> +/*
> + * ima_store_template - store ima template measurements
> + *
> + * Calculate the hash of a template entry, add the template entry
> + * to an ordered list of measurement entries maintained inside the kernel,
> + * and also update the aggregate integrity value (maintained inside the
> + * configured TPM PCR) over the hashes of the current list of measurement
> + * entries.
> + *
> + * Applications retrieve the current kernel-held measurement list through
> + * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
> + * TPM PCR (called quote) can be retrieved using a TPM user space library
> + * and is used to validate the measurement list.
> + *
> + * Returns 0 on success, error code otherwise
> + */
> +int ima_store_template(struct ima_template_data *template,
> +		       int violation, struct inode *inode)
> +{
> +	struct ima_template_entry *entry;
> +	const char *op = "add_template_measure";
> +	const char *audit_cause = "ENOMEM";
> +	int result = -ENOMEM;
> +
> +	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
> +	if (!entry)
> +		goto err_out;
> +
> +	memcpy(&entry->template, template, sizeof(*template));
> +	memset(&entry->digest, 0, sizeof(entry->digest));
> +	entry->template_name = IMA_TEMPLATE_NAME;
> +	entry->template_len = sizeof(*template);
> +
> +	if (!violation) {
> +		result = ima_calc_template_hash(entry->template_len,
> +						template, entry->digest);
> +		if (result < 0) {
> +			kfree(entry);
> +			audit_cause = "hashing_error";
> +			goto err_out;
> +		}
> +	}
> +	result = ima_add_template_entry(entry, violation, op, inode);
> +	if (result < 0)
> +		kfree(entry);
> +	return result;
> +
> +err_out:
> +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
> +			    op, audit_cause, result, 0);
> +
> +	return result;
> +}
> +
> +/*
> + * ima_add_violation - add violation to measurement list.
> + *
> + * Violations are flagged in the measurement list with zero hash values.
> + * By extending the PCR with 0xFF's instead of with zeroes, the PCR
> + * value is invalidated.
> + */
> +void ima_add_violation(struct inode *inode, const unsigned char *filename,
> +		       const char *op, const char *cause)
> +{
> +	struct ima_template_data template;
> +	int violation = 1;
> +	int result;
> +
> +	/* can overflow, only indicator */
> +	atomic_long_inc(&ima_htable.violations);
> +
> +	memset(&template, 0, sizeof(template));
> +	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
> +	result = ima_store_template(&template, violation, inode);
> +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
> +			    op, cause, result, 0);
> +}
> +
> +/**
> + * ima_must_measure - measure decision based on policy.
> + * @inode: pointer to inode to measure
> + * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
> + * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
> + *
> + * The policy is defined in terms of keypairs:
> + * 		subj=, obj=, type=, func=, mask=, fsmagic=
> + *	subj,obj, and type: are LSM specific.
> + * 	func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
> + * 	mask: contains the permission mask
> + *	fsmagic: hex value
> + *
> + * Must be called with iint->mutex held.
> + *
> + * Return 0 to measure. Return 1 if already measured.
> + * For matching a DONT_MEASURE policy, no policy, or other
> + * error, return an error code.
> +*/
> +int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
> +		     int mask, int function)
> +{
> +	int must_measure;
> +
> +	if (iint->flags & IMA_MEASURED)
> +		return 1;
> +
> +	must_measure = ima_match_policy(inode, function, mask);
> +	return must_measure ? 0 : -EACCES;
> +}
> +
> +/*
> + * ima_collect_measurement - collect file measurement
> + *
> + * Calculate the file hash, if it doesn't already exist,
> + * storing the measurement and i_version in the iint.
> + *
> + * Must be called with iint->mutex held.
> + *
> + * Return 0 on success, error code otherwise
> + */
> +int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
> +{
> +	int result = -EEXIST;
> +
> +	if (!(iint->flags & IMA_MEASURED)) {
> +		u64 i_version = file->f_dentry->d_inode->i_version;
> +
> +		memset(iint->digest, 0, IMA_DIGEST_SIZE);
> +		result = ima_calc_hash(file, iint->digest);
> +		if (!result)
> +			iint->version = i_version;
> +	}
> +	return result;
> +}
> +
> +/*
> + * ima_store_measurement - store file measurement
> + *
> + * Create an "ima" template and then store the template by calling
> + * ima_store_template.
> + *
> + * We only get here if the inode has not already been measured,
> + * but the measurement could already exist:
> + * 	- multiple copies of the same file on either the same or
> + *	  different filesystems.
> + *	- the inode was previously flushed as well as the iint info,
> + *	  containing the hashing info.
> + *
> + * Must be called with iint->mutex held.
> + */
> +void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
> +			   const unsigned char *filename)
> +{
> +
> +	struct inode *inode = file->f_dentry->d_inode;
> +	struct ima_template_data template;
> +	int violation = 0;
> +	int result;
> +
> +	memset(&template, 0, sizeof(template));
> +	memcpy(template.digest, iint->digest, IMA_DIGEST_SIZE);
> +	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
> +
> +	result = ima_store_template(&template, violation, inode);

Is there any advantage to copying the data onto the
stack here and passing that to ima_store_template(),
instead of just kmallocing a ima_template_entry right
here, just filling in the digest and file_name, and
passing those to iam_store_template()?

> +	if (!result)
> +		iint->flags |= IMA_MEASURED;
> +}
> diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
> new file mode 100644
> index 0000000..8a0f1e2
> --- /dev/null
> +++ b/security/integrity/ima/ima_audit.c
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright (C) 2008 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.
> + *
> + * File: integrity_audit.c
> + * 	Audit calls for the integrity subsystem
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/audit.h>
> +#include "ima.h"
> +
> +static int ima_audit;
> +
> +#ifdef CONFIG_IMA_AUDIT
> +
> +/* ima_audit_setup - enable informational auditing messages */
> +static int __init ima_audit_setup(char *str)
> +{
> +	unsigned long audit;
> +	int rc;
> +	char *op;
> +
> +	rc = strict_strtoul(str, 0, &audit);
> +	if (rc || audit > 1)
> +		printk(KERN_INFO "ima: invalid ima_audit value\n");
> +	else
> +		ima_audit = audit;
> +	op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled";
> +	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0);
> +	return 1;
> +}
> +__setup("ima_audit=", ima_audit_setup);
> +#endif
> +
> +void integrity_audit_msg(int audit_msgno, struct inode *inode,
> +			 const unsigned char *fname, const char *op,
> +			 const char *cause, int result, int audit_info)
> +{
> +	struct audit_buffer *ab;
> +
> +	if (!ima_audit && audit_info == 1) /* Skip informational messages */
> +		return;
> +
> +	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
> +	audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u",
> +			 current->pid, current->cred->uid,
> +			 audit_get_loginuid(current));
> +	audit_log_task_context(ab);
> +	switch (audit_msgno) {
> +	case AUDIT_INTEGRITY_DATA:
> +	case AUDIT_INTEGRITY_METADATA:
> +	case AUDIT_INTEGRITY_PCR:
> +		audit_log_format(ab, " op=%s cause=%s", op, cause);
> +		break;
> +	case AUDIT_INTEGRITY_HASH:
> +		audit_log_format(ab, " op=%s hash=%s", op, cause);
> +		break;
> +	case AUDIT_INTEGRITY_STATUS:
> +	default:
> +		audit_log_format(ab, " op=%s", op);
> +	}
> +	audit_log_format(ab, " comm=");
> +	audit_log_untrustedstring(ab, current->comm);
> +	if (fname) {
> +		audit_log_format(ab, " name=");
> +		audit_log_untrustedstring(ab, fname);
> +	}
> +	if (inode)
> +		audit_log_format(ab, " dev=%s ino=%lu",
> +				 inode->i_sb->s_id, inode->i_ino);
> +	audit_log_format(ab, " res=%d", result);
> +	audit_log_end(ab);
> +}
> diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
> new file mode 100644
> index 0000000..c2a46e4
> --- /dev/null
> +++ b/security/integrity/ima/ima_crypto.c
> @@ -0,0 +1,140 @@
> +/*
> + * Copyright (C) 2005,2006,2007,2008 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: ima_crypto.c
> + * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/file.h>
> +#include <linux/crypto.h>
> +#include <linux/scatterlist.h>
> +#include <linux/err.h>
> +#include "ima.h"
> +
> +static int init_desc(struct hash_desc *desc)
> +{
> +	int rc;
> +
> +	desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
> +	if (IS_ERR(desc->tfm)) {
> +		pr_info("failed to load %s transform: %ld\n",
> +			ima_hash, PTR_ERR(desc->tfm));
> +		rc = PTR_ERR(desc->tfm);
> +		return rc;
> +	}
> +	desc->flags = 0;
> +	rc = crypto_hash_init(desc);
> +	if (rc)
> +		crypto_free_hash(desc->tfm);
> +	return rc;
> +}
> +
> +/*
> + * Calculate the MD5/SHA1 file digest
> + */
> +int ima_calc_hash(struct file *file, char *digest)
> +{
> +	struct hash_desc desc;
> +	struct scatterlist sg[1];
> +	loff_t i_size;
> +	char *rbuf;
> +	int rc, offset = 0;
> +
> +	rc = init_desc(&desc);
> +	if (rc != 0)
> +		return rc;
> +
> +	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!rbuf) {
> +		rc = -ENOMEM;
> +		goto out;
> +	}
> +	i_size = i_size_read(file->f_dentry->d_inode);
> +	while (offset < i_size) {
> +		int rbuf_len;
> +
> +		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
> +		if (rbuf_len < 0) {
> +			rc = rbuf_len;
> +			break;
> +		}
> +		offset += rbuf_len;
> +		sg_set_buf(sg, rbuf, rbuf_len);
> +
> +		rc = crypto_hash_update(&desc, sg, rbuf_len);
> +		if (rc)
> +			break;
> +	}
> +	kfree(rbuf);
> +	if (!rc)
> +		rc = crypto_hash_final(&desc, digest);
> +out:
> +	crypto_free_hash(desc.tfm);
> +	return rc;
> +}
> +
> +/*
> + * Calculate the hash of a given template
> + */
> +int ima_calc_template_hash(int template_len, void *template, char *digest)
> +{
> +	struct hash_desc desc;
> +	struct scatterlist sg[1];
> +	int rc;
> +
> +	rc = init_desc(&desc);
> +	if (rc != 0)
> +		return rc;
> +
> +	sg_set_buf(sg, template, template_len);
> +	rc = crypto_hash_update(&desc, sg, template_len);
> +	if (!rc)
> +		rc = crypto_hash_final(&desc, digest);
> +	crypto_free_hash(desc.tfm);
> +	return rc;
> +}
> +
> +static void ima_pcrread(int idx, u8 *pcr)
> +{
> +	if (!ima_used_chip)
> +		return;
> +
> +	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
> +		pr_err("Error Communicating to TPM chip\n");
> +}
> +
> +/*
> + * Calculate the boot aggregate hash
> + */
> +int ima_calc_boot_aggregate(char *digest)
> +{
> +	struct hash_desc desc;
> +	struct scatterlist sg;
> +	u8 pcr_i[IMA_DIGEST_SIZE];
> +	int rc, i;
> +
> +	rc = init_desc(&desc);
> +	if (rc != 0)
> +		return rc;
> +
> +	/* cumulative sha1 over tpm registers 0-7 */

(Just out of curiosity) why?  Are these the ones the BIOS and grub
will use by convention?

> +	for (i = TPM_PCR0; i < TPM_PCR8; i++) {
> +		ima_pcrread(i, pcr_i);
> +		/* now accumulate with current aggregate */
> +		sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
> +		rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
> +	}
> +	if (!rc)
> +		crypto_hash_final(&desc, digest);
> +	crypto_free_hash(desc.tfm);
> +	return rc;
> +}
> diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
> new file mode 100644
> index 0000000..750db3c
> --- /dev/null
> +++ b/security/integrity/ima/ima_iint.c
> @@ -0,0 +1,185 @@
> +/*
> + * 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 radix tree.
> + */
> +#include <linux/module.h>
> +#include <linux/spinlock.h>
> +#include <linux/radix-tree.h>
> +#include "ima.h"
> +
> +#define ima_iint_delete ima_inode_free
> +
> +RADIX_TREE(ima_iint_store, GFP_ATOMIC);
> +DEFINE_SPINLOCK(ima_iint_lock);
> +
> +static struct kmem_cache *iint_cache __read_mostly;
> +
> +/* ima_iint_find_get - return the iint associated with an inode
> + *
> + * ima_iint_find_get gets a reference to the iint. Caller must
> + * remember to put the iint reference.
> + */
> +struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
> +{
> +	struct ima_iint_cache *iint;
> +
> +	rcu_read_lock();
> +	iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
> +	if (!iint)
> +		goto out;
> +	kref_get(&iint->refcount);
> +out:
> +	rcu_read_unlock();
> +	return iint;
> +}
> +
> +/* Allocate memory for the iint associated with the inode
> + * from the iint_cache slab, initialize the iint, and
> + * insert it into the radix tree.
> + *
> + * On success return a pointer to the iint; on failure return NULL.
> + */
> +struct ima_iint_cache *ima_iint_insert(struct inode *inode)
> +{
> +	struct ima_iint_cache *iint = NULL;
> +	int rc = 0;
> +
> +	if (!ima_initialized)
> +		return iint;
> +	iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
> +	if (!iint)
> +		return iint;
> +
> +	rc = radix_tree_preload(GFP_KERNEL);
> +	if (rc < 0)
> +		goto out;
> +
> +	spin_lock(&ima_iint_lock);
> +	rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
> +	spin_unlock(&ima_iint_lock);
> +out:
> +	if (rc < 0) {
> +		kmem_cache_free(iint_cache, iint);
> +		if (rc == -EEXIST) {
> +			iint = radix_tree_lookup(&ima_iint_store,
> +						 (unsigned long)inode);
> +		} else
> +			iint = NULL;
> +	}
> +	radix_tree_preload_end();
> +	return iint;
> +}
> +
> +/**
> + * ima_inode_alloc - allocate an iint associated with an inode
> + * @inode: pointer to the inode
> + *
> + * Return 0 on success, 1 on failure.
> + */
> +int ima_inode_alloc(struct inode *inode)
> +{
> +	struct ima_iint_cache *iint;
> +
> +	if (!ima_initialized)
> +		return 0;
> +
> +	iint = ima_iint_insert(inode);
> +	if (!iint)
> +		return 1;
> +	return 0;
> +}
> +
> +/* ima_iint_find_insert_get - get the iint associated with an inode
> + *
> + * Most insertions are done at inode_alloc, except those allocated
> + * before late_initcall. When the iint does not exist, allocate it,
> + * initialize and insert it, and increment the iint refcount.
> + *
> + * (Can't initialize at security_initcall before any inodes are
> + * allocated, got to wait at least until proc_init.)
> + *
> + *  Return the iint.
> + */
> +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
> +{
> +	struct ima_iint_cache *iint = NULL;
> +
> +	iint = ima_iint_find_get(inode);
> +	if (iint)
> +		return iint;
> +
> +	iint = ima_iint_insert(inode);
> +	if (iint)
> +		kref_get(&iint->refcount);
> +
> +	return iint;
> +}
> +
> +/* iint_free - called when the iint refcount goes to zero */
> +void iint_free(struct kref *kref)
> +{
> +	struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
> +						   refcount);
> +	iint->version = 0;
> +	iint->flags = 0UL;
> +	kref_set(&iint->refcount, 1);
> +	kmem_cache_free(iint_cache, iint);
> +}
> +
> +void iint_rcu_free(struct rcu_head *rcu_head)
> +{
> +	struct ima_iint_cache *iint = container_of(rcu_head,
> +						   struct ima_iint_cache, rcu);
> +	kref_put(&iint->refcount, iint_free);
> +}
> +
> +/**
> + * ima_iint_delete - called on integrity_inode_free
> + * @inode: pointer to the inode
> + *
> + * Free the integrity information(iint) associated with an inode.
> + */
> +void ima_iint_delete(struct inode *inode)
> +{
> +	struct ima_iint_cache *iint;
> +
> +	if (!ima_initialized)
> +		return;
> +	spin_lock(&ima_iint_lock);
> +	iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
> +	spin_unlock(&ima_iint_lock);
> +	if (iint)
> +		call_rcu(&iint->rcu, iint_rcu_free);
> +}
> +
> +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);
> +	iint->readcount = 0;
> +	iint->writecount = 0;
> +	kref_set(&iint->refcount, 1);
> +}
> +
> +void ima_iintcache_init(void)
> +{
> +	iint_cache =
> +	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
> +			      SLAB_PANIC, init_once);
> +}
> diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
> new file mode 100644
> index 0000000..e681a40
> --- /dev/null
> +++ b/security/integrity/ima/ima_init.c
> @@ -0,0 +1,73 @@
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer      <sailer@watson.ibm.com>
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + * 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_init.c
> + *             initialization and cleanup functions
> + */
> +#include <linux/module.h>
> +#include <linux/scatterlist.h>
> +#include <linux/err.h>
> +#include "ima.h"
> +
> +/* name for boot aggregate entry */
> +static char *boot_aggregate_name = "boot_aggregate";
> +int ima_used_chip;
> +
> +/* Add the boot aggregate to the IMA measurement list and extend
> + * the PCR register.
> + *
> + * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
> + * assuming a TPM chip exists, and zeroes if the TPM chip does not
> + * exist.  Add the boot aggregate measurement to the measurement
> + * list and extend the PCR register.
> + *
> + * If a tpm chip does not exist, indicate the core root of trust is
> + * not hardware based by invalidating the aggregate PCR value.
> + * (The aggregate PCR value is invalidated by adding one value to
> + * the measurement list and extending the aggregate PCR value with
> + * a different value.) Violations add a zero entry to the measurement
> + * list and extend the aggregate PCR value with ff...ff's.
> + */
> +static void ima_add_boot_aggregate(void)
> +{
> +	struct ima_template_data template;
> +	int violation = 1;
> +	int result;
> +
> +	memset(&template, 0, sizeof(template));
> +	strncpy(template.file_name, boot_aggregate_name,
> +		IMA_EVENT_NAME_LEN_MAX);
> +
> +	if (ima_used_chip) {
> +		violation = 0;
> +		ima_calc_boot_aggregate(template.digest);

You're ignoring the return value of ima_calc_boot_aggregate...

> +	}
> +	result = ima_store_template(&template, violation, NULL);

...and effectively ignoring result?

I realize it shouldn't ever matter...

> +}
> +
> +int ima_init(void)
> +{
> +	int rc;
> +
> +	ima_used_chip = 0;
> +	rc = tpm_pcr_read(TPM_ANY_NUM, 0, NULL);
> +	if (rc == 0)
> +		ima_used_chip = 1;
> +
> +	if (!ima_used_chip)
> +		pr_info("No TPM chip found, activating TPM-bypass!\n");
> +
> +	ima_add_boot_aggregate();	/* boot aggregate must be first entry */
> +	ima_init_policy();
> +	return 0;
> +}
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> new file mode 100644
> index 0000000..53cee4c
> --- /dev/null
> +++ b/security/integrity/ima/ima_main.c
> @@ -0,0 +1,280 @@
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * Serge Hallyn <serue@us.ibm.com>
> + * Kylene Hall <kylene@us.ibm.com>
> + * 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_main.c
> + *             implements the IMA hooks: ima_bprm_check, ima_file_mmap,
> + *             and ima_path_check.
> + */
> +#include <linux/module.h>
> +#include <linux/file.h>
> +#include <linux/binfmts.h>
> +#include <linux/mount.h>
> +#include <linux/mman.h>
> +
> +#include "ima.h"
> +
> +int ima_initialized;
> +
> +char *ima_hash = "sha1";
> +static int __init hash_setup(char *str)
> +{
> +	const char *op = "hash_setup";
> +	const char *hash = "sha1";
> +	int result = 0;
> +	int audit_info = 0;
> +
> +	if (strncmp(str, "md5", 3) == 0) {
> +		hash = "md5";
> +		ima_hash = str;
> +	} else if (strncmp(str, "sha1", 4) != 0) {
> +		hash = "invalid_hash_type";
> +		result = 1;
> +	}
> +	integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
> +			    result, audit_info);
> +	return 1;
> +}
> +__setup("ima_hash=", hash_setup);
> +
> +/**
> + * ima_file_free - called on __fput()
> + * @file: pointer to file structure being freed
> + *
> + * Flag files that changed, based on i_version;
> + * and decrement the iint readcount/writecount.
> + */
> +void ima_file_free(struct file *file)
> +{
> +	struct inode *inode = file->f_dentry->d_inode;
> +	struct ima_iint_cache *iint;
> +
> +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> +		return;
> +	iint = ima_iint_find_get(inode);
> +	if (!iint)
> +		return;
> +
> +	mutex_lock(&iint->mutex);
> +	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
> +		iint->readcount--;
> +
> +	if (file->f_mode & FMODE_WRITE) {
> +		iint->writecount--;
> +		if (iint->writecount == 0) {
> +			if (iint->version != inode->i_version)
> +				iint->flags &= ~IMA_MEASURED;
> +		}
> +	}
> +	mutex_unlock(&iint->mutex);
> +	kref_put(&iint->refcount, iint_free);
> +}
> +
> +/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
> + *
> + * When opening a file for read, if the file is already open for write,
> + * the file could change, resulting in a file measurement error.
> + *
> + * Opening a file for write, if the file is already open for read, results
> + * in a time of measure, time of use (ToMToU) error.
> + *
> + * In either case invalidate the PCR.
> + */
> +enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
> +static void ima_read_write_check(enum iint_pcr_error error,
> +				 struct ima_iint_cache *iint,
> +				 struct inode *inode,
> +				 const unsigned char *filename)
> +{
> +	switch (error) {
> +	case TOMTOU:
> +		if (iint->readcount > 0)
> +			ima_add_violation(inode, filename, "invalid_pcr",
> +					  "ToMToU");
> +		break;
> +	case OPEN_WRITERS:
> +		if (iint->writecount > 0)
> +			ima_add_violation(inode, filename, "invalid_pcr",
> +					  "open_writers");
> +		break;
> +	}
> +}
> +
> +static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
> +				const unsigned char *filename)
> +{
> +	int rc = 0;
> +
> +	if (IS_ERR(file)) {
> +		pr_info("%s dentry_open failed\n", filename);
> +		return rc;
> +	}
> +	iint->readcount++;
> +
> +	rc = ima_collect_measurement(iint, file);
> +	if (!rc)
> +		ima_store_measurement(iint, file, filename);
> +	return rc;
> +}
> +
> +/**
> + * ima_path_check - based on policy, collect/store measurement.
> + * @path: contains a pointer to the path to be measured
> + * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
> + *
> + * Measure the file being open for readonly, based on the
> + * ima_must_measure() policy decision.
> + *
> + * Keep read/write counters for all files, but only
> + * invalidate the PCR for measured files:
> + * 	- Opening a file for write when already open for read,
> + *	  results in a time of measure, time of use (ToMToU) error.
> + *	- Opening a file for read when already open for write,
> + * 	  could result in a file measurement error.
> + *
> + * Return 0 on success, an error code on failure.
> + * (Based on the results of appraise_measurement().)
> + */
> +int ima_path_check(struct path *path, int mask)
> +{
> +	struct inode *inode = path->dentry->d_inode;
> +	struct ima_iint_cache *iint;
> +	struct file *file = NULL;
> +	int rc;
> +
> +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> +		return 0;
> +	iint = ima_iint_find_insert_get(inode);
> +	if (!iint)
> +		return 0;
> +
> +	mutex_lock(&iint->mutex);
> +	if ((mask & MAY_WRITE) || (mask == 0))
> +		iint->writecount++;
> +	else if (mask & (MAY_READ | MAY_EXEC))
> +		iint->readcount++;
> +
> +	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);

why MAY_READ, if mask could have MAY_WRITE?

> +	if (rc < 0)
> +		goto out;
> +
> +	if ((mask & MAY_WRITE) || (mask == 0))
> +		ima_read_write_check(TOMTOU, iint, inode,
> +				     path->dentry->d_name.name);
> +
> +	if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
> +		goto out;
> +
> +	ima_read_write_check(OPEN_WRITERS, iint, inode,
> +			     path->dentry->d_name.name);
> +	if (!(iint->flags & IMA_MEASURED)) {
> +		struct dentry *dentry = dget(path->dentry);
> +		struct vfsmount *mnt = mntget(path->mnt);
> +
> +		file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
> +		rc = get_path_measurement(iint, file, dentry->d_name.name);
> +	}
> +out:
> +	mutex_unlock(&iint->mutex);
> +	if (file)
> +		fput(file);
> +	kref_put(&iint->refcount, iint_free);
> +	return 0;
> +}
> +
> +static int process_measurement(struct file *file, const unsigned char *filename,
> +			       int mask, int function)
> +{
> +	struct inode *inode = file->f_dentry->d_inode;
> +	struct ima_iint_cache *iint;
> +	int rc;
> +
> +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> +		return 0;
> +	iint = ima_iint_find_insert_get(inode);
> +	if (!iint)
> +		return -ENOMEM;
> +
> +	mutex_lock(&iint->mutex);
> +	rc = ima_must_measure(iint, inode, mask, function);
> +	if (rc != 0)
> +		goto out;
> +
> +	rc = ima_collect_measurement(iint, file);
> +	if (!rc)
> +		ima_store_measurement(iint, file, filename);
> +out:
> +	mutex_unlock(&iint->mutex);
> +	kref_put(&iint->refcount, iint_free);
> +	return rc;
> +}
> +
> +/**
> + * ima_file_mmap - based on policy, collect/store measurement.
> + * @file: pointer to the file to be measured (May be NULL)
> + * @prot: contains the protection that will be applied by the kernel.
> + *
> + * Measure files being mmapped executable based on the ima_must_measure()
> + * policy decision.
> + *
> + * Return 0 on success, an error code on failure.
> + * (Based on the results of appraise_measurement().)
> + */
> +int ima_file_mmap(struct file *file, unsigned long prot)
> +{
> +	int rc;
> +
> +	if (!file)
> +		return 0;
> +	if (prot & PROT_EXEC)
> +		rc = process_measurement(file, file->f_dentry->d_name.name,
> +					 MAY_EXEC, FILE_MMAP);
> +	return 0;
> +}
> +
> +/**
> + * ima_bprm_check - based on policy, collect/store measurement.
> + * @bprm: contains the linux_binprm structure
> + *
> + * The OS protects against an executable file, already open for write,
> + * from being executed in deny_write_access() and an executable file,
> + * already open for execute, from being modified in get_write_access().
> + * So we can be certain that what we verify and measure here is actually
> + * what is being executed.
> + *
> + * Return 0 on success, an error code on failure.
> + * (Based on the results of appraise_measurement().)
> + */
> +int ima_bprm_check(struct linux_binprm *bprm)
> +{
> +	int rc;
> +
> +	rc = process_measurement(bprm->file, bprm->filename,
> +				 MAY_EXEC, BPRM_CHECK);
> +	return 0;

?

> +}
> +
> +static int __init init_ima(void)
> +{
> +	int error;
> +
> +	ima_iintcache_init();
> +	error = ima_init();
> +	ima_initialized = 1;
> +	return error;
> +}
> +
> +late_initcall(init_ima);	/* Start IMA after the TPM is available */
> +
> +MODULE_DESCRIPTION("Integrity Measurement Architecture");
> +MODULE_LICENSE("GPL");
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> new file mode 100644
> index 0000000..7c3d1ff
> --- /dev/null
> +++ b/security/integrity/ima/ima_policy.c
> @@ -0,0 +1,126 @@
> +/*
> + * Copyright (C) 2008 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.
> + *
> + * ima_policy.c
> + * 	- initialize default measure policy rules
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/list.h>
> +#include <linux/audit.h>
> +#include <linux/security.h>
> +#include <linux/magic.h>
> +
> +#include "ima.h"
> +
> +/* flags definitions */
> +#define IMA_FUNC 	0x0001
> +#define IMA_MASK 	0x0002
> +#define IMA_FSMAGIC	0x0004
> +#define IMA_UID		0x0008
> +
> +enum ima_action { DONT_MEASURE, MEASURE };
> +
> +struct ima_measure_rule_entry {
> +	struct list_head list;
> +	enum ima_action action;
> +	unsigned int flags;
> +	enum ima_hooks func;
> +	int mask;
> +	unsigned long fsmagic;
> +	uid_t uid;
> +};
> +
> +static struct ima_measure_rule_entry default_rules[] = {
> +	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
> +	 .flags = IMA_FSMAGIC},
> +	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
> +	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
> +	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
> +	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
> +	 .flags = IMA_FSMAGIC},
> +	{.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
> +	{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
> +	 .flags = IMA_FUNC | IMA_MASK},
> +	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
> +	 .flags = IMA_FUNC | IMA_MASK},
> +	{.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
> +	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}
> +};
> +
> +static LIST_HEAD(measure_default_rules);
> +static struct list_head *ima_measure;
> +
> +/**
> + * ima_match_rules - determine whether an inode matches the measure rule.
> + * @rule: a pointer to a rule
> + * @inode: a pointer to an inode
> + * @func: LIM hook identifier
> + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
> + *
> + * Returns true on rule match, false on failure.
> + */
> +static bool ima_match_rules(struct ima_measure_rule_entry *rule,
> +			    struct inode *inode, enum ima_hooks func, int mask)
> +{
> +	struct task_struct *tsk = current;
> +
> +	if ((rule->flags & IMA_FUNC) && rule->func != func)
> +		return false;
> +	if ((rule->flags & IMA_MASK) && rule->mask != mask)
> +		return false;
> +	if ((rule->flags & IMA_FSMAGIC)
> +	    && rule->fsmagic != inode->i_sb->s_magic)
> +		return false;
> +	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
> +		return false;
> +	return true;
> +}
> +
> +/**
> + * ima_match_policy - decision based on LSM and other conditions
> + * @inode: pointer to an inode for which the policy decision is being made
> + * @func: IMA hook identifier
> + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
> + *
> + * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
> + * conditions.
> + *
> + * (There is no need for locking when walking the policy list,
> + * as elements in the list are never deleted, nor does the list
> + * change.)
> + */
> +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
> +{
> +	struct ima_measure_rule_entry *entry;
> +
> +	list_for_each_entry(entry, ima_measure, list) {

The list never changes, but  once per boot the value of ima_measure
itself can change (at least after a later patch).  Does that matter?
Could you end up with a corrupt value here?

> +		bool rc;
> +
> +		rc = ima_match_rules(entry, inode, func, mask);
> +		if (rc)
> +			return entry->action;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * ima_init_policy - initialize the default measure rules.
> + *
> + * (Could use the default_rules directly, but in policy patch
> + * ima_measure points to either the measure_default_rules or the
> + * the new measure_policy_rules.)
> + */
> +void ima_init_policy(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(default_rules); i++)
> +		list_add_tail(&default_rules[i].list, &measure_default_rules);
> +	ima_measure = &measure_default_rules;
> +}
> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> new file mode 100644
> index 0000000..7cb518f
> --- /dev/null
> +++ b/security/integrity/ima/ima_queue.c
> @@ -0,0 +1,138 @@
> +/*
> + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> + *
> + * Authors:
> + * Serge Hallyn <serue@us.ibm.com>
> + * Reiner Sailer <sailer@watson.ibm.com>
> + * 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_queue.c
> + *       Implements queues that store template measurements and
> + *       maintains aggregate over the stored measurements
> + *       in the pre-configured TPM PCR (if available).
> + *       The measurement list is append-only. No entry is
> + *       ever removed or changed during the boot-cycle.
> + */
> +#include <linux/module.h>
> +#include <linux/rculist.h>
> +#include "ima.h"
> +
> +LIST_HEAD(ima_measurements);	/* list of all measurements */
> +
> +/* key: inode (before secure-hashing a file) */
> +struct ima_h_table ima_htable = {
> +	.len = ATOMIC_LONG_INIT(0),
> +	.violations = ATOMIC_LONG_INIT(0),
> +	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
> +};
> +
> +/* mutex protects atomicity of extending measurement list
> + * and extending the TPM PCR aggregate. Since tpm_extend can take
> + * long (and the tpm driver uses a mutex), we can't use the spinlock.
> + */
> +static DEFINE_MUTEX(ima_extend_list_mutex);
> +
> +/* lookup up the digest value in the hash table, and return the entry */
> +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
> +{
> +	struct ima_queue_entry *qe, *ret = NULL;
> +	unsigned int key;
> +	struct hlist_node *pos;
> +
> +	key = ima_hash_key(digest_value);
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
> +		if (memcmp(qe->entry->digest, digest_value, 20) == 0) {
> +			ret = qe;
> +			break;
> +		}
> +	}
> +	rcu_read_unlock();
> +	return ret;
> +}
> +
> +/* ima_add_template_entry helper function:
> + * - Add template entry to measurement list and hash table.
> + *
> + * (Called with ima_extend_list_mutex held.)
> + */
> +static int ima_add_digest_entry(struct ima_template_entry *entry)
> +{
> +	struct ima_queue_entry *qe;
> +	unsigned int key;
> +
> +	qe = kmalloc(sizeof(*qe), GFP_KERNEL);
> +	if (qe == NULL) {
> +		pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
> +		return -ENOMEM;
> +	}
> +	qe->entry = entry;
> +
> +	INIT_LIST_HEAD(&qe->later);
> +	list_add_tail_rcu(&qe->later, &ima_measurements);
> +
> +	atomic_long_inc(&ima_htable.len);
> +	key = ima_hash_key(entry->digest);
> +	hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
> +	return 0;
> +}
> +
> +static int ima_pcr_extend(const u8 *hash)
> +{
> +	int result = 0;
> +
> +	if (!ima_used_chip)
> +		return result;
> +
> +	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
> +	if (result != 0)
> +		pr_err("Error Communicating to TPM chip\n");
> +	return result;
> +}
> +
> +/* Add template entry to the measurement list and hash table,
> + * and extend the pcr.
> + */
> +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> +			   const char *op, struct inode *inode)
> +{
> +	u8 digest[IMA_DIGEST_SIZE];
> +	const char *audit_cause = "hash_added";
> +	int audit_info = 1;
> +	int result = 0;
> +
> +	mutex_lock(&ima_extend_list_mutex);
> +	if (!violation) {
> +		memcpy(digest, entry->digest, sizeof digest);
> +		if (ima_lookup_digest_entry(digest)) {
> +			audit_cause = "hash_exists";
> +			goto out;

Ok so not that I'm saying this would be easy, but an attacker
compromising say ftpd doesn't need to come up with a compromised
ftpd where sha1sum(evilftpd)==sha1sum(origftpd) - he just needs to
come up with one wher sha1sum(evilftpd)==sha1sum(X) where X is
any pristine program already loaded.  Right?

Is checking that strcmp(entry->file_name, newfilename)==0 warranted
here, or am I being silly?

> +		}
> +	}
> +
> +	result = ima_add_digest_entry(entry);
> +	if (result < 0) {
> +		audit_cause = "ENOMEM";
> +		audit_info = 0;
> +		goto out;
> +	}
> +
> +	if (violation)		/* invalidate pcr */
> +		memset(digest, 0xff, sizeof digest);
> +
> +	result = ima_pcr_extend(digest);
> +	if (result != 0) {
> +		audit_cause = "TPM error";
> +		audit_info = 0;
> +	}
> +out:
> +	mutex_unlock(&ima_extend_list_mutex);
> +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
> +			    op, audit_cause, result, audit_info);
> +	return result;
> +}
> -- 
> 1.5.6.6
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/6] integrity: IMA display
  2009-01-30 13:14     ` Mimi Zohar
@ 2009-02-02 23:14       ` Serge E. Hallyn
  2009-02-03  0:03         ` James Morris
  0 siblings, 1 reply; 27+ messages in thread
From: Serge E. Hallyn @ 2009-02-02 23:14 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: James Morris, linux-kernel, Andrew Morton, linux-security-module,
	Christoph Hellwig, Dave Hansen, <David Safford, Serge Hallyn,
	Mimi Zohar

Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> On Fri, 2009-01-30 at 20:18 +1100, James Morris wrote:
> > On Thu, 29 Jan 2009, Mimi Zohar wrote:
> > 
> > > +int ima_fs_init(void)
> > > +{
> > > +	ima_dir = securityfs_create_dir("ima", NULL);
> > > +	if (!ima_dir || IS_ERR(ima_dir))
> > > +		return -1;
> > > +
> > > +	binary_runtime_measurements =
> > > +	    securityfs_create_file("binary_runtime_measurements",
> > > +				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> > > +				   &ima_measurements_ops);
> > > +	if (!binary_runtime_measurements || IS_ERR(binary_runtime_measurements))
> > > +		goto out;
> > 
> > You should not be checking for NULL returns from securityfs_create_file(), 
> > because it does not return NULL (the documentation is wrong).
> > 
> > 
> > - James
> 
> Thanks!
> 
> Mimi

Wow, that's still the case??

>From dec581b116f16657db9b6f59b7e71f7a7026cd21 Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <serue@us.ibm.com>
Date: Mon, 2 Feb 2009 15:07:33 -0800
Subject: [PATCH 1/1] securityfs: fix long-broken securityfs_create_file comment

If there is an error creating a file through securityfs_create_file,
NULL is not returned, rather the error is propagated.

Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
---
 security/inode.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index efea5a6..ae243dd 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -205,12 +205,11 @@ static int create_by_name(const char *name, mode_t mode,
  * This function returns a pointer to a dentry if it succeeds.  This
  * pointer must be passed to the securityfs_remove() function when the file is
  * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here).  If an error occurs, %NULL is returned.
+ * you are responsible here).  If an error occurs, the function will return
+ * the erorr value (via ERR_PTR).
  *
  * If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned.  It is not wise to check for this value, but rather, check for
- * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
- * code.
+ * returned.
  */
 struct dentry *securityfs_create_file(const char *name, mode_t mode,
 				   struct dentry *parent, void *data,
-- 
1.5.4.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/6] integrity: IMA policy
  2009-01-29 22:23 ` [PATCH 4/6] integrity: IMA policy Mimi Zohar
@ 2009-02-02 23:40   ` Serge E. Hallyn
  2009-02-03  1:29     ` Mimi Zohar
  0 siblings, 1 reply; 27+ messages in thread
From: Serge E. Hallyn @ 2009-02-02 23:40 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> Support for a user loadable policy through securityfs
> with support for LSM specific policy data.
> 
> Based on comments made by: Matt Helsley, Serge Hallyn
> - replaced policy parsing code with version using strsep and match_token
> - only replace default policy with a valid policy
> 
> Signed-off-by: Mimi Zohar <zohar@us.ibm.com>

Apart from comments below,

Acked-by: Serge Hallyn <serue@us.ibm.com>

> ---
> diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
> new file mode 100644
> index 0000000..6434f0d
> --- /dev/null
> +++ b/Documentation/ABI/testing/ima_policy
> @@ -0,0 +1,61 @@
> +What:		security/ima/policy
> +Date:		May 2008
> +Contact:	Mimi Zohar <zohar@us.ibm.com>
> +Description:
> +		The Trusted Computing Group(TCG) runtime Integrity
> +		Measurement Architecture(IMA) maintains a list of hash
> +		values of executables and other sensitive system files
> +		loaded into the run-time of this system.  At runtime,
> +		the policy can be constrained based on LSM specific data.
> +		Policies are loaded into the securityfs file ima/policy
> +		by opening the file, writing the rules one at a time and
> +		then closing the file.  The new policy takes effect after
> +		the file ima/policy is closed.
> +
> +		rule format: action [condition ...]
> +
> +		action: measure | dont_measure
> +		condition:= base | lsm
> +			base:	[[func=] [mask=] [fsmagic=] [uid=]]
> +			lsm:	[[subj_user=] [subj_role=] [subj_type=]
> +				 [obj_user=] [obj_role=] [obj_type=]]
> +
> +		base: 	func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
> +			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
> +			fsmagic:= hex value
> +			uid:= decimal value
> +		lsm:  	are LSM specific
> +
> +		default policy:
> +			# PROC_SUPER_MAGIC
> +			dont_measure fsmagic=0x9fa0
> +			# SYSFS_MAGIC
> +			dont_measure fsmagic=0x62656572
> +			# DEBUGFS_MAGIC
> +			dont_measure fsmagic=0x64626720
> +			# TMPFS_MAGIC
> +			dont_measure fsmagic=0x01021994
> +			# SECURITYFS_MAGIC
> +			dont_measure fsmagic=0x73636673
> +
> +			measure func=BPRM_CHECK
> +			measure func=FILE_MMAP mask=MAY_EXEC
> +			measure func=INODE_PERM mask=MAY_READ uid=0
> +
> +		The default policy measures all executables in bprm_check,
> +		all files mmapped executable in file_mmap, and all files
> +		open for read by root in inode_permission.
> +
> +		Examples of LSM specific definitions:
> +
> +		SELinux:
> +			# SELINUX_MAGIC
> +			dont_measure fsmagic=0xF97CFF8C
> +
> +			dont_measure obj_type=var_log_t
> +			dont_measure obj_type=auditd_log_t
> +			measure subj_user=system_u func=INODE_PERM mask=MAY_READ
> +			measure subj_role=system_r func=INODE_PERM mask=MAY_READ
> +
> +		Smack:
> +			measure subj_user=_ func=INODE_PERM mask=MAY_READ
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 2a761c8..3d2b6ee 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -47,3 +47,9 @@ config IMA_AUDIT
>  	  auditing messages can be enabled with 'ima_audit=1' on
>  	  the kernel command line.
>  
> +config IMA_LSM_RULES
> +	bool
> +	depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK)
> +	default y
> +	help
> +	  Disabling this option will disregard LSM based policy rules
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 236b74e..5b72cdb 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -138,4 +138,28 @@ enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
>  int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
>  void ima_init_policy(void);
>  void ima_update_policy(void);
> +int ima_parse_add_rule(char *);
> +void ima_delete_rules(void);
> +
> +/* LSM based policy rules require audit */
> +#ifdef CONFIG_IMA_LSM_RULES
> +
> +#define security_filter_rule_init security_audit_rule_init
> +#define security_filter_rule_match security_audit_rule_match
> +
> +#else
> +
> +static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
> +					    void **lsmrule)
> +{
> +	return -EINVAL;
> +}
> +
> +static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
> +					     void *lsmrule,
> +					     struct audit_context *actx)
> +{
> +	return -EINVAL;
> +}
> +#endif /* CONFIG_IMA_LSM_RULES */
>  #endif
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 5044e4c..752a344 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -19,9 +19,11 @@
>  #include <linux/seq_file.h>
>  #include <linux/rculist.h>
>  #include <linux/rcupdate.h>
> +#include <linux/parser.h>
>  
>  #include "ima.h"
>  
> +static int valid_policy = 1;
>  #define TMPBUFLEN 12
>  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>  				     loff_t *ppos, atomic_long_t *val)
> @@ -237,11 +239,66 @@ static struct file_operations ima_ascii_measurements_ops = {
>  	.release = seq_release,
>  };
>  
> +static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> +				size_t datalen, loff_t *ppos)
> +{
> +	char *data;
> +	int rc;
> +
> +	if (datalen >= PAGE_SIZE)
> +		return -ENOMEM;
> +	if (*ppos != 0) {
> +		/* No partial writes. */
> +		return -EINVAL;
> +	}
> +	data = kmalloc(datalen + 1, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(data, buf, datalen)) {
> +		kfree(data);
> +		return -EFAULT;
> +	}
> +	*(data + datalen) = '\0';
> +	rc = ima_parse_add_rule(data);
> +	if (rc < 0) {
> +		datalen = -EINVAL;
> +		valid_policy = 0;
> +	}
> +
> +	kfree(data);
> +	return datalen;
> +}
> +
>  static struct dentry *ima_dir;
>  static struct dentry *binary_runtime_measurements;
>  static struct dentry *ascii_runtime_measurements;
>  static struct dentry *runtime_measurements_count;
>  static struct dentry *violations;
> +static struct dentry *ima_policy;
> +
> +/*
> + * ima_release_policy - start using the new measure policy rules.
> + *
> + * Initially, ima_measure points to the default policy rules, now
> + * point to the new policy rules, and remove the securityfs policy file.
> + */
> +static int ima_release_policy(struct inode *inode, struct file *file)
> +{
> +	if (!valid_policy) {
> +		ima_delete_rules();
> +		return 0;
> +	}
> +	ima_update_policy();
> +	securityfs_remove(ima_policy);
> +	ima_policy = NULL;
> +	return 0;
> +}
> +
> +static struct file_operations ima_measure_policy_ops = {
> +	.write = ima_write_policy,
> +	.release = ima_release_policy
> +};
>  
>  int ima_fs_init(void)
>  {
> @@ -276,13 +333,20 @@ int ima_fs_init(void)
>  	if (!violations || IS_ERR(violations))
>  		goto out;
>  
> -	return 0;
> +	ima_policy = securityfs_create_file("policy",
> +					    S_IRUSR | S_IRGRP | S_IWUSR,
> +					    ima_dir, NULL,
> +					    &ima_measure_policy_ops);
> +	if (!ima_policy || IS_ERR(ima_policy))
> +		goto out;

Of course, James' same comment applies here :)

> +/**
> + * ima_parse_add_rule - add a rule to measure_policy_rules
> + * @rule - ima measurement policy rule
> + *
> + * Uses a mutex to protect the policy list from multiple concurrent writers.
> + * Returns 0 on success, an error code on failure.
> + */
> +int ima_parse_add_rule(char *rule)
> +{
> +	const char *op = "add_rule";
> +	struct ima_measure_rule_entry *entry;
> +	int result = 0;
> +	int audit_info = 0;
> +
> +	/* Prevent installed policy from changing */
> +	if (ima_measure != &measure_default_rules) {
> +		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
> +				    NULL, op, "already exists",
> +				    -EACCES, audit_info);
> +		return -EACCES;
> +	}
> +
> +	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +	if (!entry) {
> +		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
> +				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
> +		return -ENOMEM;
> +	}
> +
> +	INIT_LIST_HEAD(&entry->list);
> +
> +	result = ima_parse_rule(rule, entry);
> +	if (!result) {
> +		mutex_lock(&ima_measure_mutex);
> +		list_add_tail(&entry->list, &measure_policy_rules);
> +		mutex_unlock(&ima_measure_mutex);
> +	}

Should you kfree(entry) if ima_parse_rule() failed?


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/6] Integrity: IMA file free imbalance
  2009-01-29 22:23 ` [PATCH 6/6] Integrity: IMA file free imbalance Mimi Zohar
@ 2009-02-02 23:47   ` Serge E. Hallyn
  2009-02-03  1:27     ` Mimi Zohar
  0 siblings, 1 reply; 27+ messages in thread
From: Serge E. Hallyn @ 2009-02-02 23:47 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> The number of calls to ima_path_check()/ima_file_free()
> should be balanced.  An extra call to fput(), indicates
> the file could have been accessed without first being
> measured.
> 
> Although f_count is incremented/decremented in places other
> than fget/fput, like fget_light/fput_light and get_file, the
> current task must already hold a file refcnt.  The call to
> __fput() is delayed until the refcnt becomes 0, resulting
> in ima_file_free() flagging any changes.
> 
> - add hook to increment opencount for IPC shared memory(SYSV)
>   and shmat files
> 
> Signed-off-by: Mimi Zohar <zohar@us.ibm.com>

...

> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c

...

> +static void opencount_get(struct file *file)
> +{
> +	struct inode *inode = file->f_dentry->d_inode;
> +	struct ima_iint_cache *iint;
> +
> +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> +		return;
> +	iint = ima_iint_find_insert_get(inode);
> +	mutex_lock(&iint->mutex);
> +	if (iint)

Hey, I think you've got those two lines above mixed
up a bit :)

Very neat, though.

Acked-by: Serge Hallyn <serue@us.ibm.com>

once that's fixed up.

thanks,
-serge

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/6] integrity: IMA display
  2009-02-02 23:14       ` Serge E. Hallyn
@ 2009-02-03  0:03         ` James Morris
  0 siblings, 0 replies; 27+ messages in thread
From: James Morris @ 2009-02-03  0:03 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: Mimi Zohar, linux-kernel, Andrew Morton, linux-security-module,
	Christoph Hellwig, Dave Hansen, <David Safford, Serge Hallyn,
	Mimi Zohar

On Mon, 2 Feb 2009, Serge E. Hallyn wrote:

> >From dec581b116f16657db9b6f59b7e71f7a7026cd21 Mon Sep 17 00:00:00 2001
> From: Serge E. Hallyn <serue@us.ibm.com>
> Date: Mon, 2 Feb 2009 15:07:33 -0800
> Subject: [PATCH 1/1] securityfs: fix long-broken securityfs_create_file comment
> 
> If there is an error creating a file through securityfs_create_file,
> NULL is not returned, rather the error is propagated.
> 
> Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>


Applied, thanks.

> ---
>  security/inode.c |    7 +++----
>  1 files changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/security/inode.c b/security/inode.c
> index efea5a6..ae243dd 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -205,12 +205,11 @@ static int create_by_name(const char *name, mode_t mode,
>   * This function returns a pointer to a dentry if it succeeds.  This
>   * pointer must be passed to the securityfs_remove() function when the file is
>   * to be removed (no automatic cleanup happens if your module is unloaded,
> - * you are responsible here).  If an error occurs, %NULL is returned.
> + * you are responsible here).  If an error occurs, the function will return
> + * the erorr value (via ERR_PTR).
>   *
>   * If securityfs is not enabled in the kernel, the value %-ENODEV is
> - * returned.  It is not wise to check for this value, but rather, check for
> - * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
> - * code.
> + * returned.
>   */
>  struct dentry *securityfs_create_file(const char *name, mode_t mode,
>  				   struct dentry *parent, void *data,
> -- 
> 1.5.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/6] Integrity: IMA file free imbalance
  2009-02-02 23:47   ` Serge E. Hallyn
@ 2009-02-03  1:27     ` Mimi Zohar
  0 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-02-03  1:27 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

On Mon, 2009-02-02 at 17:47 -0600, Serge E. Hallyn wrote:
> Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> > The number of calls to ima_path_check()/ima_file_free()
> > should be balanced.  An extra call to fput(), indicates
> > the file could have been accessed without first being
> > measured.
> > 
> > Although f_count is incremented/decremented in places other
> > than fget/fput, like fget_light/fput_light and get_file, the
> > current task must already hold a file refcnt.  The call to
> > __fput() is delayed until the refcnt becomes 0, resulting
> > in ima_file_free() flagging any changes.
> > 
> > - add hook to increment opencount for IPC shared memory(SYSV)
> >   and shmat files
> > 
> > Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
> 
> ...
> 
> > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> 
> ...
> 
> > +static void opencount_get(struct file *file)
> > +{
> > +	struct inode *inode = file->f_dentry->d_inode;
> > +	struct ima_iint_cache *iint;
> > +
> > +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> > +		return;
> > +	iint = ima_iint_find_insert_get(inode);
> > +	mutex_lock(&iint->mutex);
> > +	if (iint)
> 
> Hey, I think you've got those two lines above mixed
> up a bit :)

yes, thanks!

> Very neat, though.
> 
> Acked-by: Serge Hallyn <serue@us.ibm.com>
> 
> once that's fixed up.
> 
> thanks,
> -serge


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/6] integrity: IMA policy
  2009-02-02 23:40   ` Serge E. Hallyn
@ 2009-02-03  1:29     ` Mimi Zohar
  0 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-02-03  1:29 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

On Mon, 2009-02-02 at 17:40 -0600, Serge E. Hallyn wrote:
> Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> > Support for a user loadable policy through securityfs
> > with support for LSM specific policy data.
> > 
> > Based on comments made by: Matt Helsley, Serge Hallyn
> > - replaced policy parsing code with version using strsep and match_token
> > - only replace default policy with a valid policy
> > 
> > Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
> 
> Apart from comments below,
> 
> Acked-by: Serge Hallyn <serue@us.ibm.com>
> 
> > ---
> > diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
> > new file mode 100644
> > index 0000000..6434f0d
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/ima_policy
> > @@ -0,0 +1,61 @@
> > +What:		security/ima/policy
> > +Date:		May 2008
> > +Contact:	Mimi Zohar <zohar@us.ibm.com>
> > +Description:
> > +		The Trusted Computing Group(TCG) runtime Integrity
> > +		Measurement Architecture(IMA) maintains a list of hash
> > +		values of executables and other sensitive system files
> > +		loaded into the run-time of this system.  At runtime,
> > +		the policy can be constrained based on LSM specific data.
> > +		Policies are loaded into the securityfs file ima/policy
> > +		by opening the file, writing the rules one at a time and
> > +		then closing the file.  The new policy takes effect after
> > +		the file ima/policy is closed.
> > +
> > +		rule format: action [condition ...]
> > +
> > +		action: measure | dont_measure
> > +		condition:= base | lsm
> > +			base:	[[func=] [mask=] [fsmagic=] [uid=]]
> > +			lsm:	[[subj_user=] [subj_role=] [subj_type=]
> > +				 [obj_user=] [obj_role=] [obj_type=]]
> > +
> > +		base: 	func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
> > +			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
> > +			fsmagic:= hex value
> > +			uid:= decimal value
> > +		lsm:  	are LSM specific
> > +
> > +		default policy:
> > +			# PROC_SUPER_MAGIC
> > +			dont_measure fsmagic=0x9fa0
> > +			# SYSFS_MAGIC
> > +			dont_measure fsmagic=0x62656572
> > +			# DEBUGFS_MAGIC
> > +			dont_measure fsmagic=0x64626720
> > +			# TMPFS_MAGIC
> > +			dont_measure fsmagic=0x01021994
> > +			# SECURITYFS_MAGIC
> > +			dont_measure fsmagic=0x73636673
> > +
> > +			measure func=BPRM_CHECK
> > +			measure func=FILE_MMAP mask=MAY_EXEC
> > +			measure func=INODE_PERM mask=MAY_READ uid=0
> > +
> > +		The default policy measures all executables in bprm_check,
> > +		all files mmapped executable in file_mmap, and all files
> > +		open for read by root in inode_permission.
> > +
> > +		Examples of LSM specific definitions:
> > +
> > +		SELinux:
> > +			# SELINUX_MAGIC
> > +			dont_measure fsmagic=0xF97CFF8C
> > +
> > +			dont_measure obj_type=var_log_t
> > +			dont_measure obj_type=auditd_log_t
> > +			measure subj_user=system_u func=INODE_PERM mask=MAY_READ
> > +			measure subj_role=system_r func=INODE_PERM mask=MAY_READ
> > +
> > +		Smack:
> > +			measure subj_user=_ func=INODE_PERM mask=MAY_READ
> > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > index 2a761c8..3d2b6ee 100644
> > --- a/security/integrity/ima/Kconfig
> > +++ b/security/integrity/ima/Kconfig
> > @@ -47,3 +47,9 @@ config IMA_AUDIT
> >  	  auditing messages can be enabled with 'ima_audit=1' on
> >  	  the kernel command line.
> >  
> > +config IMA_LSM_RULES
> > +	bool
> > +	depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK)
> > +	default y
> > +	help
> > +	  Disabling this option will disregard LSM based policy rules
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index 236b74e..5b72cdb 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -138,4 +138,28 @@ enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
> >  int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
> >  void ima_init_policy(void);
> >  void ima_update_policy(void);
> > +int ima_parse_add_rule(char *);
> > +void ima_delete_rules(void);
> > +
> > +/* LSM based policy rules require audit */
> > +#ifdef CONFIG_IMA_LSM_RULES
> > +
> > +#define security_filter_rule_init security_audit_rule_init
> > +#define security_filter_rule_match security_audit_rule_match
> > +
> > +#else
> > +
> > +static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
> > +					    void **lsmrule)
> > +{
> > +	return -EINVAL;
> > +}
> > +
> > +static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
> > +					     void *lsmrule,
> > +					     struct audit_context *actx)
> > +{
> > +	return -EINVAL;
> > +}
> > +#endif /* CONFIG_IMA_LSM_RULES */
> >  #endif
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 5044e4c..752a344 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -19,9 +19,11 @@
> >  #include <linux/seq_file.h>
> >  #include <linux/rculist.h>
> >  #include <linux/rcupdate.h>
> > +#include <linux/parser.h>
> >  
> >  #include "ima.h"
> >  
> > +static int valid_policy = 1;
> >  #define TMPBUFLEN 12
> >  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> >  				     loff_t *ppos, atomic_long_t *val)
> > @@ -237,11 +239,66 @@ static struct file_operations ima_ascii_measurements_ops = {
> >  	.release = seq_release,
> >  };
> >  
> > +static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> > +				size_t datalen, loff_t *ppos)
> > +{
> > +	char *data;
> > +	int rc;
> > +
> > +	if (datalen >= PAGE_SIZE)
> > +		return -ENOMEM;
> > +	if (*ppos != 0) {
> > +		/* No partial writes. */
> > +		return -EINVAL;
> > +	}
> > +	data = kmalloc(datalen + 1, GFP_KERNEL);
> > +	if (!data)
> > +		return -ENOMEM;
> > +
> > +	if (copy_from_user(data, buf, datalen)) {
> > +		kfree(data);
> > +		return -EFAULT;
> > +	}
> > +	*(data + datalen) = '\0';
> > +	rc = ima_parse_add_rule(data);
> > +	if (rc < 0) {
> > +		datalen = -EINVAL;
> > +		valid_policy = 0;
> > +	}
> > +
> > +	kfree(data);
> > +	return datalen;
> > +}
> > +
> >  static struct dentry *ima_dir;
> >  static struct dentry *binary_runtime_measurements;
> >  static struct dentry *ascii_runtime_measurements;
> >  static struct dentry *runtime_measurements_count;
> >  static struct dentry *violations;
> > +static struct dentry *ima_policy;
> > +
> > +/*
> > + * ima_release_policy - start using the new measure policy rules.
> > + *
> > + * Initially, ima_measure points to the default policy rules, now
> > + * point to the new policy rules, and remove the securityfs policy file.
> > + */
> > +static int ima_release_policy(struct inode *inode, struct file *file)
> > +{
> > +	if (!valid_policy) {
> > +		ima_delete_rules();
> > +		return 0;
> > +	}
> > +	ima_update_policy();
> > +	securityfs_remove(ima_policy);
> > +	ima_policy = NULL;
> > +	return 0;
> > +}
> > +
> > +static struct file_operations ima_measure_policy_ops = {
> > +	.write = ima_write_policy,
> > +	.release = ima_release_policy
> > +};
> >  
> >  int ima_fs_init(void)
> >  {
> > @@ -276,13 +333,20 @@ int ima_fs_init(void)
> >  	if (!violations || IS_ERR(violations))
> >  		goto out;
> >  
> > -	return 0;
> > +	ima_policy = securityfs_create_file("policy",
> > +					    S_IRUSR | S_IRGRP | S_IWUSR,
> > +					    ima_dir, NULL,
> > +					    &ima_measure_policy_ops);
> > +	if (!ima_policy || IS_ERR(ima_policy))
> > +		goto out;
> 
> Of course, James' same comment applies here :)

Yes, got that one too.

> > +/**
> > + * ima_parse_add_rule - add a rule to measure_policy_rules
> > + * @rule - ima measurement policy rule
> > + *
> > + * Uses a mutex to protect the policy list from multiple concurrent writers.
> > + * Returns 0 on success, an error code on failure.
> > + */
> > +int ima_parse_add_rule(char *rule)
> > +{
> > +	const char *op = "add_rule";
> > +	struct ima_measure_rule_entry *entry;
> > +	int result = 0;
> > +	int audit_info = 0;
> > +
> > +	/* Prevent installed policy from changing */
> > +	if (ima_measure != &measure_default_rules) {
> > +		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
> > +				    NULL, op, "already exists",
> > +				    -EACCES, audit_info);
> > +		return -EACCES;
> > +	}
> > +
> > +	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> > +	if (!entry) {
> > +		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
> > +				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	INIT_LIST_HEAD(&entry->list);
> > +
> > +	result = ima_parse_rule(rule, entry);
> > +	if (!result) {
> > +		mutex_lock(&ima_measure_mutex);
> > +		list_add_tail(&entry->list, &measure_policy_rules);
> > +		mutex_unlock(&ima_measure_mutex);
> > +	}
> 
> Should you kfree(entry) if ima_parse_rule() failed?

yes, thanks.

Mimi


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-02-02 23:02   ` Serge E. Hallyn
@ 2009-02-03  2:09     ` Mimi Zohar
  2009-02-03 13:36     ` david safford
  1 sibling, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2009-02-03  2:09 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-kernel, Andrew Morton, James Morris, Christoph Hellwig,
	Dave Hansen, <David Safford, Serge Hallyn, Mimi Zohar

On Mon, 2009-02-02 at 17:02 -0600, Serge E. Hallyn wrote:
> Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> > IMA provides hardware (TPM) based measurement and attestation for
> > file measurements. As the Trusted Computing (TPM) model requires,
> > IMA measures all files before they are accessed in any way (on the
> > integrity_bprm_check, integrity_path_check and integrity_file_mmap
> > hooks), and commits the measurements to the TPM. Once added to the
> > TPM, measurements can not be removed.
> > 
> > In addition, IMA maintains a list of these file measurements, which
> > can be used to validate the aggregate value stored in the TPM.  The
> > TPM can sign these measurements, and thus the system can prove, to
> > itself and to a third party, the system's integrity in a way that
> > cannot be circumvented by malicious or compromised software.
> > 
> > - removed LIM hooks and API registration; IMA is now called directly
> > - added slab for integrity information(iint) associated with an inode
> > - added a local read and write count in iint
> > - lots of code refactoring (i.e. calculating the boot aggregate,
> >   flagging openwriters/ToMToU)
> > - statically defined and initialized variables
> > - addressed the credential merge changes
> > - addressed locking issues in general (i.e. ima_file_free())
> >   and addressed the rcu-locking problem in ima_iint_delete() in particular
> > - removed caching of measurement policy results
> > - removed Kconfig prompt for: pcr index, informational audit messages
> > 
> > Signed-off-by: Mimi Zohar <zohar@us.ibm.com>
> 
> I notice there is no MAINTAINERS entry for IMA?
> 
> A few comments below.  Aside from my comments in ima_bprm_check and
> ima_path_check,
> 
> Acked-by: Serge Hallyn <serue@us.ibm.com>
> 
> > ---
> > diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
> > index 40c51e9..8cc40a1 100644
> > --- a/Documentation/kernel-parameters.txt
> > +++ b/Documentation/kernel-parameters.txt
> > @@ -901,6 +901,15 @@ and is between 256 and 4096 characters. It is defined in the file
> >  	ihash_entries=	[KNL]
> >  			Set number of hash buckets for inode cache.
> >  
> > +	ima_audit=	[IMA]
> > +			Format: { "0" | "1" }
> > +			0 -- integrity auditing messages. (Default)
> > +			1 -- enable informational integrity auditing messages.
> > +
> > +	ima_hash=	[IMA]
> > +			Formt: { "sha1" | "md5" }
> > +			default: "sha1"
> > +
> >  	in2000=		[HW,SCSI]
> >  			See header of drivers/scsi/in2000.c.
> >  
> > diff --git a/include/linux/audit.h b/include/linux/audit.h
> > index 67e5dbf..930939a 100644
> > --- a/include/linux/audit.h
> > +++ b/include/linux/audit.h
> > @@ -125,6 +125,11 @@
> >  #define AUDIT_LAST_KERN_ANOM_MSG    1799
> >  #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
> >  #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
> > +#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
> > +#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
> > +#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
> > +#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
> > +#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
> >  
> >  #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
> >  
> > diff --git a/include/linux/ima.h b/include/linux/ima.h
> > index 4ed1e4d..dcc3664 100644
> > --- a/include/linux/ima.h
> > +++ b/include/linux/ima.h
> > @@ -12,6 +12,15 @@
> >  #ifndef _LINUX_IMA_H
> >  #define _LINUX_IMA_H
> >  
> > +#ifdef CONFIG_IMA
> > +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_path_check(struct path *path, int mask);
> > +extern void ima_file_free(struct file *file);
> > +extern int ima_file_mmap(struct file *file, unsigned long prot);
> > +
> > +#else
> >  static inline int ima_bprm_check(struct linux_binprm *bprm)
> >  {
> >  	return 0;
> > @@ -41,4 +50,5 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
> >  {
> >  	return 0;
> >  }
> > +#endif /* CONFIG_IMA_H */
> >  #endif /* _LINUX_IMA_H */
> > diff --git a/security/Kconfig b/security/Kconfig
> > index 9438535..bf129f8 100644
> > --- a/security/Kconfig
> > +++ b/security/Kconfig
> > @@ -55,7 +55,8 @@ config SECURITYFS
> >  	bool "Enable the securityfs filesystem"
> >  	help
> >  	  This will build the securityfs filesystem.  It is currently used by
> > -	  the TPM bios character driver.  It is not used by SELinux or SMACK.
> > +	  the TPM bios character driver and IMA, an integrity provider.  It is
> > +	  not used by SELinux or SMACK.
> >  
> >  	  If you are unsure how to answer this question, answer N.
> >  
> > @@ -135,5 +136,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
> >  source security/selinux/Kconfig
> >  source security/smack/Kconfig
> >  
> > +source security/integrity/ima/Kconfig
> > +
> >  endmenu
> >  
> > diff --git a/security/Makefile b/security/Makefile
> > index c05c127..595536c 100644
> > --- a/security/Makefile
> > +++ b/security/Makefile
> > @@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
> >  obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
> >  obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
> >  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
> > +
> > +# Object integrity file lists
> > +subdir-$(CONFIG_IMA)			+= integrity/ima
> > +obj-$(CONFIG_IMA)			+= integrity/ima/built-in.o
> > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > new file mode 100644
> > index 0000000..2a761c8
> > --- /dev/null
> > +++ b/security/integrity/ima/Kconfig
> > @@ -0,0 +1,49 @@
> > +# IBM Integrity Measurement Architecture
> > +#
> > +config IMA
> > +	bool "Integrity Measurement Architecture(IMA)"
> > +	depends on ACPI
> > +	select SECURITYFS
> > +	select CRYPTO
> > +	select CRYPTO_HMAC
> > +	select CRYPTO_MD5
> > +	select CRYPTO_SHA1
> > +	select TCG_TPM
> > +	select TCG_TIS
> > +	help
> > +	  The Trusted Computing Group(TCG) runtime Integrity
> > +	  Measurement Architecture(IMA) maintains a list of hash
> > +	  values of executables and other sensitive system files,
> > +	  as they are read or executed. If an attacker manages
> > +	  to change the contents of an important system file
> > +	  being measured, we can tell.
> > +
> > +	  If your system has a TPM chip, then IMA also maintains
> > +	  an aggregate integrity value over this list inside the
> > +	  TPM hardware, so that the TPM can prove to a third party
> > +	  whether or not critical system files have been modified.
> > +	  Read <http://www.usenix.org/events/sec04/tech/sailer.html>
> > +	  to learn more about IMA.
> > +	  If unsure, say N.
> > +
> > +config IMA_MEASURE_PCR_IDX
> > +	int
> > +	depends on IMA
> > +	range 8 14
> > +	default 10
> > +	help
> > +	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
> > +	  that IMA uses to maintain the integrity aggregate of the
> > +	  measurement list.  If unsure, use the default 10.
> > +
> > +config IMA_AUDIT
> > +	bool
> > +	depends on IMA
> > +	default y
> > +	help
> > +	  This option adds a kernel parameter 'ima_audit', which
> > +	  allows informational auditing messages to be enabled
> > +	  at boot.  If this option is selected, informational integrity
> > +	  auditing messages can be enabled with 'ima_audit=1' on
> > +	  the kernel command line.
> > +
> > diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
> > new file mode 100644
> > index 0000000..9d6bf97
> > --- /dev/null
> > +++ b/security/integrity/ima/Makefile
> > @@ -0,0 +1,9 @@
> > +#
> > +# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
> > +# Measurement Architecture(IMA).
> > +#
> > +
> > +obj-$(CONFIG_IMA) += ima.o
> > +
> > +ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
> > +	 ima_policy.o ima_iint.o ima_audit.o
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > new file mode 100644
> > index 0000000..04a397e
> > --- /dev/null
> > +++ b/security/integrity/ima/ima.h
> > @@ -0,0 +1,136 @@
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + *
> > + * Authors:
> > + * Reiner Sailer <sailer@watson.ibm.com>
> > + * 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.h
> > + *	internal Integrity Measurement Architecture (IMA) definitions
> > + */
> > +
> > +#ifndef __LINUX_IMA_H
> > +#define __LINUX_IMA_H
> > +
> > +#include <linux/types.h>
> > +#include <linux/crypto.h>
> > +#include <linux/security.h>
> > +#include <linux/hash.h>
> > +#include <linux/tpm.h>
> > +#include <linux/audit.h>
> > +
> > +enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
> > +enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
> > +
> > +/* digest size for IMA, fits SHA1 or MD5 */
> > +#define IMA_DIGEST_SIZE		20
> > +#define IMA_EVENT_NAME_LEN_MAX	255
> > +
> > +#define IMA_HASH_BITS 9
> > +#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
> > +
> > +/* set during initialization */
> > +extern int ima_initialized;
> > +extern int ima_used_chip;
> > +extern char *ima_hash;
> > +
> > +/* IMA inode template definition */
> > +struct ima_template_data {
> > +	u8 digest[IMA_DIGEST_SIZE];	/* sha1/md5 measurement hash */
> > +	char file_name[IMA_EVENT_NAME_LEN_MAX + 1];	/* name + \0 */
> > +};
> > +
> > +#define IMA_TEMPLATE_NAME_LEN_MAX 20
> 
> No longer used

True

> > +struct ima_template_entry {
> > +	u8 digest[IMA_DIGEST_SIZE];	/* sha1 or md5 measurement hash */
> > +	char *template_name;
> > +	int template_len;
> > +	struct ima_template_data template;
> > +};
> > +
> > +struct ima_queue_entry {
> > +	struct hlist_node hnext;	/* place in hash collision list */
> > +	struct list_head later;		/* place in ima_measurements list */
> > +	struct ima_template_entry *entry;
> > +};
> > +extern struct list_head ima_measurements;	/* list of all measurements */
> > +
> > +/* declarations */
> > +void integrity_audit_msg(int audit_msgno, struct inode *inode,
> > +			 const unsigned char *fname, const char *op,
> > +			 const char *cause, int result, int info);
> > +
> > +/* Internal IMA function definitions */
> > +void ima_iintcache_init(void);
> > +int ima_init(void);
> > +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> > +			   const char *op, struct inode *inode);
> > +int ima_calc_hash(struct file *file, char *digest);
> > +int ima_calc_template_hash(int template_len, void *template, char *digest);
> > +int ima_calc_boot_aggregate(char *digest);
> > +void ima_add_violation(struct inode *inode, const unsigned char *filename,
> > +		       const char *op, const char *cause);
> > +
> > +/*
> > + * used to protect h_table and sha_table
> > + */
> > +extern spinlock_t ima_queue_lock;
> > +
> > +struct ima_h_table {
> > +	atomic_long_t len;	/* number of stored measurements in the list */
> > +	atomic_long_t violations;
> > +	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
> > +};
> > +extern struct ima_h_table ima_htable;
> > +
> > +static inline unsigned long ima_hash_key(u8 *digest)
> > +{
> > +	return hash_long(*digest, IMA_HASH_BITS);
> > +}
> > +
> > +/* iint cache flags */
> > +#define IMA_MEASURED		1
> > +
> > +/* integrity data associated with an inode */
> > +struct ima_iint_cache {
> > +	u64 version;		/* track inode changes */
> > +	unsigned long flags;
> > +	u8 digest[IMA_DIGEST_SIZE];
> > +	struct mutex mutex;	/* protects: version, flags, digest */
> > +	long readcount;		/* measured files readcount */
> > +	long writecount;	/* measured files writecount */
> > +	struct kref refcount;	/* ima_iint_cache reference count */
> > +	struct rcu_head rcu;
> > +};
> > +
> > +/* LIM API function definitions */
> > +int ima_must_measure(struct ima_iint_cache *iint, 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,
> > +			   const unsigned char *filename);
> > +int ima_store_template(struct ima_template_data *data, int violation,
> > +		       struct inode *inode);
> > +
> > +/* radix tree calls to lookup, insert, delete
> > + * integrity data associated with an inode.
> > + */
> > +struct ima_iint_cache *ima_iint_insert(struct inode *inode);
> > +struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
> > +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
> > +void ima_iint_delete(struct inode *inode);
> > +void iint_free(struct kref *kref);
> > +void iint_rcu_free(struct rcu_head *rcu);
> > +
> > +/* IMA policy related functions */
> > +enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
> > +
> > +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
> > +void ima_init_policy(void);
> > +void ima_update_policy(void);
> > +#endif
> > diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
> > new file mode 100644
> > index 0000000..7c1db11
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_api.c
> > @@ -0,0 +1,185 @@
> > +/*
> > + * Copyright (C) 2008 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.
> > + *
> > + * File: ima_api.c
> > + *	Implements must_measure, collect_measurement, store_measurement,
> > + *	and store_template.
> > + */
> > +#include <linux/module.h>
> > +
> > +#include "ima.h"
> > +static char *IMA_TEMPLATE_NAME = "ima";
> > +
> > +/*
> > + * ima_store_template - store ima template measurements
> > + *
> > + * Calculate the hash of a template entry, add the template entry
> > + * to an ordered list of measurement entries maintained inside the kernel,
> > + * and also update the aggregate integrity value (maintained inside the
> > + * configured TPM PCR) over the hashes of the current list of measurement
> > + * entries.
> > + *
> > + * Applications retrieve the current kernel-held measurement list through
> > + * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
> > + * TPM PCR (called quote) can be retrieved using a TPM user space library
> > + * and is used to validate the measurement list.
> > + *
> > + * Returns 0 on success, error code otherwise
> > + */
> > +int ima_store_template(struct ima_template_data *template,
> > +		       int violation, struct inode *inode)
> > +{
> > +	struct ima_template_entry *entry;
> > +	const char *op = "add_template_measure";
> > +	const char *audit_cause = "ENOMEM";
> > +	int result = -ENOMEM;
> > +
> > +	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
> > +	if (!entry)
> > +		goto err_out;
> > +
> > +	memcpy(&entry->template, template, sizeof(*template));
> > +	memset(&entry->digest, 0, sizeof(entry->digest));
> > +	entry->template_name = IMA_TEMPLATE_NAME;
> > +	entry->template_len = sizeof(*template);
> > +
> > +	if (!violation) {
> > +		result = ima_calc_template_hash(entry->template_len,
> > +						template, entry->digest);
> > +		if (result < 0) {
> > +			kfree(entry);
> > +			audit_cause = "hashing_error";
> > +			goto err_out;
> > +		}
> > +	}
> > +	result = ima_add_template_entry(entry, violation, op, inode);
> > +	if (result < 0)
> > +		kfree(entry);
> > +	return result;
> > +
> > +err_out:
> > +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
> > +			    op, audit_cause, result, 0);
> > +
> > +	return result;
> > +}
> > +
> > +/*
> > + * ima_add_violation - add violation to measurement list.
> > + *
> > + * Violations are flagged in the measurement list with zero hash values.
> > + * By extending the PCR with 0xFF's instead of with zeroes, the PCR
> > + * value is invalidated.
> > + */
> > +void ima_add_violation(struct inode *inode, const unsigned char *filename,
> > +		       const char *op, const char *cause)
> > +{
> > +	struct ima_template_data template;
> > +	int violation = 1;
> > +	int result;
> > +
> > +	/* can overflow, only indicator */
> > +	atomic_long_inc(&ima_htable.violations);
> > +
> > +	memset(&template, 0, sizeof(template));
> > +	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
> > +	result = ima_store_template(&template, violation, inode);
> > +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
> > +			    op, cause, result, 0);
> > +}
> > +
> > +/**
> > + * ima_must_measure - measure decision based on policy.
> > + * @inode: pointer to inode to measure
> > + * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
> > + * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
> > + *
> > + * The policy is defined in terms of keypairs:
> > + * 		subj=, obj=, type=, func=, mask=, fsmagic=
> > + *	subj,obj, and type: are LSM specific.
> > + * 	func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
> > + * 	mask: contains the permission mask
> > + *	fsmagic: hex value
> > + *
> > + * Must be called with iint->mutex held.
> > + *
> > + * Return 0 to measure. Return 1 if already measured.
> > + * For matching a DONT_MEASURE policy, no policy, or other
> > + * error, return an error code.
> > +*/
> > +int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
> > +		     int mask, int function)
> > +{
> > +	int must_measure;
> > +
> > +	if (iint->flags & IMA_MEASURED)
> > +		return 1;
> > +
> > +	must_measure = ima_match_policy(inode, function, mask);
> > +	return must_measure ? 0 : -EACCES;
> > +}
> > +
> > +/*
> > + * ima_collect_measurement - collect file measurement
> > + *
> > + * Calculate the file hash, if it doesn't already exist,
> > + * storing the measurement and i_version in the iint.
> > + *
> > + * Must be called with iint->mutex held.
> > + *
> > + * Return 0 on success, error code otherwise
> > + */
> > +int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
> > +{
> > +	int result = -EEXIST;
> > +
> > +	if (!(iint->flags & IMA_MEASURED)) {
> > +		u64 i_version = file->f_dentry->d_inode->i_version;
> > +
> > +		memset(iint->digest, 0, IMA_DIGEST_SIZE);
> > +		result = ima_calc_hash(file, iint->digest);
> > +		if (!result)
> > +			iint->version = i_version;
> > +	}
> > +	return result;
> > +}
> > +
> > +/*
> > + * ima_store_measurement - store file measurement
> > + *
> > + * Create an "ima" template and then store the template by calling
> > + * ima_store_template.
> > + *
> > + * We only get here if the inode has not already been measured,
> > + * but the measurement could already exist:
> > + * 	- multiple copies of the same file on either the same or
> > + *	  different filesystems.
> > + *	- the inode was previously flushed as well as the iint info,
> > + *	  containing the hashing info.
> > + *
> > + * Must be called with iint->mutex held.
> > + */
> > +void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
> > +			   const unsigned char *filename)
> > +{
> > +
> > +	struct inode *inode = file->f_dentry->d_inode;
> > +	struct ima_template_data template;
> > +	int violation = 0;
> > +	int result;
> > +
> > +	memset(&template, 0, sizeof(template));
> > +	memcpy(template.digest, iint->digest, IMA_DIGEST_SIZE);
> > +	strncpy(template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
> > +
> > +	result = ima_store_template(&template, violation, inode);
> 
> Is there any advantage to copying the data onto the
> stack here and passing that to ima_store_template(),
> instead of just kmallocing a ima_template_entry right
> here, just filling in the digest and file_name, and
> passing those to iam_store_template()?

It would only make a difference if other types of templates were
supported, as that is not the case, you're right. It might as well be
done here.

> > +	if (!result)
> > +		iint->flags |= IMA_MEASURED;
> > +}
> > diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
> > new file mode 100644
> > index 0000000..8a0f1e2
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_audit.c
> > @@ -0,0 +1,78 @@
> > +/*
> > + * Copyright (C) 2008 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.
> > + *
> > + * File: integrity_audit.c
> > + * 	Audit calls for the integrity subsystem
> > + */
> > +
> > +#include <linux/fs.h>
> > +#include <linux/audit.h>
> > +#include "ima.h"
> > +
> > +static int ima_audit;
> > +
> > +#ifdef CONFIG_IMA_AUDIT
> > +
> > +/* ima_audit_setup - enable informational auditing messages */
> > +static int __init ima_audit_setup(char *str)
> > +{
> > +	unsigned long audit;
> > +	int rc;
> > +	char *op;
> > +
> > +	rc = strict_strtoul(str, 0, &audit);
> > +	if (rc || audit > 1)
> > +		printk(KERN_INFO "ima: invalid ima_audit value\n");
> > +	else
> > +		ima_audit = audit;
> > +	op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled";
> > +	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0);
> > +	return 1;
> > +}
> > +__setup("ima_audit=", ima_audit_setup);
> > +#endif
> > +
> > +void integrity_audit_msg(int audit_msgno, struct inode *inode,
> > +			 const unsigned char *fname, const char *op,
> > +			 const char *cause, int result, int audit_info)
> > +{
> > +	struct audit_buffer *ab;
> > +
> > +	if (!ima_audit && audit_info == 1) /* Skip informational messages */
> > +		return;
> > +
> > +	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
> > +	audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u",
> > +			 current->pid, current->cred->uid,
> > +			 audit_get_loginuid(current));
> > +	audit_log_task_context(ab);
> > +	switch (audit_msgno) {
> > +	case AUDIT_INTEGRITY_DATA:
> > +	case AUDIT_INTEGRITY_METADATA:
> > +	case AUDIT_INTEGRITY_PCR:
> > +		audit_log_format(ab, " op=%s cause=%s", op, cause);
> > +		break;
> > +	case AUDIT_INTEGRITY_HASH:
> > +		audit_log_format(ab, " op=%s hash=%s", op, cause);
> > +		break;
> > +	case AUDIT_INTEGRITY_STATUS:
> > +	default:
> > +		audit_log_format(ab, " op=%s", op);
> > +	}
> > +	audit_log_format(ab, " comm=");
> > +	audit_log_untrustedstring(ab, current->comm);
> > +	if (fname) {
> > +		audit_log_format(ab, " name=");
> > +		audit_log_untrustedstring(ab, fname);
> > +	}
> > +	if (inode)
> > +		audit_log_format(ab, " dev=%s ino=%lu",
> > +				 inode->i_sb->s_id, inode->i_ino);
> > +	audit_log_format(ab, " res=%d", result);
> > +	audit_log_end(ab);
> > +}
> > diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
> > new file mode 100644
> > index 0000000..c2a46e4
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_crypto.c
> > @@ -0,0 +1,140 @@
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 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: ima_crypto.c
> > + * 	Calculates md5/sha1 file hash, template hash, boot-aggreate hash
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/file.h>
> > +#include <linux/crypto.h>
> > +#include <linux/scatterlist.h>
> > +#include <linux/err.h>
> > +#include "ima.h"
> > +
> > +static int init_desc(struct hash_desc *desc)
> > +{
> > +	int rc;
> > +
> > +	desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
> > +	if (IS_ERR(desc->tfm)) {
> > +		pr_info("failed to load %s transform: %ld\n",
> > +			ima_hash, PTR_ERR(desc->tfm));
> > +		rc = PTR_ERR(desc->tfm);
> > +		return rc;
> > +	}
> > +	desc->flags = 0;
> > +	rc = crypto_hash_init(desc);
> > +	if (rc)
> > +		crypto_free_hash(desc->tfm);
> > +	return rc;
> > +}
> > +
> > +/*
> > + * Calculate the MD5/SHA1 file digest
> > + */
> > +int ima_calc_hash(struct file *file, char *digest)
> > +{
> > +	struct hash_desc desc;
> > +	struct scatterlist sg[1];
> > +	loff_t i_size;
> > +	char *rbuf;
> > +	int rc, offset = 0;
> > +
> > +	rc = init_desc(&desc);
> > +	if (rc != 0)
> > +		return rc;
> > +
> > +	rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> > +	if (!rbuf) {
> > +		rc = -ENOMEM;
> > +		goto out;
> > +	}
> > +	i_size = i_size_read(file->f_dentry->d_inode);
> > +	while (offset < i_size) {
> > +		int rbuf_len;
> > +
> > +		rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
> > +		if (rbuf_len < 0) {
> > +			rc = rbuf_len;
> > +			break;
> > +		}
> > +		offset += rbuf_len;
> > +		sg_set_buf(sg, rbuf, rbuf_len);
> > +
> > +		rc = crypto_hash_update(&desc, sg, rbuf_len);
> > +		if (rc)
> > +			break;
> > +	}
> > +	kfree(rbuf);
> > +	if (!rc)
> > +		rc = crypto_hash_final(&desc, digest);
> > +out:
> > +	crypto_free_hash(desc.tfm);
> > +	return rc;
> > +}
> > +
> > +/*
> > + * Calculate the hash of a given template
> > + */
> > +int ima_calc_template_hash(int template_len, void *template, char *digest)
> > +{
> > +	struct hash_desc desc;
> > +	struct scatterlist sg[1];
> > +	int rc;
> > +
> > +	rc = init_desc(&desc);
> > +	if (rc != 0)
> > +		return rc;
> > +
> > +	sg_set_buf(sg, template, template_len);
> > +	rc = crypto_hash_update(&desc, sg, template_len);
> > +	if (!rc)
> > +		rc = crypto_hash_final(&desc, digest);
> > +	crypto_free_hash(desc.tfm);
> > +	return rc;
> > +}
> > +
> > +static void ima_pcrread(int idx, u8 *pcr)
> > +{
> > +	if (!ima_used_chip)
> > +		return;
> > +
> > +	if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
> > +		pr_err("Error Communicating to TPM chip\n");
> > +}
> > +
> > +/*
> > + * Calculate the boot aggregate hash
> > + */
> > +int ima_calc_boot_aggregate(char *digest)
> > +{
> > +	struct hash_desc desc;
> > +	struct scatterlist sg;
> > +	u8 pcr_i[IMA_DIGEST_SIZE];
> > +	int rc, i;
> > +
> > +	rc = init_desc(&desc);
> > +	if (rc != 0)
> > +		return rc;
> > +
> > +	/* cumulative sha1 over tpm registers 0-7 */
> 
> (Just out of curiosity) why?  Are these the ones the BIOS and grub
> will use by convention?

Yes.  The PCR aggregate values 0-7 can be re-calculated based
on: /sys/kernel/security/tpm0/binary_bios_measurements.

> > +	for (i = TPM_PCR0; i < TPM_PCR8; i++) {
> > +		ima_pcrread(i, pcr_i);
> > +		/* now accumulate with current aggregate */
> > +		sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
> > +		rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
> > +	}
> > +	if (!rc)
> > +		crypto_hash_final(&desc, digest);
> > +	crypto_free_hash(desc.tfm);
> > +	return rc;
> > +}
> > diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
> > new file mode 100644
> > index 0000000..750db3c
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_iint.c
> > @@ -0,0 +1,185 @@
> > +/*
> > + * 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 radix tree.
> > + */
> > +#include <linux/module.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/radix-tree.h>
> > +#include "ima.h"
> > +
> > +#define ima_iint_delete ima_inode_free
> > +
> > +RADIX_TREE(ima_iint_store, GFP_ATOMIC);
> > +DEFINE_SPINLOCK(ima_iint_lock);
> > +
> > +static struct kmem_cache *iint_cache __read_mostly;
> > +
> > +/* ima_iint_find_get - return the iint associated with an inode
> > + *
> > + * ima_iint_find_get gets a reference to the iint. Caller must
> > + * remember to put the iint reference.
> > + */
> > +struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
> > +{
> > +	struct ima_iint_cache *iint;
> > +
> > +	rcu_read_lock();
> > +	iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
> > +	if (!iint)
> > +		goto out;
> > +	kref_get(&iint->refcount);
> > +out:
> > +	rcu_read_unlock();
> > +	return iint;
> > +}
> > +
> > +/* Allocate memory for the iint associated with the inode
> > + * from the iint_cache slab, initialize the iint, and
> > + * insert it into the radix tree.
> > + *
> > + * On success return a pointer to the iint; on failure return NULL.
> > + */
> > +struct ima_iint_cache *ima_iint_insert(struct inode *inode)
> > +{
> > +	struct ima_iint_cache *iint = NULL;
> > +	int rc = 0;
> > +
> > +	if (!ima_initialized)
> > +		return iint;
> > +	iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
> > +	if (!iint)
> > +		return iint;
> > +
> > +	rc = radix_tree_preload(GFP_KERNEL);
> > +	if (rc < 0)
> > +		goto out;
> > +
> > +	spin_lock(&ima_iint_lock);
> > +	rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
> > +	spin_unlock(&ima_iint_lock);
> > +out:
> > +	if (rc < 0) {
> > +		kmem_cache_free(iint_cache, iint);
> > +		if (rc == -EEXIST) {
> > +			iint = radix_tree_lookup(&ima_iint_store,
> > +						 (unsigned long)inode);
> > +		} else
> > +			iint = NULL;
> > +	}
> > +	radix_tree_preload_end();
> > +	return iint;
> > +}
> > +
> > +/**
> > + * ima_inode_alloc - allocate an iint associated with an inode
> > + * @inode: pointer to the inode
> > + *
> > + * Return 0 on success, 1 on failure.
> > + */
> > +int ima_inode_alloc(struct inode *inode)
> > +{
> > +	struct ima_iint_cache *iint;
> > +
> > +	if (!ima_initialized)
> > +		return 0;
> > +
> > +	iint = ima_iint_insert(inode);
> > +	if (!iint)
> > +		return 1;
> > +	return 0;
> > +}
> > +
> > +/* ima_iint_find_insert_get - get the iint associated with an inode
> > + *
> > + * Most insertions are done at inode_alloc, except those allocated
> > + * before late_initcall. When the iint does not exist, allocate it,
> > + * initialize and insert it, and increment the iint refcount.
> > + *
> > + * (Can't initialize at security_initcall before any inodes are
> > + * allocated, got to wait at least until proc_init.)
> > + *
> > + *  Return the iint.
> > + */
> > +struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
> > +{
> > +	struct ima_iint_cache *iint = NULL;
> > +
> > +	iint = ima_iint_find_get(inode);
> > +	if (iint)
> > +		return iint;
> > +
> > +	iint = ima_iint_insert(inode);
> > +	if (iint)
> > +		kref_get(&iint->refcount);
> > +
> > +	return iint;
> > +}
> > +
> > +/* iint_free - called when the iint refcount goes to zero */
> > +void iint_free(struct kref *kref)
> > +{
> > +	struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
> > +						   refcount);
> > +	iint->version = 0;
> > +	iint->flags = 0UL;
> > +	kref_set(&iint->refcount, 1);
> > +	kmem_cache_free(iint_cache, iint);
> > +}
> > +
> > +void iint_rcu_free(struct rcu_head *rcu_head)
> > +{
> > +	struct ima_iint_cache *iint = container_of(rcu_head,
> > +						   struct ima_iint_cache, rcu);
> > +	kref_put(&iint->refcount, iint_free);
> > +}
> > +
> > +/**
> > + * ima_iint_delete - called on integrity_inode_free
> > + * @inode: pointer to the inode
> > + *
> > + * Free the integrity information(iint) associated with an inode.
> > + */
> > +void ima_iint_delete(struct inode *inode)
> > +{
> > +	struct ima_iint_cache *iint;
> > +
> > +	if (!ima_initialized)
> > +		return;
> > +	spin_lock(&ima_iint_lock);
> > +	iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
> > +	spin_unlock(&ima_iint_lock);
> > +	if (iint)
> > +		call_rcu(&iint->rcu, iint_rcu_free);
> > +}
> > +
> > +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);
> > +	iint->readcount = 0;
> > +	iint->writecount = 0;
> > +	kref_set(&iint->refcount, 1);
> > +}
> > +
> > +void ima_iintcache_init(void)
> > +{
> > +	iint_cache =
> > +	    kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
> > +			      SLAB_PANIC, init_once);
> > +}
> > diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
> > new file mode 100644
> > index 0000000..e681a40
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_init.c
> > @@ -0,0 +1,73 @@
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + *
> > + * Authors:
> > + * Reiner Sailer      <sailer@watson.ibm.com>
> > + * Leendert van Doorn <leendert@watson.ibm.com>
> > + * 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_init.c
> > + *             initialization and cleanup functions
> > + */
> > +#include <linux/module.h>
> > +#include <linux/scatterlist.h>
> > +#include <linux/err.h>
> > +#include "ima.h"
> > +
> > +/* name for boot aggregate entry */
> > +static char *boot_aggregate_name = "boot_aggregate";
> > +int ima_used_chip;
> > +
> > +/* Add the boot aggregate to the IMA measurement list and extend
> > + * the PCR register.
> > + *
> > + * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
> > + * assuming a TPM chip exists, and zeroes if the TPM chip does not
> > + * exist.  Add the boot aggregate measurement to the measurement
> > + * list and extend the PCR register.
> > + *
> > + * If a tpm chip does not exist, indicate the core root of trust is
> > + * not hardware based by invalidating the aggregate PCR value.
> > + * (The aggregate PCR value is invalidated by adding one value to
> > + * the measurement list and extending the aggregate PCR value with
> > + * a different value.) Violations add a zero entry to the measurement
> > + * list and extend the aggregate PCR value with ff...ff's.
> > + */
> > +static void ima_add_boot_aggregate(void)
> > +{
> > +	struct ima_template_data template;
> > +	int violation = 1;
> > +	int result;
> > +
> > +	memset(&template, 0, sizeof(template));
> > +	strncpy(template.file_name, boot_aggregate_name,
> > +		IMA_EVENT_NAME_LEN_MAX);
> > +
> > +	if (ima_used_chip) {
> > +		violation = 0;
> > +		ima_calc_boot_aggregate(template.digest);
> 
> You're ignoring the return value of ima_calc_boot_aggregate...
> 
> > +	}
> > +	result = ima_store_template(&template, violation, NULL);
> 
> ...and effectively ignoring result?
> 
> I realize it shouldn't ever matter...

Right, but there should at least be an error message noting it.

> > +}
> > +
> > +int ima_init(void)
> > +{
> > +	int rc;
> > +
> > +	ima_used_chip = 0;
> > +	rc = tpm_pcr_read(TPM_ANY_NUM, 0, NULL);
> > +	if (rc == 0)
> > +		ima_used_chip = 1;
> > +
> > +	if (!ima_used_chip)
> > +		pr_info("No TPM chip found, activating TPM-bypass!\n");
> > +
> > +	ima_add_boot_aggregate();	/* boot aggregate must be first entry */
> > +	ima_init_policy();
> > +	return 0;
> > +}
> > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> > new file mode 100644
> > index 0000000..53cee4c
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_main.c
> > @@ -0,0 +1,280 @@
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + *
> > + * Authors:
> > + * Reiner Sailer <sailer@watson.ibm.com>
> > + * Serge Hallyn <serue@us.ibm.com>
> > + * Kylene Hall <kylene@us.ibm.com>
> > + * 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_main.c
> > + *             implements the IMA hooks: ima_bprm_check, ima_file_mmap,
> > + *             and ima_path_check.
> > + */
> > +#include <linux/module.h>
> > +#include <linux/file.h>
> > +#include <linux/binfmts.h>
> > +#include <linux/mount.h>
> > +#include <linux/mman.h>
> > +
> > +#include "ima.h"
> > +
> > +int ima_initialized;
> > +
> > +char *ima_hash = "sha1";
> > +static int __init hash_setup(char *str)
> > +{
> > +	const char *op = "hash_setup";
> > +	const char *hash = "sha1";
> > +	int result = 0;
> > +	int audit_info = 0;
> > +
> > +	if (strncmp(str, "md5", 3) == 0) {
> > +		hash = "md5";
> > +		ima_hash = str;
> > +	} else if (strncmp(str, "sha1", 4) != 0) {
> > +		hash = "invalid_hash_type";
> > +		result = 1;
> > +	}
> > +	integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
> > +			    result, audit_info);
> > +	return 1;
> > +}
> > +__setup("ima_hash=", hash_setup);
> > +
> > +/**
> > + * ima_file_free - called on __fput()
> > + * @file: pointer to file structure being freed
> > + *
> > + * Flag files that changed, based on i_version;
> > + * and decrement the iint readcount/writecount.
> > + */
> > +void ima_file_free(struct file *file)
> > +{
> > +	struct inode *inode = file->f_dentry->d_inode;
> > +	struct ima_iint_cache *iint;
> > +
> > +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> > +		return;
> > +	iint = ima_iint_find_get(inode);
> > +	if (!iint)
> > +		return;
> > +
> > +	mutex_lock(&iint->mutex);
> > +	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
> > +		iint->readcount--;
> > +
> > +	if (file->f_mode & FMODE_WRITE) {
> > +		iint->writecount--;
> > +		if (iint->writecount == 0) {
> > +			if (iint->version != inode->i_version)
> > +				iint->flags &= ~IMA_MEASURED;
> > +		}
> > +	}
> > +	mutex_unlock(&iint->mutex);
> > +	kref_put(&iint->refcount, iint_free);
> > +}
> > +
> > +/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
> > + *
> > + * When opening a file for read, if the file is already open for write,
> > + * the file could change, resulting in a file measurement error.
> > + *
> > + * Opening a file for write, if the file is already open for read, results
> > + * in a time of measure, time of use (ToMToU) error.
> > + *
> > + * In either case invalidate the PCR.
> > + */
> > +enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
> > +static void ima_read_write_check(enum iint_pcr_error error,
> > +				 struct ima_iint_cache *iint,
> > +				 struct inode *inode,
> > +				 const unsigned char *filename)
> > +{
> > +	switch (error) {
> > +	case TOMTOU:
> > +		if (iint->readcount > 0)
> > +			ima_add_violation(inode, filename, "invalid_pcr",
> > +					  "ToMToU");
> > +		break;
> > +	case OPEN_WRITERS:
> > +		if (iint->writecount > 0)
> > +			ima_add_violation(inode, filename, "invalid_pcr",
> > +					  "open_writers");
> > +		break;
> > +	}
> > +}
> > +
> > +static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
> > +				const unsigned char *filename)
> > +{
> > +	int rc = 0;
> > +
> > +	if (IS_ERR(file)) {
> > +		pr_info("%s dentry_open failed\n", filename);
> > +		return rc;
> > +	}
> > +	iint->readcount++;
> > +
> > +	rc = ima_collect_measurement(iint, file);
> > +	if (!rc)
> > +		ima_store_measurement(iint, file, filename);
> > +	return rc;
> > +}
> > +
> > +/**
> > + * ima_path_check - based on policy, collect/store measurement.
> > + * @path: contains a pointer to the path to be measured
> > + * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
> > + *
> > + * Measure the file being open for readonly, based on the
> > + * ima_must_measure() policy decision.
> > + *
> > + * Keep read/write counters for all files, but only
> > + * invalidate the PCR for measured files:
> > + * 	- Opening a file for write when already open for read,
> > + *	  results in a time of measure, time of use (ToMToU) error.
> > + *	- Opening a file for read when already open for write,
> > + * 	  could result in a file measurement error.
> > + *
> > + * Return 0 on success, an error code on failure.
> > + * (Based on the results of appraise_measurement().)
> > + */
> > +int ima_path_check(struct path *path, int mask)
> > +{
> > +	struct inode *inode = path->dentry->d_inode;
> > +	struct ima_iint_cache *iint;
> > +	struct file *file = NULL;
> > +	int rc;
> > +
> > +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> > +		return 0;
> > +	iint = ima_iint_find_insert_get(inode);
> > +	if (!iint)
> > +		return 0;
> > +
> > +	mutex_lock(&iint->mutex);
> > +	if ((mask & MAY_WRITE) || (mask == 0))
> > +		iint->writecount++;
> > +	else if (mask & (MAY_READ | MAY_EXEC))
> > +		iint->readcount++;
> > +
> > +	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
> 
> why MAY_READ, if mask could have MAY_WRITE?

We only want to emit ToMToU/open_writer messages for measured files.

> > +	if (rc < 0)
> > +		goto out;
> > +
> > +	if ((mask & MAY_WRITE) || (mask == 0))
> > +		ima_read_write_check(TOMTOU, iint, inode,
> > +				     path->dentry->d_name.name);
> > +
> > +	if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
> > +		goto out;
> > +
> > +	ima_read_write_check(OPEN_WRITERS, iint, inode,
> > +			     path->dentry->d_name.name);
> > +	if (!(iint->flags & IMA_MEASURED)) {
> > +		struct dentry *dentry = dget(path->dentry);
> > +		struct vfsmount *mnt = mntget(path->mnt);
> > +
> > +		file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
> > +		rc = get_path_measurement(iint, file, dentry->d_name.name);
> > +	}
> > +out:
> > +	mutex_unlock(&iint->mutex);
> > +	if (file)
> > +		fput(file);
> > +	kref_put(&iint->refcount, iint_free);
> > +	return 0;
> > +}
> > +
> > +static int process_measurement(struct file *file, const unsigned char *filename,
> > +			       int mask, int function)
> > +{
> > +	struct inode *inode = file->f_dentry->d_inode;
> > +	struct ima_iint_cache *iint;
> > +	int rc;
> > +
> > +	if (!ima_initialized || !S_ISREG(inode->i_mode))
> > +		return 0;
> > +	iint = ima_iint_find_insert_get(inode);
> > +	if (!iint)
> > +		return -ENOMEM;
> > +
> > +	mutex_lock(&iint->mutex);
> > +	rc = ima_must_measure(iint, inode, mask, function);
> > +	if (rc != 0)
> > +		goto out;
> > +
> > +	rc = ima_collect_measurement(iint, file);
> > +	if (!rc)
> > +		ima_store_measurement(iint, file, filename);
> > +out:
> > +	mutex_unlock(&iint->mutex);
> > +	kref_put(&iint->refcount, iint_free);
> > +	return rc;
> > +}
> > +
> > +/**
> > + * ima_file_mmap - based on policy, collect/store measurement.
> > + * @file: pointer to the file to be measured (May be NULL)
> > + * @prot: contains the protection that will be applied by the kernel.
> > + *
> > + * Measure files being mmapped executable based on the ima_must_measure()
> > + * policy decision.
> > + *
> > + * Return 0 on success, an error code on failure.
> > + * (Based on the results of appraise_measurement().)
> > + */
> > +int ima_file_mmap(struct file *file, unsigned long prot)
> > +{
> > +	int rc;
> > +
> > +	if (!file)
> > +		return 0;
> > +	if (prot & PROT_EXEC)
> > +		rc = process_measurement(file, file->f_dentry->d_name.name,
> > +					 MAY_EXEC, FILE_MMAP);
> > +	return 0;
> > +}
> > +
> > +/**
> > + * ima_bprm_check - based on policy, collect/store measurement.
> > + * @bprm: contains the linux_binprm structure
> > + *
> > + * The OS protects against an executable file, already open for write,
> > + * from being executed in deny_write_access() and an executable file,
> > + * already open for execute, from being modified in get_write_access().
> > + * So we can be certain that what we verify and measure here is actually
> > + * what is being executed.
> > + *
> > + * Return 0 on success, an error code on failure.
> > + * (Based on the results of appraise_measurement().)
> > + */
> > +int ima_bprm_check(struct linux_binprm *bprm)
> > +{
> > +	int rc;
> > +
> > +	rc = process_measurement(bprm->file, bprm->filename,
> > +				 MAY_EXEC, BPRM_CHECK);
> > +	return 0;

Yes, I know this looks weird, but a return code here should indicate
lack of integrity, which this doesn't.  Measurement appraisal,
ima_appraise_measurement(), still needs to be added.

> 
> > +}
> > +
> > +static int __init init_ima(void)
> > +{
> > +	int error;
> > +
> > +	ima_iintcache_init();
> > +	error = ima_init();
> > +	ima_initialized = 1;
> > +	return error;
> > +}
> > +
> > +late_initcall(init_ima);	/* Start IMA after the TPM is available */
> > +
> > +MODULE_DESCRIPTION("Integrity Measurement Architecture");
> > +MODULE_LICENSE("GPL");
> > diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> > new file mode 100644
> > index 0000000..7c3d1ff
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_policy.c
> > @@ -0,0 +1,126 @@
> > +/*
> > + * Copyright (C) 2008 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.
> > + *
> > + * ima_policy.c
> > + * 	- initialize default measure policy rules
> > + *
> > + */
> > +#include <linux/module.h>
> > +#include <linux/list.h>
> > +#include <linux/audit.h>
> > +#include <linux/security.h>
> > +#include <linux/magic.h>
> > +
> > +#include "ima.h"
> > +
> > +/* flags definitions */
> > +#define IMA_FUNC 	0x0001
> > +#define IMA_MASK 	0x0002
> > +#define IMA_FSMAGIC	0x0004
> > +#define IMA_UID		0x0008
> > +
> > +enum ima_action { DONT_MEASURE, MEASURE };
> > +
> > +struct ima_measure_rule_entry {
> > +	struct list_head list;
> > +	enum ima_action action;
> > +	unsigned int flags;
> > +	enum ima_hooks func;
> > +	int mask;
> > +	unsigned long fsmagic;
> > +	uid_t uid;
> > +};
> > +
> > +static struct ima_measure_rule_entry default_rules[] = {
> > +	{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
> > +	 .flags = IMA_FSMAGIC},
> > +	{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
> > +	{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
> > +	{.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
> > +	{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
> > +	 .flags = IMA_FSMAGIC},
> > +	{.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
> > +	{.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
> > +	 .flags = IMA_FUNC | IMA_MASK},
> > +	{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
> > +	 .flags = IMA_FUNC | IMA_MASK},
> > +	{.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
> > +	 .flags = IMA_FUNC | IMA_MASK | IMA_UID}
> > +};
> > +
> > +static LIST_HEAD(measure_default_rules);
> > +static struct list_head *ima_measure;
> > +
> > +/**
> > + * ima_match_rules - determine whether an inode matches the measure rule.
> > + * @rule: a pointer to a rule
> > + * @inode: a pointer to an inode
> > + * @func: LIM hook identifier
> > + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
> > + *
> > + * Returns true on rule match, false on failure.
> > + */
> > +static bool ima_match_rules(struct ima_measure_rule_entry *rule,
> > +			    struct inode *inode, enum ima_hooks func, int mask)
> > +{
> > +	struct task_struct *tsk = current;
> > +
> > +	if ((rule->flags & IMA_FUNC) && rule->func != func)
> > +		return false;
> > +	if ((rule->flags & IMA_MASK) && rule->mask != mask)
> > +		return false;
> > +	if ((rule->flags & IMA_FSMAGIC)
> > +	    && rule->fsmagic != inode->i_sb->s_magic)
> > +		return false;
> > +	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
> > +		return false;
> > +	return true;
> > +}
> > +
> > +/**
> > + * ima_match_policy - decision based on LSM and other conditions
> > + * @inode: pointer to an inode for which the policy decision is being made
> > + * @func: IMA hook identifier
> > + * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
> > + *
> > + * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
> > + * conditions.
> > + *
> > + * (There is no need for locking when walking the policy list,
> > + * as elements in the list are never deleted, nor does the list
> > + * change.)
> > + */
> > +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
> > +{
> > +	struct ima_measure_rule_entry *entry;
> > +
> > +	list_for_each_entry(entry, ima_measure, list) {
> 
> The list never changes, but  once per boot the value of ima_measure
> itself can change (at least after a later patch).  Does that matter?
> Could you end up with a corrupt value here?

As the lists themselves never change (the list entries are never freed),
changing ima_measure to point to a different list shouldn't affect
walking a list.

> > +		bool rc;
> > +
> > +		rc = ima_match_rules(entry, inode, func, mask);
> > +		if (rc)
> > +			return entry->action;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * ima_init_policy - initialize the default measure rules.
> > + *
> > + * (Could use the default_rules directly, but in policy patch
> > + * ima_measure points to either the measure_default_rules or the
> > + * the new measure_policy_rules.)
> > + */
> > +void ima_init_policy(void)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(default_rules); i++)
> > +		list_add_tail(&default_rules[i].list, &measure_default_rules);
> > +	ima_measure = &measure_default_rules;
> > +}
> > diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> > new file mode 100644
> > index 0000000..7cb518f
> > --- /dev/null
> > +++ b/security/integrity/ima/ima_queue.c
> > @@ -0,0 +1,138 @@
> > +/*
> > + * Copyright (C) 2005,2006,2007,2008 IBM Corporation
> > + *
> > + * Authors:
> > + * Serge Hallyn <serue@us.ibm.com>
> > + * Reiner Sailer <sailer@watson.ibm.com>
> > + * 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_queue.c
> > + *       Implements queues that store template measurements and
> > + *       maintains aggregate over the stored measurements
> > + *       in the pre-configured TPM PCR (if available).
> > + *       The measurement list is append-only. No entry is
> > + *       ever removed or changed during the boot-cycle.
> > + */
> > +#include <linux/module.h>
> > +#include <linux/rculist.h>
> > +#include "ima.h"
> > +
> > +LIST_HEAD(ima_measurements);	/* list of all measurements */
> > +
> > +/* key: inode (before secure-hashing a file) */
> > +struct ima_h_table ima_htable = {
> > +	.len = ATOMIC_LONG_INIT(0),
> > +	.violations = ATOMIC_LONG_INIT(0),
> > +	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
> > +};
> > +
> > +/* mutex protects atomicity of extending measurement list
> > + * and extending the TPM PCR aggregate. Since tpm_extend can take
> > + * long (and the tpm driver uses a mutex), we can't use the spinlock.
> > + */
> > +static DEFINE_MUTEX(ima_extend_list_mutex);
> > +
> > +/* lookup up the digest value in the hash table, and return the entry */
> > +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
> > +{
> > +	struct ima_queue_entry *qe, *ret = NULL;
> > +	unsigned int key;
> > +	struct hlist_node *pos;
> > +
> > +	key = ima_hash_key(digest_value);
> > +	rcu_read_lock();
> > +	hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
> > +		if (memcmp(qe->entry->digest, digest_value, 20) == 0) {
> > +			ret = qe;
> > +			break;
> > +		}
> > +	}
> > +	rcu_read_unlock();
> > +	return ret;
> > +}
> > +
> > +/* ima_add_template_entry helper function:
> > + * - Add template entry to measurement list and hash table.
> > + *
> > + * (Called with ima_extend_list_mutex held.)
> > + */
> > +static int ima_add_digest_entry(struct ima_template_entry *entry)
> > +{
> > +	struct ima_queue_entry *qe;
> > +	unsigned int key;
> > +
> > +	qe = kmalloc(sizeof(*qe), GFP_KERNEL);
> > +	if (qe == NULL) {
> > +		pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
> > +		return -ENOMEM;
> > +	}
> > +	qe->entry = entry;
> > +
> > +	INIT_LIST_HEAD(&qe->later);
> > +	list_add_tail_rcu(&qe->later, &ima_measurements);
> > +
> > +	atomic_long_inc(&ima_htable.len);
> > +	key = ima_hash_key(entry->digest);
> > +	hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
> > +	return 0;
> > +}
> > +
> > +static int ima_pcr_extend(const u8 *hash)
> > +{
> > +	int result = 0;
> > +
> > +	if (!ima_used_chip)
> > +		return result;
> > +
> > +	result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
> > +	if (result != 0)
> > +		pr_err("Error Communicating to TPM chip\n");
> > +	return result;
> > +}
> > +
> > +/* Add template entry to the measurement list and hash table,
> > + * and extend the pcr.
> > + */
> > +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> > +			   const char *op, struct inode *inode)
> > +{
> > +	u8 digest[IMA_DIGEST_SIZE];
> > +	const char *audit_cause = "hash_added";
> > +	int audit_info = 1;
> > +	int result = 0;
> > +
> > +	mutex_lock(&ima_extend_list_mutex);
> > +	if (!violation) {
> > +		memcpy(digest, entry->digest, sizeof digest);
> > +		if (ima_lookup_digest_entry(digest)) {
> > +			audit_cause = "hash_exists";
> > +			goto out;
> 
> Ok so not that I'm saying this would be easy, but an attacker
> compromising say ftpd doesn't need to come up with a compromised
> ftpd where sha1sum(evilftpd)==sha1sum(origftpd) - he just needs to
> come up with one wher sha1sum(evilftpd)==sha1sum(X) where X is
> any pristine program already loaded.  Right?
> 
> Is checking that strcmp(entry->file_name, newfilename)==0 warranted
> here, or am I being silly?

Perhaps in isolated cases it would make a difference if the same hash
value exists multiple times in the measurement list, but the vast
majority of duplicate hash entries would be for the same inode, with
just a different name (i.e. links).

> > +		}
> > +	}
> > +
> > +	result = ima_add_digest_entry(entry);
> > +	if (result < 0) {
> > +		audit_cause = "ENOMEM";
> > +		audit_info = 0;
> > +		goto out;
> > +	}
> > +
> > +	if (violation)		/* invalidate pcr */
> > +		memset(digest, 0xff, sizeof digest);
> > +
> > +	result = ima_pcr_extend(digest);
> > +	if (result != 0) {
> > +		audit_cause = "TPM error";
> > +		audit_info = 0;
> > +	}
> > +out:
> > +	mutex_unlock(&ima_extend_list_mutex);
> > +	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
> > +			    op, audit_cause, result, audit_info);
> > +	return result;
> > +}
> > -- 
> > 1.5.6.6
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at  http://www.tux.org/lkml/


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-02-02 23:02   ` Serge E. Hallyn
  2009-02-03  2:09     ` Mimi Zohar
@ 2009-02-03 13:36     ` david safford
  2009-02-03 14:03       ` Serge E. Hallyn
  1 sibling, 1 reply; 27+ messages in thread
From: david safford @ 2009-02-03 13:36 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: Mimi Zohar, linux-kernel, Andrew Morton, James Morris,
	Christoph Hellwig, Dave Hansen, Serge Hallyn, Mimi Zohar

On Mon, 2009-02-02 at 17:02 -0600, Serge E. Hallyn wrote:
> Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> > +
> > +/* Add template entry to the measurement list and hash table,
> > + * and extend the pcr.
> > + */
> > +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> > +			   const char *op, struct inode *inode)
> > +{
> > +	u8 digest[IMA_DIGEST_SIZE];
> > +	const char *audit_cause = "hash_added";
> > +	int audit_info = 1;
> > +	int result = 0;
> > +
> > +	mutex_lock(&ima_extend_list_mutex);
> > +	if (!violation) {
> > +		memcpy(digest, entry->digest, sizeof digest);
> > +		if (ima_lookup_digest_entry(digest)) {
> > +			audit_cause = "hash_exists";
> > +			goto out;
> 
> Ok so not that I'm saying this would be easy, but an attacker
> compromising say ftpd doesn't need to come up with a compromised
> ftpd where sha1sum(evilftpd)==sha1sum(origftpd) - he just needs to
> come up with one wher sha1sum(evilftpd)==sha1sum(X) where X is
> any pristine program already loaded.  Right?
> 
> Is checking that strcmp(entry->file_name, newfilename)==0 warranted
> here, or am I being silly?

The birthday paradox, which is essential for any practical attack,
requires that the attacker can freely choose both x and y in 
his search for collisions sha1sum(x) == sha1sum(y). Here, the
attacker cannot freely choose x, but can only choose x from a list
of existing files. Even if there are a million valid files, that only
reduces the attack complexity by 2^20, which is still infeasible.

dave


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/6] integrity: IMA as an integrity service provider
  2009-02-03 13:36     ` david safford
@ 2009-02-03 14:03       ` Serge E. Hallyn
  0 siblings, 0 replies; 27+ messages in thread
From: Serge E. Hallyn @ 2009-02-03 14:03 UTC (permalink / raw)
  To: david safford
  Cc: Mimi Zohar, linux-kernel, Andrew Morton, James Morris,
	Christoph Hellwig, Dave Hansen, Serge Hallyn, Mimi Zohar

Quoting david safford (safford@watson.ibm.com):
> On Mon, 2009-02-02 at 17:02 -0600, Serge E. Hallyn wrote:
> > Quoting Mimi Zohar (zohar@linux.vnet.ibm.com):
> > > +
> > > +/* Add template entry to the measurement list and hash table,
> > > + * and extend the pcr.
> > > + */
> > > +int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> > > +			   const char *op, struct inode *inode)
> > > +{
> > > +	u8 digest[IMA_DIGEST_SIZE];
> > > +	const char *audit_cause = "hash_added";
> > > +	int audit_info = 1;
> > > +	int result = 0;
> > > +
> > > +	mutex_lock(&ima_extend_list_mutex);
> > > +	if (!violation) {
> > > +		memcpy(digest, entry->digest, sizeof digest);
> > > +		if (ima_lookup_digest_entry(digest)) {
> > > +			audit_cause = "hash_exists";
> > > +			goto out;
> > 
> > Ok so not that I'm saying this would be easy, but an attacker
> > compromising say ftpd doesn't need to come up with a compromised
> > ftpd where sha1sum(evilftpd)==sha1sum(origftpd) - he just needs to
> > come up with one wher sha1sum(evilftpd)==sha1sum(X) where X is
> > any pristine program already loaded.  Right?
> > 
> > Is checking that strcmp(entry->file_name, newfilename)==0 warranted
> > here, or am I being silly?
> 
> The birthday paradox, which is essential for any practical attack,
> requires that the attacker can freely choose both x and y in 
> his search for collisions sha1sum(x) == sha1sum(y). Here, the
> attacker cannot freely choose x,

That's why I said a forward-looking attacker.  He can, in fact,
choose x.

>  but can only choose x from a list
> of existing files. Even if there are a million valid files, that only
> reduces the attack complexity by 2^20, which is still infeasible.
> 
> dave

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] integrity
  2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
                   ` (5 preceding siblings ...)
  2009-01-29 22:23 ` [PATCH 6/6] Integrity: IMA file free imbalance Mimi Zohar
@ 2009-02-06  0:00 ` James Morris
  2009-02-06  2:29   ` Mimi Zohar
  6 siblings, 1 reply; 27+ messages in thread
From: James Morris @ 2009-02-06  0:00 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn

I seem to be getting a bunch of these in my logs with IMA running:

auditd[1746]: dispatch err (Bad address) event lost


-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] integrity
  2009-02-06  0:00 ` [PATCH 0/6] integrity James Morris
@ 2009-02-06  2:29   ` Mimi Zohar
  2009-02-06  8:14     ` James Morris
  0 siblings, 1 reply; 27+ messages in thread
From: Mimi Zohar @ 2009-02-06  2:29 UTC (permalink / raw)
  To: James Morris
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn

On Fri, 2009-02-06 at 11:00 +1100, James Morris wrote:
> I seem to be getting a bunch of these in my logs with IMA running:
> 
> auditd[1746]: dispatch err (Bad address) event lost
> 

auditd needs to reflect the integrity definitions defined in
include/linux/audit.h.  ausearch-parse needs to be updated as well. For
which version of auditd should I post a patch? I have one for
audit-1.7.9.

Thanks!

Mimi



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6] integrity
  2009-02-06  2:29   ` Mimi Zohar
@ 2009-02-06  8:14     ` James Morris
  0 siblings, 0 replies; 27+ messages in thread
From: James Morris @ 2009-02-06  8:14 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: linux-kernel, Andrew Morton, Christoph Hellwig, Dave Hansen,
	<David Safford, Serge Hallyn

On Thu, 5 Feb 2009, Mimi Zohar wrote:

> On Fri, 2009-02-06 at 11:00 +1100, James Morris wrote:
> > I seem to be getting a bunch of these in my logs with IMA running:
> > 
> > auditd[1746]: dispatch err (Bad address) event lost
> > 
> 
> auditd needs to reflect the integrity definitions defined in
> include/linux/audit.h.  ausearch-parse needs to be updated as well. For
> which version of auditd should I post a patch? I have one for
> audit-1.7.9.

I don't know, ask the audit folk.

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6] integrity
@ 2008-12-02 21:47 Mimi Zohar
  0 siblings, 0 replies; 27+ messages in thread
From: Mimi Zohar @ 2008-12-02 21:47 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mimi Zohar, Andrew Morton, James Morris, Christoph Hellwig,
	Al Viro, David Safford, Serge Hallyn

This patchset contains the LIM and IMA integrity system patches.
To address concerns raised on the mailing list of the IMA patch
size, the IMA patch has been broken up into three smaller patches
for easier review.

1 - TPM internal interfaces (unchanged)
2 - LIM - (minor cleanup)
3 - IMA - Main (updates in response to comments)
4 - IMA - securityfs - display (unchanged)
5 - IMA - securityfs - policy (unchanged)
6 - (credential patch for security-testing-2.6/next)

The first five patches apply cleanly to linux-2.6.28-rc6 and with minor
offsets to security-testing-2.6/#next. The last patch addresses the
security-testing-2.6/#next credential merge issues.

Mimi Zohar (6):
  integrity: TPM internel kernel interface
  integrity: Linux Integrity Module(LIM)
  integrity: IMA as an integrity service provider
  integrity: IMA display
  integrity: IMA policy
  integrity: replace task uid with cred uid

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2009-02-06  8:15 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-29 22:23 [PATCH 0/6] integrity Mimi Zohar
2009-01-29 22:23 ` [PATCH 1/6] integrity: IMA hooks Mimi Zohar
2009-01-29 22:23 ` [PATCH 2/6] integrity: IMA as an integrity service provider Mimi Zohar
2009-01-30  0:07   ` James Morris
2009-01-30 19:29     ` Mimi Zohar
2009-01-30  9:04   ` James Morris
2009-01-30 13:13     ` Mimi Zohar
2009-02-02 23:02   ` Serge E. Hallyn
2009-02-03  2:09     ` Mimi Zohar
2009-02-03 13:36     ` david safford
2009-02-03 14:03       ` Serge E. Hallyn
2009-01-29 22:23 ` [PATCH 3/6] integrity: IMA display Mimi Zohar
2009-01-30  9:18   ` James Morris
2009-01-30 13:14     ` Mimi Zohar
2009-02-02 23:14       ` Serge E. Hallyn
2009-02-03  0:03         ` James Morris
2009-01-29 22:23 ` [PATCH 4/6] integrity: IMA policy Mimi Zohar
2009-02-02 23:40   ` Serge E. Hallyn
2009-02-03  1:29     ` Mimi Zohar
2009-01-29 22:23 ` [PATCH 5/6] integrity: IMA policy open Mimi Zohar
2009-01-29 22:23 ` [PATCH 6/6] Integrity: IMA file free imbalance Mimi Zohar
2009-02-02 23:47   ` Serge E. Hallyn
2009-02-03  1:27     ` Mimi Zohar
2009-02-06  0:00 ` [PATCH 0/6] integrity James Morris
2009-02-06  2:29   ` Mimi Zohar
2009-02-06  8:14     ` James Morris
  -- strict thread matches above, loose matches on Subject: below --
2008-12-02 21:47 Mimi Zohar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.