linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] 3 of 5 IMA: LSM-based measurement code
@ 2005-06-15 14:40 Reiner Sailer
  2005-06-15 20:02 ` James Morris
  2005-06-16  2:01 ` Chris Wright
  0 siblings, 2 replies; 18+ messages in thread
From: Reiner Sailer @ 2005-06-15 14:40 UTC (permalink / raw)
  To: LKML, LSM
  Cc: Chris Wright, Greg KH, Kylene Hall, Tom Lendacky, Emily Rattlif,
	Reiner Sailer

This patch applies against linux-2.6.12-rc6-mm1 and provides the main
Integrity Measurement Architecture code (LSM-based).

Signed-off-by: Reiner Sailer <sailer@watson.ibm.com>
---

diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_fs.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_fs.c
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_fs.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_fs.c	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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 imafs 
+ *		for reporting measurement log and userspace measure requests
+ */
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/security.h>
+#include <linux/major.h>
+#include <linux/seq_file.h>
+#include <linux/percpu.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/file.h>
+#include <linux/parser.h>
+#include <linux/device.h>
+
+#include "ima.h"
+
+struct measure_request {
+	int fd;
+	u16 label;
+};
+
+extern struct h_table htable;
+
+/* based on selinux pseudo filesystem */
+
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user * buf, size_t count,
+				     loff_t * ppos, atomic_t * val)
+{
+	char tmpbuf[TMPBUFLEN];
+	ssize_t len;
+
+	len = scnprintf(tmpbuf, TMPBUFLEN, "%i\n", atomic_read(val));
+	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_clean_inode_hits(struct file *filp,
+						char __user * buf,
+						size_t count,
+						loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.clean_inode_hits);
+}
+static struct file_operations ima_htable_clean_inode_hits_ops = {
+	.read = ima_show_htable_clean_inode_hits
+};
+
+static ssize_t ima_show_htable_clean_table_hits(struct file *filp,
+						char __user * buf,
+						size_t count,
+						loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.clean_table_hits);
+}
+static struct file_operations ima_htable_clean_table_hits_ops = {
+	.read = ima_show_htable_clean_table_hits
+};
+
+static ssize_t ima_show_htable_dirty_table_hits(struct file *filp,
+						char __user * buf,
+						size_t count,
+						loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.dirty_table_hits);
+}
+static struct file_operations ima_htable_dirty_table_hits_ops = {
+	.read = ima_show_htable_dirty_table_hits
+};
+
+static ssize_t ima_show_htable_changed_files(struct file *filp,
+					     char __user * buf,
+					     size_t count, loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.changed_files);
+}
+static struct file_operations ima_htable_changed_files_ops = {
+	.read = ima_show_htable_changed_files
+};
+
+static ssize_t ima_show_htable_user_measure(struct file *filp,
+					    char __user * buf,
+					    size_t count, loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.user_measure);
+}
+static struct file_operations ima_htable_user_measure_ops = {
+	.read = ima_show_htable_user_measure
+};
+
+static ssize_t ima_show_htable_kernel_measure(struct file *filp,
+					      char __user * buf,
+					      size_t count, loff_t * ppos)
+{
+	return ima_show_htable_value(buf, count, ppos,
+				     &htable.kernel_measure);
+}
+static struct file_operations ima_htable_kernel_measure_ops = {
+	.read = ima_show_htable_kernel_measure
+};
+
+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, &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, &htable.len);
+
+}
+static struct file_operations ima_measurements_count_ops = {
+	.read = ima_show_measurements_count
+};
+
+extern int measure_user_file(struct file *, u32 measure_flags);
+extern int ima_enabled;
+
+enum ima_inos {
+	IMA_ROOT_INO = 1,
+	IMA_MEASURE,		/* userspace measurement request */
+	IMA_MEASUREMENTS,	/* measurement log in binary format */
+	IMA_MEASUREMENTS_COUNT,	/* number of measurements in log */
+	IMA_HTABLE_CLEAN_INODE_HITS,
+	IMA_HTABLE_CLEAN_TABLE_HITS,
+	IMA_HTABLE_DIRTY_TABLE_HITS,
+	IMA_HTABLE_CHANGED_FILES,
+	IMA_HTABLE_USER_MEASURE,
+	IMA_HTABLE_KERNEL_MEASURE,
+	IMA_HTABLE_VIOLATIONS,
+};
+
+#define IMA_MAX_EVENT_SIZE 69
+/* print format: 32bit-le=pcr#||char[20]=digest||flags||filename||'\0'  flags bits: 32-16 application flags, 15-3 kernel flags, 2-0 hook len(filename)<=40*/
+static int print_measure_event_entry(struct measure_entry *e, char *buf,
+				     int count)
+{
+	void *ptr = (void *) buf;
+	int filename_len = strlen(e->file_name);
+
+	/* 1st: PCR used is always the same (config option) in little-endian format */
+	*((u32 *) ptr) = (u32) CONFIG_IMA_MEASURE_PCR_IDX;
+	ptr += 4;
+
+	/* 2nd: SHA1 */
+	memcpy(ptr, e->digest, 20);
+	ptr += 20;
+
+	/* 3rd: flags */
+	*((u32 *)ptr) = e->measure_flags;
+	ptr += 4;
+
+	/* 4th:  filename <= 40 + \'0' delimiter */
+	if (filename_len > TCG_EVENT_NAME_LEN_MAX)
+		filename_len = TCG_EVENT_NAME_LEN_MAX;
+
+	memcpy(ptr, e->file_name, filename_len);
+	ptr += filename_len;
+
+	/* 4th: delimiter */
+	*((char *) ptr) = '\0';
+	ptr += 1;
+
+	return ((u32) ptr - (u32) buf);
+}
+
+/* Position pointer is overrided to mean entry # rather than size in bytes */
+static ssize_t ima_measurements_read(struct file *filp, char __user * buf,
+				     size_t count, loff_t * ppos)
+{
+	struct queue_entry *qe;
+	char *tmpbuf;
+	int tmpsiz, i, ret = 0, len;
+	loff_t pos = 0;
+
+	if (count < 0)
+		return -EINVAL;
+
+	tmpsiz = (count < PAGE_SIZE) ? count : PAGE_SIZE;
+	tmpbuf = kmalloc(tmpsiz, GFP_KERNEL);
+	if (!tmpbuf)
+		return -ENOMEM;
+
+
+	down(&h_table_mutex);
+
+	/* fast forward to correct measurement for requested position */
+	for (qe = first_measurement, i = 0; qe && qe->entry && i < *ppos;
+	     qe = qe->later, i++);
+
+
+	/* make sure the next entry fits completely */
+	while ((tmpsiz >= IMA_MAX_EVENT_SIZE) && qe && qe->entry) {
+		/* now fill rest of page */
+		len =
+		    print_measure_event_entry(qe->entry, tmpbuf + ret,
+					      count);
+		qe = qe->later;
+		tmpsiz -= len;
+		ret += len;
+		*ppos += 1;
+	}
+	up(&h_table_mutex);
+	len = simple_read_from_buffer(buf, count, &pos, tmpbuf, ret);
+
+	kfree(tmpbuf);
+	return len;
+}
+
+static ssize_t ima_measure_write(struct file *file,
+				 const char __user * buf, size_t count,
+				 loff_t * ppos)
+{
+	struct measure_request *mr;
+	struct file *meas_file;
+	int error = -EINVAL;
+	char tmpbuf[sizeof(struct measure_request)];
+
+	atomic_inc(&htable.user_measure);
+	if (count != sizeof(struct measure_request)) {
+		ima_error("illegal request size (%d, expected %d).\n",
+			  count, sizeof(struct measure_request));
+		return -EIO;
+	}
+
+	if (copy_from_user(tmpbuf, buf, count)) {
+		ima_error("trouble copying request\n");
+		return -EIO;
+	}
+
+	mr = (struct measure_request *) tmpbuf;
+	if (mr->fd < 0) {
+		ima_error("bad descriptor request\n");
+		return -EBADF;
+	}
+
+	meas_file = fget(mr->fd);
+	if (!meas_file) {
+		ima_error("could not open request\n");
+		return -EACCES;
+	}
+
+	error = measure_user_file(meas_file, (u32)(((mr->label) << 16) | USER_MEASURE_FLAG));
+	fput(meas_file);
+	if (error) {
+		ima_error("problem measuring request\n");
+		return error;
+	} else
+		return count;
+}
+
+static struct file_operations ima_measure_ops = {
+	.write = ima_measure_write,
+};
+
+static struct file_operations ima_measurements_ops = {
+	.read = ima_measurements_read
+};
+
+enum { Opt_uid, Opt_gid };
+
+static match_table_t tokens = {
+	{Opt_uid, "uid=%u"},
+	{Opt_gid, "gid=%u"}
+};
+
+static int ima_remount(struct super_block *sb, int *flags, char *data)
+{
+	char *p;
+	int option;
+	int changed = 0, uid = 0, gid = 0;
+	struct inode *inode;
+
+	if (!data)
+		return 0;
+
+	while ((p = strsep(&data, ",")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+
+		case Opt_uid:
+			if (match_int(args, &option))
+				return -EINVAL;
+			uid = option;
+			changed = 1;
+			break;
+
+		case Opt_gid:
+			if (match_int(args, &option))
+				return -EINVAL;
+			gid = option;
+			changed = 1;
+			break;
+
+		default:
+			ima_error("ima_fs: unrecognized mount option\n");
+			return -EINVAL;
+		}
+	}
+
+	if (changed) {
+		list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+			inode->i_uid = uid;
+			inode->i_gid = gid;
+		}
+	}
+
+	return 0;
+}
+
+/* imafs Filenames and Permissions are set here -- Double CHECK */
+static int ima_fill_super(struct super_block *sb, void *data, int silent)
+{
+	static struct tree_descr ima_files[] = {
+		[IMA_MEASURE] =
+		    {"measurereq", &ima_measure_ops, S_IWUSR | S_IWGRP},
+		[IMA_MEASUREMENTS] =
+		    {"binary_measurements", &ima_measurements_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_MEASUREMENTS_COUNT] =
+		    {"binary_measurements_count",
+		     &ima_measurements_count_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_CLEAN_INODE_HITS] =
+		    {"clean_inode_hits", &ima_htable_clean_inode_hits_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_CLEAN_TABLE_HITS] =
+		    {"clean_hashtable_hits",
+		     &ima_htable_clean_table_hits_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_DIRTY_TABLE_HITS] =
+		    {"dirty_hashtable_hits",
+		     &ima_htable_dirty_table_hits_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_CHANGED_FILES] =
+		    {"changed_files", &ima_htable_changed_files_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_USER_MEASURE] =
+		    {"user_count", &ima_htable_user_measure_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_KERNEL_MEASURE] =
+		    {"kernel_count", &ima_htable_kernel_measure_ops,
+		     S_IRUSR | S_IRGRP},
+		[IMA_HTABLE_VIOLATIONS] =
+		    {"violations", &ima_htable_violations_ops,
+		     S_IRUSR | S_IRGRP},
+
+		/* last one */ {""}
+	};
+
+	return simple_fill_super(sb, IMA_MAGIC, ima_files);
+}
+
+static struct super_block *ima_get_sb(struct file_system_type *fs_type,
+				      int flags, const char *dev_name,
+				      void *data)
+{
+	struct super_block *sb;
+	sb = get_sb_single(fs_type, flags, data, ima_fill_super);
+
+	sb->s_op->remount_fs = ima_remount;
+
+	return sb;
+}
+
+static struct file_system_type ima_fs_type = {
+	.name = "imafs",
+	.get_sb = ima_get_sb,
+	.kill_sb = kill_litter_super,
+};
+
+struct vfsmount *imafs_mount;
+
+void ima_fs_init(void)
+{
+	int err;
+
+	if (!ima_enabled)
+		return;
+
+	err = register_filesystem(&ima_fs_type);
+	if (!err) {
+		imafs_mount = kern_mount(&ima_fs_type);
+		if (IS_ERR(imafs_mount)) {
+			ima_error("imafs:  could not mount!\n");
+			err = PTR_ERR(imafs_mount);
+			imafs_mount = NULL;
+		}
+	}
+	return;
+}
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima.h linux-2.6.12-rc6-mm1-ima/security/ima/ima.h
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima.h	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.	  
+ *
+ * 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
+ *             defs
+ */
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+
+#define ima_printk(level, format, arg...)	\
+	printk(level "ima (%s): " format ,__func__, ## arg)
+       
+#define ima_error(format, arg...)	\
+	ima_printk(KERN_ERR, format, ## arg)
+
+#define ima_info(format, arg...)	\
+	ima_printk(KERN_INFO, format, ## arg)
+
+/* if you cannot tolerate panic for the sake of attestation guarantees,
+ * then redefine IMA_PANIC to, e g., ima_error (see INSTALL documentation) */
+#define IMA_PANIC   \
+	panic
+
+/* set during registering as lsm */
+extern unsigned char ima_terminating;
+void invalidate_pcr(char *);
+
+#define IMA_MEASURE_MODULE_NAME	"IMA"
+#define TCG_EVENT_NAME_LEN_MAX	40
+
+/* file systems we expect to change without
+ * our inode_permission hook being called (nfs, remote fs) */
+#define NFS_SUPER_MAGIC		0x6969
+
+/* file systems we won't measure */
+#define IMA_MAGIC 		0x9999
+
+/* Flags for measurement entries (identifying hook) */
+#define FLAG_HOOK_MASK		0x0f
+#define MMAP_MEASURE_FLAG 	0x01
+#define MODULE_MEASURE_FLAG 	0x02
+#define USER_MEASURE_FLAG 	0x04
+
+#define MEASURE_HTABLE_SIZE	512
+#define HASH_KEY(inode_number) ((inode_number) % MEASURE_HTABLE_SIZE)
+#define SHA_KEY(sha_value) (((sha_value)[18] << 8 | (sha_value)[19]) % MEASURE_HTABLE_SIZE)
+typedef enum { CLEAN, DIRTY, CHANGED } ima_entry_flags;
+
+/* security structure appended to inodes */
+struct ima_inode {
+	atomic_t measure_count;	/* # processes currently using this file in measure-mode */
+	ima_entry_flags dirty;
+};
+
+/* security structure appended to measured files*/
+struct ima_file {
+	char is_measuring;	/* identify fds that are "measuring" */
+};
+
+/* get/store security state information;
+ * if stacking were to be implemented, this would be the place */
+#define ima_get_inode_security(inode) \
+	((struct ima_inode *) ((inode)->i_security))
+
+#define ima_store_inode_security(inode,sec_struct) \
+	((inode)->i_security = (sec_struct))
+
+#define ima_get_file_security(file) \
+	((struct ima_file *) ((file)->f_security))
+
+#define ima_store_file_security(file, sec_struct) \
+	((file)->f_security = (sec_struct))
+
+struct measure_entry {
+	u32 measure_flags;
+	unsigned long inode_nr;
+	dev_t dev_id;
+	ima_entry_flags dirty;
+	u8 digest[20];				  /* sha1 measurement hash */
+	char file_name[TCG_EVENT_NAME_LEN_MAX+1]; /* name + \0 */
+	struct super_block *super_block;	/* super block link (for umount-dirty flagging) */
+};
+
+struct sha_entry {
+	struct sha_entry *next;
+	u8 *digest;
+	struct measure_entry *m_entry;
+};
+
+struct queue_entry {
+	struct queue_entry *next;
+	struct queue_entry *later;
+	struct measure_entry *entry;
+};
+
+extern struct queue_entry *first_measurement;	/* for printing */
+extern struct queue_entry *latest_measurement;	/* for adding */
+
+/* hash table to keep fast access to past measurements 
+ * uses one global lock for now (read/write) */
+extern struct semaphore h_table_mutex;
+
+struct h_table {
+	atomic_t len;
+	atomic_t user_measure; /* # measurements requested from userspace */
+	atomic_t kernel_measure; /* # measurements performed from kernel */
+	atomic_t clean_inode_hits; /* times we find an inode clean when measuring */
+	atomic_t clean_table_hits; /* times we find a clean htable hit */
+	atomic_t dirty_table_hits; /* times we find a dirty htable hit */
+	atomic_t changed_files;	 /* times we realize a dirty marked entry really changed */
+	atomic_t violations;
+	unsigned int max_htable_size;
+	struct queue_entry *queue[MEASURE_HTABLE_SIZE];
+	atomic_t queue_len[MEASURE_HTABLE_SIZE];
+};
+
+struct sha_table {
+	atomic_t len;
+	unsigned int max_htable_size;
+	struct sha_entry *queue[MEASURE_HTABLE_SIZE];
+	atomic_t queue_len[MEASURE_HTABLE_SIZE];
+};
+
+#define MEM_MINOR	1
+#define KMEM_MINOR	2
+#ifdef CONFIG_IMA_KMEM_BYPASS_PROTECTION
+static inline void check_kmem_bypass(struct inode *inode)
+{
+	if ((imajor(inode) == MEM_MAJOR) 
+	    && S_ISCHR(inode->i_mode) && (iminor(inode) == KMEM_MINOR))
+		invalidate_pcr("/dev/kmem write violation");
+}
+#else
+static inline void check_kmem_bypass(struct inode *inode)
+{
+	return;
+}
+#endif
+
+#ifdef CONFIG_IMA_MEM_BYPASS_PROTECTION
+static inline void check_mem_bypass(struct inode *inode)
+{
+	if ((imajor(inode) == MEM_MAJOR) 
+	    && S_ISCHR(inode->i_mode) && (iminor(inode) == MEM_MINOR))
+		invalidate_pcr("/dev/mmem write violation");
+}
+#else
+static inline void check_mem_bypass(struct inode *inode)
+{
+	return;
+}
+#endif
+
+#ifdef CONFIG_IMA_RAM_BYPASS_PROTECTION
+static inline void check_ram_bypass(struct inode *inode)
+{
+	if ((imajor(inode) == RAMDISK_MAJOR) && S_ISBLK(inode->i_mode))
+		invalidate_pcr("/dev/ram write violation");
+}
+#else
+static inline void check_ram_bypass(struct inode *inode)
+{
+	return;
+}
+#endif
+
+#ifdef CONFIG_IMA_HD_SD_BYPASS_PROTECTION
+static inline void check_hd_sd_bypass(struct inode *inode)
+{
+	if ((imajor(inode) == HD_MAJOR) && S_ISBLK(inode->i_mode))
+		invalidate_pcr("/dev/hdx write violation");
+	else if ((imajor(inode) == SCSI_DISK0_MAJOR) && S_ISBLK(inode->i_mode))
+		invalidate_pcr("/dev/sdx write violation");
+}
+#else
+static inline void check_hd_sd_bypass(struct inode *inode)
+{
+	return;
+}
+#endif
+
+/* configuration options*/
+extern int ima_test_mode;
+extern int skip_boot_aggregate;
+
+static inline void read_configs(void)
+{
+#ifdef CONFIG_IMA_TEST_MODE
+	ima_test_mode = 1;
+#else
+	ima_test_mode = 0;
+#endif
+
+#ifdef CONFIG_IMA_SKIP_BOOT_AGGREGATE
+	skip_boot_aggregate = 1;
+#else
+	skip_boot_aggregate = 0;
+#endif
+}
+
+#ifdef CONFIG_TCG_TPM
+struct tpm_chip;
+
+extern ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz);
+
+extern struct tpm_chip *tpm_chip_lookup(int chip_num);
+#else
+struct tpm_chip {
+	char dummy;
+};
+
+static inline ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz)
+{
+	return 0;
+}
+
+static inline struct tpm_chip *tpm_chip_lookup(int chip_num)
+{
+	return NULL;
+}
+#endif
+
+
+#endif
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer      <sailer@watson.ibm.com>
+ *
+ * Contributions:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.	  
+ *
+ * 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
+ *             init functions to start up IBM IMA as LSM
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include "ima.h"
+#include "ima_tpm_pcrread.h"
+#include "ima_tpm_extend.h"
+
+/* name for boot aggregate entry */
+char *boot_aggregate_name = "boot_aggregate";
+
+extern struct h_table htable;
+
+/* These identify the driver base version and may not be removed. */
+static const char version[] = "v4.0 06/15/2005";
+static const char illegal_pcr[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/* configuration parameters */
+int ima_test_mode;
+int skip_boot_aggregate;
+
+void create_htable(void);
+void create_sha_htable(void);
+void ima_lsm_init(void);
+void ima_fs_init(void);
+int ima_add_measure_entry(struct measure_entry *);
+
+int ima_enabled = 0;
+struct tpm_chip *ima_used_chip;
+
+static int __init ima_enabled_setup(char *str)
+{
+	ima_enabled = simple_strtol(str, NULL, 0);
+	return 1;
+}
+
+__setup("ima=", ima_enabled_setup);
+
+
+void ima_add_boot_aggregate(void)
+{
+	/* cumulative sha1 the first 8 tpm registers */
+	struct measure_entry *entry;
+	size_t count;
+
+	/* create new entry for boot aggregate */
+	entry = (struct measure_entry *)
+	    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
+	if (entry == NULL) {
+		invalidate_pcr("error allocating new measurement entry");
+		return;
+	}
+	entry->inode_nr = 0;	/* 0,0 are special (no files) */
+	entry->dev_id = 0;
+	entry->measure_flags = 0;
+	entry->dirty = DIRTY;
+	entry->super_block = NULL;
+	memset(entry->digest, 0, 20);
+	if ((count = strlen(boot_aggregate_name)) > TCG_EVENT_NAME_LEN_MAX)
+		count = TCG_EVENT_NAME_LEN_MAX;
+	memcpy(entry->file_name, boot_aggregate_name, count);
+	entry->file_name[count] = '\0';	/* ez-print */
+	if (ima_used_chip != NULL) {
+		int i;
+		u8 pcr_i[20];
+		struct crypto_tfm *tfm;
+		
+		tfm = crypto_alloc_tfm("sha1", 0);
+		if (tfm == NULL) {
+			ima_error("Digest init failed ERROR.\n");
+			return;
+		}
+		crypto_digest_init(tfm);
+
+		for (i = 0; i < 8; i++) {
+			tpm_pcrread(i, pcr_i);
+			/* now accumulate with current aggregate */
+			tfm->__crt_alg->cra_digest.
+			    dia_update(crypto_tfm_ctx(tfm), pcr_i, 20);
+		}
+		crypto_digest_final(tfm, entry->digest);
+		crypto_free_tfm(tfm);
+	} else
+		memset(entry->digest, 0xff, 20);
+
+	/* now add measurement; if TPM bypassed, we have a ff..ff entry */
+	if (ima_add_measure_entry(entry) < 0) {
+		kfree(entry);
+		invalidate_pcr("error adding boot aggregate");
+	} else
+		tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest);
+}
+
+
+/* general invalidation function called by the measurement code */
+void invalidate_pcr(char *cause)
+{
+	/* extend pcr with illegal digest (no digest yields 0) */
+	/* extending twice is obviously flagging the exception condition... */
+	ima_error("INVALIDATING PCR AGGREGATE. Cause=%s.\n", cause);
+	tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr);
+	tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr);
+	atomic_inc(&htable.violations); /* can overflow into 0; this is an indicator only */
+}
+
+
+static int __init measure_init(void)
+{
+	struct security_operations null_ops;
+
+	printk(KERN_INFO "IBM Integrity Measurement Architecture (IBM IMA %s).\n", 
+	       version);
+	read_configs();
+
+	/* check pre-conditions and dependencies */
+	if (!ima_test_mode)
+		ima_enabled = 1;	/* unconditionally */
+	else {
+		if (!ima_enabled) {
+			printk(KERN_INFO "    IMA (not enabled in kernel command line) aborting!\n");
+			return 0;
+		}
+		printk(KERN_INFO "    IMA (test mode)\n");
+	}
+	ima_used_chip = tpm_chip_lookup(0);
+	if (ima_used_chip == NULL) {
+		if (ima_test_mode)
+			printk(KERN_INFO "    IMA (TPM/BYPASS - no TPM chip found)\n");
+		else
+			/* no way to invalidate pcr and inform remote party */
+			IMA_PANIC("IMA: TPM/no support and IMA not in test mode!\n");
+	}
+	/* check for LSM availability */
+	memset(&null_ops, 0, sizeof(struct security_operations));
+	if (!register_security(&null_ops))
+		unregister_security(&null_ops);
+	else {
+		if (ima_test_mode) {
+			ima_enabled = 0;
+			printk(KERN_INFO "    IMA (LSM/not free) aborting!\n");
+			return -EFAULT;
+		} else
+			invalidate_pcr("LSM/not free in real mode!\n");
+	}
+	create_htable(); /* for measurements */
+	create_sha_htable();
+	/* boot aggregate must be very first entry */
+	if (!skip_boot_aggregate)
+		ima_add_boot_aggregate();
+	ima_lsm_init();
+	ima_fs_init();
+	return 0;
+}
+
+__initcall(measure_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Reiner Sailer <sailer@watson.ibm.com>");
+MODULE_DESCRIPTION
+    ("Run-time LSM-based IBM Integrity Measurement Architecture");
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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_lsmhooks.c
+ *             implements Linux Security Modules hooks that call into
+ *             into the measurement functions
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/mman.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include "ima.h"
+
+extern struct h_table htable;
+
+/* if set, then hooks do nothing 
+ * (controls non-lsm module hook as well) */
+unsigned char ima_terminating = 1;
+
+struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t);
+void measure_mmap_file(struct file *, u32 flags);
+int measure_dirty_flag_super(struct super_block *);
+
+/* measure files mmapped with exec permission */
+int ima_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags)
+{
+	/* filter interesting calls that actually map files executable */
+	if (!(reqprot & PROT_EXEC))
+		return 0;
+
+	/* now check protection  */
+	if (reqprot & MAP_SHARED & PROT_EXEC & PROT_WRITE) {
+		ima_error("MMAP protection flag error!!!\n");
+		invalidate_pcr("MMAP protection flag violation!");
+	}
+	atomic_inc(&htable.kernel_measure);
+	measure_mmap_file(file, (u32)MMAP_MEASURE_FLAG);
+	/* IMA is non-intrusive, so we always map */
+	return 0;
+}
+
+/* dirty flags on open with MAY_WRITE|MAY_APPEND */
+int ima_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+	struct ima_inode *i_security = NULL;
+	struct measure_entry *entry;
+
+	/* dirty-flagging applies to changing files */
+	if (!(mask & (MAY_WRITE | MAY_APPEND)) || !inode)
+		return 0;
+	
+	/* general checks against bypassing dirty-flagging */
+	check_kmem_bypass(inode);
+	check_mem_bypass(inode);
+	check_ram_bypass(inode);
+	check_hd_sd_bypass(inode);
+	
+	/* files that are written to are usually not executed (measured),
+	   optimize this path */
+	down(&h_table_mutex);
+	if ((entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev)) == NULL)
+		goto out; /* not a measured file */
+	if (entry->dirty == CLEAN)
+		entry->dirty = DIRTY;
+	/* dirty flag inode */
+	if ((i_security = ima_get_inode_security(inode)) != NULL) {
+		if (atomic_read(&(i_security->measure_count))) {
+			/* write permission on measured file was granted! */
+			invalidate_pcr("ToMToU violation");
+			ima_error("VIOLATION: Writing to measured file (%s) while it is being used!\n", 
+				  entry->file_name);	
+		}
+		if (i_security->dirty == CLEAN)
+			i_security->dirty = DIRTY;
+	}
+ out:
+	up(&h_table_mutex);
+	return 0;
+}
+
+/* dirty flag files on an umounted file system */
+static int ima_sb_umount(struct vfsmount *mnt, int flags)
+{
+	/* mark all clean entries with this superblock dirty */
+	struct queue_entry *qe;
+	struct super_block *super = mnt->mnt_sb;
+	int j;
+
+	down(&h_table_mutex);
+	for (j = 0; j < htable.max_htable_size; j++) {
+		qe = htable.queue[j];
+		while (qe != NULL) {
+			if (qe->entry->super_block == super)
+				if (qe->entry->dirty == CLEAN)
+					qe->entry->dirty = DIRTY;
+			qe = qe->next;
+		}
+	}
+	up(&h_table_mutex);
+	return 0;
+}
+
+/* free security structure if applies */
+static void ima_inode_free_security(struct inode *inode)
+{
+	struct ima_inode *i_security = ima_get_inode_security(inode);
+
+	if (i_security) {
+		kfree(i_security);
+		ima_store_inode_security(inode, NULL);
+	}
+}
+
+static void ima_file_free_security(struct file *file)
+{
+	struct ima_file *f_security;
+	struct ima_inode *i_security = NULL;
+
+	if ((f_security = ima_get_file_security(file)) == NULL)
+		return;
+	/* decrease measure count if file is measured */
+	i_security = ima_get_inode_security(file->f_dentry->d_inode);
+	if (i_security && (f_security->is_measuring))
+			atomic_dec(&(i_security->measure_count));
+	kfree(f_security);
+	ima_store_file_security(file, NULL);
+}
+
+/* module stacking operations */
+int ima_register_security(const char *name, struct security_operations *ops)
+{
+	/* no stacking */
+	return -EFAULT;
+}
+
+int ima_unregister_security(const char *name, struct security_operations *ops)
+{
+	/* no stacking */
+	return -EFAULT;
+}
+
+struct security_operations ima_ops;
+
+/* IMA requires early initialization in order measure
+   all executables etc from the very beginning. */
+void ima_lsm_init(void)
+{
+	/* prepare ima_ops struct */
+	memset(&ima_ops, 0, sizeof(struct security_operations));
+	/* set the few non-null elements */
+	ima_ops.file_mmap = ima_file_mmap;
+	ima_ops.file_free_security = ima_file_free_security;
+	ima_ops.inode_permission = ima_inode_permission;
+	ima_ops.inode_free_security = ima_inode_free_security;
+	ima_ops.sb_umount = ima_sb_umount;
+	ima_ops.register_security = ima_register_security;
+	ima_ops.unregister_security = ima_unregister_security;
+	/* rest will be taken care of by registration (fixup) */
+	if (register_security(&ima_ops)) {
+		invalidate_pcr("IMA: Unable to register with kernel.\n");
+		return;
+	}
+	/* module measurement hook becomes hot */
+	ima_terminating = 0;
+}
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c	2005-06-14 21:58:13.000000000 -0400
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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 run-time measurements
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include "ima.h"
+#include "ima_tpm_extend.h"
+
+extern struct h_table htable;
+extern struct sha_table sha_htable;
+
+struct sha_entry *ima_lookup_sha_entry(u8 * sha_value);
+struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t);
+int ima_add_measure_entry(struct measure_entry *);
+int measure_dirty_flag_inode(struct inode *);
+extern struct tpm_chip *ima_used_chip;
+
+DEFINE_SPINLOCK(ima_measure_file_lock);
+
+/* 
+ * Returns the dirty flag setting for an inode
+ * (nfs, since we don't control changes)
+ */
+static inline ima_entry_flags get_default_dirty_setting(struct inode *inode)
+{
+	switch (inode->i_sb->s_magic) {
+	case NFS_SUPER_MAGIC:
+		return DIRTY;	/* dirty */
+		break;
+	default:		/* local fs etc. */
+		return CLEAN;	/* clean */
+	}
+}
+
+/* returns >0 if measurement must be skipped
+ * returns =0 if measurement allowed 
+ */
+static inline int skip_measurement(struct inode *inode)
+{
+	/* measuring only regular files; can't measure IMA files */
+	if (S_ISREG(inode->i_mode) && (inode->i_sb->s_magic != IMA_MAGIC))
+		return 0;       	/* measure */
+	else
+		return 1;       	/* skip */	
+}
+
+
+/*  measures new file and adds it to measurement list */
+static struct measure_entry *do_measure_file(struct file *file, struct inode *inode)
+{
+	struct ima_inode *i_security = NULL;
+	mm_segment_t oldfs;
+	int error = 0;
+	loff_t offset = 0;
+	size_t count;
+	struct crypto_tfm *tfm;
+	struct measure_entry *entry;
+
+	char *bufp = NULL;
+	/* create read buffer */
+	if ((bufp =
+	     (char *) kmalloc(PAGE_SIZE, GFP_KERNEL)) == 0) {
+		ima_error("no memory for read buffer\n");
+		error = -ENOMEM;
+		goto out;	/* invalidate pcr */
+	}
+	/* create new entry and measure */
+	entry = (struct measure_entry *)
+	    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
+	if (entry == NULL) {
+		error = -ENOMEM;
+		ima_error("error allocating new measurement entry");
+		kfree(bufp);
+		goto out;	/* invalidate pcr */
+	}
+	entry->inode_nr = inode->i_ino;
+	entry->dev_id = inode->i_rdev;
+	entry->dirty = get_default_dirty_setting(inode);
+	entry->super_block = inode->i_sb;
+	if ((count = file->f_dentry->d_name.len) > TCG_EVENT_NAME_LEN_MAX)
+		count = TCG_EVENT_NAME_LEN_MAX;
+	memcpy(entry->file_name, file->f_dentry->d_name.name, count);
+	entry->file_name[count] = '\0';	/* ez-print */
+	error = 0;
+	/* second add sha1 over file contents */
+	/* init context */
+	tfm = crypto_alloc_tfm("sha1", 0);
+	if (tfm == NULL) {
+		ima_error("Digest init failed ERROR.\n");
+		goto outm;
+	}
+	crypto_digest_init(tfm);
+
+	/* set fs so that kernel writes into kernel segment */
+	oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	do {
+		if ((count =
+		     (file->f_op->read) (file,
+					 (char __user *) bufp,
+					 PAGE_SIZE,
+					 &offset)) < 0) {
+			error = count;
+			ima_error("Error reading from file (%d)\n", error);
+			goto outf;
+		}
+		/* update hash with this part */
+		tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm),
+						      bufp, count);
+	} while (count);
+	set_fs(oldfs);
+
+	/* complete hash */
+	crypto_digest_final(tfm, entry->digest);
+	crypto_free_tfm(tfm);
+	/* before returning, replicate important information into inode->i_security */
+	i_security = ima_get_inode_security(inode);
+	if (i_security != NULL) {
+		/* update */
+		i_security->dirty = entry->dirty;
+	} else {
+		ima_error("error No security structure in measure!\n");
+		goto outm;
+	}
+
+	kfree(bufp);
+	return (entry);
+
+	/* error exits */
+      outf:
+	set_fs(oldfs);
+      outm:
+	kfree(entry);
+	kfree(bufp);
+      out:
+	return (NULL);
+}
+
+/* measure memory (kernel module; still the exact copy of the object file) */
+int do_measure_memory(void *start, unsigned long len, u32 measure_flags, char *name)
+{
+	struct crypto_tfm *tfm;
+	u8 mem_digest[20];
+	int error = 0;
+	struct measure_entry *entry;
+	int count;
+
+	/* init context */
+	tfm = crypto_alloc_tfm("sha1", 0);
+	if (tfm == NULL) {
+		invalidate_pcr("No SHA1 available");
+		return -EFAULT;
+	}
+	crypto_digest_init(tfm);
+	/* now measure the memory ... */
+	tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm), start,
+					      len);
+	crypto_digest_final(tfm, mem_digest);
+	crypto_free_tfm(tfm);
+
+	down(&h_table_mutex);
+	if (!ima_lookup_sha_entry(mem_digest)) {
+		/* create new entry and measure */
+		entry = (struct measure_entry *)
+		    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
+		if (entry == NULL) {
+			invalidate_pcr("OUT OF MEMORY");
+			error = -EFAULT;
+			goto out;
+		}
+		entry->inode_nr = 0;	/* special entries, no file entries */
+		entry->dev_id = 0;
+		entry->dirty = DIRTY;
+		entry->super_block = NULL;
+		memcpy(entry->digest, mem_digest, 20);
+		if ((count = strlen(name)) > TCG_EVENT_NAME_LEN_MAX)
+			count = TCG_EVENT_NAME_LEN_MAX;
+		strncpy(entry->file_name, name, count);
+		entry->file_name[count] = '\0';
+		entry->measure_flags = measure_flags;
+		if ((error = ima_add_measure_entry(entry)) < 0) {
+			kfree(entry);
+			invalidate_pcr
+			    ("error adding new measurement entry");;
+			goto out;
+		} else {	/* extend PCR */
+			tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest);
+		}
+	}			/* else we already have this hash value from an exec/file that was running earlier */
+	up(&h_table_mutex);
+	return 0;
+      out:
+	up(&h_table_mutex);
+	return -EFAULT;
+}
+
+static unsigned int find_mod_sec(Elf_Ehdr * hdr, Elf_Shdr * sechdrs, const char *secstrings, const char *name)
+{
+	unsigned int i;
+	for (i = 1; i < hdr->e_shnum; i++)
+		/* Alloc bit cleared means "here is nothing to look for (ignore)" */
+		if ((sechdrs[i].sh_flags & SHF_ALLOC)
+		    && strcmp(secstrings + sechdrs[i].sh_name, name) == 0)
+			return i;
+	return 0;
+}
+
+/* Measure kernel modules in-memory before relocation */
+void measure_kernel_module(void *start, unsigned long len, const char __user * uargs)
+{
+	Elf_Ehdr *hdr;
+	Elf_Shdr *sechdrs;
+	struct module *mod;
+	unsigned int modindex;
+	char *args, *secstrings;
+	long arglen;
+
+	arglen = strlen_user(uargs);
+	if (!arglen) {
+		invalidate_pcr("ERROR measuring kernel module!");
+		return;
+	}
+	args = kmalloc(arglen, GFP_KERNEL);
+	if (!args) {
+		invalidate_pcr("OUT OF MEMORY measuring kernel module!");
+		return;
+	}
+	if (copy_from_user(args, uargs, arglen) != 0) {
+		invalidate_pcr("ERROR measuring kernel module!");
+		return;
+	}
+	/* get the module name for entry */
+	hdr = (Elf_Ehdr *) start;
+	sechdrs = (void *) hdr + hdr->e_shoff;
+	secstrings = (void *) hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+	modindex = find_mod_sec(hdr, sechdrs, secstrings,
+				".gnu.linkonce.this_module");
+	if (!modindex) {
+		ima_error("No module found in object\n");
+		invalidate_pcr("Module without name?!");
+		return;
+	}
+	mod = (void *) ((size_t) hdr + sechdrs[modindex].sh_offset);
+	atomic_inc(&htable.kernel_measure); /* CHECK */
+	do_measure_memory(start, len, (u32)MODULE_MEASURE_FLAG, mod->name);
+	return;
+}
+
+
+
+static void measure_file (struct file *file, u32 measure_flags, struct inode *inode, struct ima_inode *i_security)
+{
+	struct measure_entry *entry, *new_entry;
+
+	down(&h_table_mutex);
+	entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev);
+	if ((entry != NULL) && (entry->dirty == CLEAN)) {
+		i_security->dirty = CLEAN;
+		atomic_inc(&htable.clean_table_hits);
+		goto out; /* done */
+	}
+	new_entry = do_measure_file(file, inode);
+	/* now we adjust the entry table:
+	 * -- if there was no entry, we just add the new one
+	 * -- if there was one but different hash, we add the new one
+	 * -- if there was one and same hash, we clear dirty bit on existing one
+	 */
+	if (!new_entry) {
+		/* internal error, make sure attestation fails from now on */
+		invalidate_pcr("error measuring file");
+		goto out;
+	}
+	new_entry->measure_flags = measure_flags;
+	if (entry == NULL) {	/* no old entry for this inode found */
+		/* add if this hash is new (i.e., no copy measured yet) */
+		if (!ima_lookup_sha_entry(new_entry->digest)) {
+			if (ima_add_measure_entry(new_entry) < 0) {
+				kfree(new_entry);
+				invalidate_pcr("error adding measurement entry");
+			} else	
+				tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest);
+		}
+		goto out;
+	}
+	/* old entry exists (!= clean) */
+	if (!memcmp(entry->digest, new_entry->digest, 20)) {
+		/* set with default (no clean-flag for nfs) */
+		entry->dirty = get_default_dirty_setting(inode);
+		i_security->dirty = entry->dirty;
+		atomic_inc(&htable.dirty_table_hits);
+		kfree(new_entry);
+	} else {
+		/* dirty and look whether to add new entry */
+		entry->dirty = CHANGED;
+		atomic_inc(&htable.changed_files);
+		if (!ima_lookup_sha_entry(new_entry->digest)) {
+			if (ima_add_measure_entry(new_entry) < 0) {
+				kfree(new_entry);
+				invalidate_pcr("error adding measurement entry");
+			} else
+				tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest);
+		}
+	}
+ out:
+	up(&h_table_mutex);
+}
+
+
+/* Measure user space file descriptor, protect file from being
+ * written until all measureing processes have closed the file
+ */
+int measure_user_file(struct file *file, u32 measure_flags)
+{
+	struct inode *inode;
+	struct ima_file *f_security = NULL;
+	struct ima_inode *i_security = NULL;
+
+	if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode)
+		return -EACCES;
+
+	inode = file->f_dentry->d_inode;
+
+	/* here we skip unnecessary measurements */
+	if (skip_measurement(inode))
+		return -EACCES; /* not allowed to measure; user apps to handle error */
+ 
+	/* a) if there is already a writer on this file --> error! */
+	if (atomic_read(&(inode->i_writecount)) > 0) {
+		struct measure_entry *entry;
+		invalidate_pcr("ToMToU violation");
+		down(&h_table_mutex);
+		entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev);
+		ima_error("VIOLATION: Measured file (%s) has writers!\n",
+			  (entry != NULL) ? entry->file_name : "most likely measuring file opened rw");
+		up(&h_table_mutex);
+		return -EACCES;
+	}
+	inode = file->f_dentry->d_inode;
+	/* mark this file as measuring (increases measurement-refcount on inode) */
+	if ((f_security = ima_get_file_security(file)) != NULL) {
+		i_security = ima_get_inode_security(file->f_dentry->d_inode);
+		if (i_security == NULL) {
+			invalidate_pcr("Internal error (f_security not free but no i_security).\n");
+			return -EFAULT;
+		}
+	} else {
+		/* create f_security and if necessary i_security */
+		f_security = kmalloc(sizeof(struct ima_file), GFP_KERNEL);
+		if (f_security == NULL) {
+			invalidate_pcr("out of memory error");
+			return -ENOMEM;
+		} else {
+			f_security->is_measuring = 1;
+			ima_store_file_security(file, f_security);
+		}
+		/* we maintain an inode copy of clean etc. to speed up clean hits */
+		i_security = ima_get_inode_security(inode);
+		if (i_security != NULL)
+			atomic_inc(&(i_security->measure_count));
+		else {
+			spin_lock(&ima_measure_file_lock);
+			if ((i_security = ima_get_inode_security(inode)))
+				goto dontalloc;
+			i_security =
+				kmalloc(sizeof(struct ima_inode), GFP_KERNEL);
+			if (i_security == NULL) {
+				spin_unlock(&ima_measure_file_lock);
+				invalidate_pcr("out of memory error");
+				return -EFAULT;
+			} else {
+				i_security->dirty = DIRTY;
+				/* is reset later after measuring file */
+				atomic_set(&(i_security->measure_count), 1);
+				ima_store_inode_security(inode, i_security);
+			}
+dontalloc:
+			spin_unlock(&ima_measure_file_lock);	
+		}
+	}
+	/* catch most cases */
+	if (i_security->dirty == CLEAN)
+		atomic_inc(&htable.clean_inode_hits);
+	else
+		measure_file(file, measure_flags, inode, i_security);
+	return 0;
+}
+
+
+/* Measure files mapped as executable */
+void measure_mmap_file(struct file *file, u32 measure_flags)
+{
+	struct inode *inode;
+	struct ima_inode *i_security = NULL;
+
+	if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode)
+		return;
+	
+	inode = file->f_dentry->d_inode;
+
+	/* here we skip non-allowed measurements */
+	if (skip_measurement(inode))
+		return;
+
+	/* if there is already a writer on this file --> error! */
+	if (atomic_read(&(inode->i_writecount)) > 0) {
+		invalidate_pcr("Measured file has writers.");
+		return;
+	}
+	/* we maintain an inode copy of clean etc. to speed up clean hits */
+	i_security = ima_get_inode_security(inode);
+	if (!i_security) {
+		spin_lock(&ima_measure_file_lock);
+		if ((i_security = ima_get_inode_security(inode)))
+			goto dontalloc;
+		i_security = kmalloc(sizeof(struct ima_inode), GFP_KERNEL);
+		if (i_security == NULL) {
+			spin_unlock(&ima_measure_file_lock);
+			invalidate_pcr("out of memory error");
+			return;
+		} else {
+			i_security->dirty = DIRTY;
+			/* is reset later after measuring file */
+			atomic_set(&(i_security->measure_count), 0);
+			ima_store_inode_security(inode, i_security);
+		}
+dontalloc:
+		spin_unlock(&ima_measure_file_lock);
+	}
+	/* catch most cases */
+	if (i_security->dirty == CLEAN)
+		atomic_inc(&htable.clean_inode_hits);
+	else
+		measure_file(file, measure_flags, inode, i_security);
+}
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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 for run-time measurement
+ *             functions based on SHA1
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/linkage.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/crypto.h>
+
+#include "ima.h"
+
+/* pointer to very first and latest measurement (time-ordered) */
+struct queue_entry *first_measurement = NULL;	/* for printing */
+struct queue_entry *latest_measurement = NULL;	/* for adding */
+
+struct h_table htable;		/* key: inode (before secure-hashing a file) */
+struct sha_table sha_htable;	/* key: hash (after secure-hashing a file) */
+int ima_add_sha_entry(struct measure_entry *);
+
+DECLARE_MUTEX_LOCKED(h_table_mutex);
+
+void create_sha_htable(void)
+{
+	int i;
+
+	atomic_set(&sha_htable.len, 0);
+	sha_htable.max_htable_size = MEASURE_HTABLE_SIZE;
+	for (i = 0; i < sha_htable.max_htable_size; i++) {
+		sha_htable.queue[i] = NULL;
+		atomic_set(&sha_htable.queue_len[i], 0);
+	}
+}
+
+void create_htable(void)
+{
+	int i;
+
+	init_MUTEX_LOCKED(&h_table_mutex);
+	first_measurement = NULL;
+	latest_measurement = NULL;
+	atomic_set(&htable.len, 0);
+	atomic_set(&htable.user_measure, 0);
+	atomic_set(&htable.kernel_measure, 0);
+	atomic_set(&htable.clean_inode_hits, 0);
+	atomic_set(&htable.clean_table_hits, 0);
+	atomic_set(&htable.dirty_table_hits, 0);
+	atomic_set(&htable.changed_files, 0);
+	atomic_set(&htable.violations, 0);
+	htable.max_htable_size = MEASURE_HTABLE_SIZE;
+	for (i = 0; i < htable.max_htable_size; i++) {
+		htable.queue[i] = NULL;
+		atomic_set(&htable.queue_len[i], 0);
+	}
+	up(&h_table_mutex);
+}
+
+/* 
+ * also sets clean and dirty table hit marks 
+ */
+struct measure_entry *ima_lookup_measure_entry(unsigned long inode_number, dev_t dev_number)
+{
+	struct queue_entry *qe;
+	struct measure_entry *me;
+
+	/* fill in later */
+	qe = htable.queue[HASH_KEY(inode_number)];
+	while ((qe != NULL) && ((qe->entry->inode_nr != inode_number)
+				|| (qe->entry->dev_id != dev_number)))
+		qe = qe->next;
+
+	if (qe != NULL) {
+		if (qe->entry->dirty != CLEAN)
+			atomic_inc(&htable.dirty_table_hits);
+		else
+			atomic_inc(&htable.clean_table_hits);
+
+		me = qe->entry;
+	} else {
+		me = NULL;
+	}
+	return me;
+}
+
+
+
+struct sha_entry *ima_lookup_sha_entry(u8 * sha_value)
+{
+	struct sha_entry *se;
+	unsigned int key;
+
+	key = SHA_KEY(sha_value);
+	se = sha_htable.queue[key];
+	while ((se != NULL) && (memcmp(se->digest, sha_value, 20)))
+		se = se->next;
+	return se;
+}
+
+
+int ima_add_measure_entry(struct measure_entry *entry)
+{
+	unsigned int key;
+	struct queue_entry *qe;
+	int error = 0;
+
+	/* calculate key */
+	key = HASH_KEY(entry->inode_nr);
+
+	/* create queue_entry */
+	if ((qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL)) == NULL) {
+		ima_error("OUT OF MEMORY in %s.\n", __func__);
+		error = -ENOMEM;
+		goto out;
+	}
+	qe->entry = entry;
+
+	/* insert entry at beginning of queue */
+	qe->next = htable.queue[key];
+	qe->later = NULL;
+	htable.queue[key] = qe;
+	atomic_inc(&htable.queue_len[key]);
+	/* update later list */
+	if (first_measurement == NULL)
+		first_measurement = qe;
+	else
+		latest_measurement->later = qe;
+	
+	latest_measurement = qe;
+	atomic_inc(&htable.len);
+	/* now add to sha hash table, too */
+	if (ima_add_sha_entry(entry))
+		error = -ENOMEM;
+      out:
+	return error;
+}
+
+
+
+int ima_add_sha_entry(struct measure_entry *entry)
+{
+	unsigned int key;
+	struct sha_entry *se;
+
+	/* calculate key */
+	key = SHA_KEY(entry->digest);
+	/* create queue_entry */
+	if ((se = kmalloc(sizeof(struct sha_entry), GFP_KERNEL)) == NULL)
+		goto out;
+	se->m_entry = entry;
+	se->digest = entry->digest;
+	se->next = NULL;
+
+	/* insert entry at beginning of queue */
+	se->next = sha_htable.queue[key];
+	sha_htable.queue[key] = se;
+	atomic_inc(&sha_htable.queue_len[key]);
+	/* update later list */
+	atomic_inc(&sha_htable.len);
+	return 0;
+
+      out:
+	ima_error("OUT OF MEMORY ERROR creating queue entry.\n");
+	return -ENOMEM;
+}
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h	2005-06-14 20:32:39.000000000 -0400
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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_tpm_extend.h
+ *             implements glue code to connect IMA to the TPM driver
+ *             (glues to tpmdd on www.sourceforge.net/tpmdd)
+ */
+#ifndef __LINUX_IMA_TPM_EXTEND_H
+#define __LINUX_IMA_TPM_EXTEND_H
+
+#define TPM_BUFSIZE 2048
+
+extern struct tpm_chip *ima_used_chip;
+
+static const u8 extend[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 34,		/* length */
+	0, 0, 0, 20,		/* TPM_ORD_Extend */
+	0, 0, 0, 0		/* PCR index */
+};
+
+static void tpm_extend(int index, const u8 * digest)
+{
+	u8 *data;
+	u32 i;
+	int len;
+
+	if (ima_used_chip == NULL)
+		return;
+
+	if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL)
+		goto error;
+
+	memcpy(data, extend, sizeof(extend));
+	i = cpu_to_be32(index);
+	memcpy(data+10, &i, 4);
+	memcpy(data + 14, digest, 20);
+	if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) {
+		memcpy(&i, data + 6, 4); /* return code */
+		if (be32_to_cpu(i) == 0)
+			goto out; /* ok */
+	}
+ error:
+	if (!ima_test_mode)
+		IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n");
+	else
+		ima_error("Error Communicating to TPM chip\n");
+ out:
+	if (data != NULL)
+		kfree(data);
+}
+
+#endif
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h
--- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
+ *
+ * LSM IBM Integrity Measurement Architecture.		  
+ *
+ * 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_tpm_pcrread.h
+ *             implements glue code to connect IMA to the TPM driver
+ *             (glues to tpmdd on www.sourceforge.net/tpmdd)
+ */
+#ifndef __LINUX_IMA_TPM_PCRREAD_H
+#define __LINUX_IMA_TPM_PCRREAD_H
+
+#define TPM_BUFSIZE 2048
+
+extern struct tpm_chip *ima_used_chip;
+
+static const u8 pcrread[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 14,		/* length */
+	0, 0, 0, 21,		/* TPM_ORD_PcrRead */
+	0, 0, 0, 0		/* PCR index */
+};
+
+
+static void tpm_pcrread(int index, u8 * hash)
+{
+	u8 *data;
+	u32 i;
+	ssize_t len;
+
+	if (ima_used_chip == NULL)
+		return;
+
+	if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL)
+		goto error;
+
+	memcpy(data, pcrread, sizeof(pcrread));
+	i = cpu_to_be32(index);
+	memcpy(data+10, &i, 4);
+	if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) {
+		memcpy(&i, data + 6, 4); /* return code */
+		if (be32_to_cpu(i) == 0) {
+			memcpy(hash, data + 10, 20);
+			goto out; /* ok */
+		}
+	}
+ error:
+	if (!ima_test_mode)
+		IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n");
+	else
+		ima_error("Error Communicating to TPM chip\n");
+ out:
+	if (data != NULL)
+		kfree(data);
+}
+
+#endif
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/Kconfig linux-2.6.12-rc6-mm1-ima/security/ima/Kconfig
--- linux-2.6.12-rc6-mm1_orig/security/ima/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/Kconfig	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,90 @@
+#
+# IBM Integrity Measurement Architecture
+#
+
+#menu "TPM-based Integrity Measurement Architecture"
+
+config IMA_MEASURE
+	bool "TCG run-time Integrity Measurement Architecture"
+	depends on SECURITY && (CRYPTO_SHA1=y)
+	help
+    		To measure executable code running on this 
+		system, say Y. If you say Y, you must disable
+		any other security modules because LSM are 
+		currently not stackable.To actually start IMA,
+		you need to set a kernel boot parameter "ima=1".
+    		If unsure, say N.
+
+config IMA_TEST_MODE
+	bool "IMA test mode"
+	depends on IMA_MEASURE
+	default y
+	help
+		If you would like to test the measurement 
+		architecture but you do not have a TPM hardware 
+		on your system, say Y. Otherwise say N. If you say
+		Y and IMA does not find a TPM chip it will just bypass
+
+config IMA_MEASURE_PCR_IDX
+	int "PCR for Aggregate (8<= Index <= 15)"
+	depends on IMA_MEASURE
+	range 8 15
+	default 10
+	help
+		This determines the PCR index used for aggregating the
+		measurement list into the TPM hardware.
+		If unsure, use the default 10.
+
+config IMA_SKIP_BOOT_AGGREGATE
+	bool "Skip Boot Aggregate Creation"
+	depends on IMA_MEASURE
+	help
+		If y, the usual aggregate over the boot PCRs 
+		of the TPM is not calculated and not added to 
+		the measurement list. If unsure, say N.
+
+config IMA_KMEM_BYPASS_PROTECTION
+	bool "Invalidate PCR on /dev/kmem write"
+	depends on IMA_MEASURE
+	help
+		This setting enforces TPM PCR invalidation if /dev/kmem
+                is written (bypass of measurements possible). Usually,
+                this does not restrict normal systems. 
+		If unsure, say Y.
+
+config IMA_RAM_BYPASS_PROTECTION
+	bool "Invalidate PCR on /dev/ram write"
+	depends on IMA_MEASURE
+	help
+		This setting enforces TPM PCR invalidation if /dev/ram
+                is written (bypass of measurements possible). If you use
+		ramdisk, you might have a problem.
+		If unsure, say N.
+
+config IMA_HD_SD_BYPASS_PROTECTION
+	bool "Invalidate PCR on /dev/hdx /dev/sdx write"
+	depends on IMA_MEASURE
+	help
+		This setting enforces TPM PCR invalidation if /dev/hda,
+		/dev/hdb ... or /dev/sda, /dev/sdb ... are written
+		directly (bypass of measurement dirty flagging possible). 
+		This requires some changes in /etc/rc.sysinit:
+		   * check filesystems readonly (in rc.sysinit add "-n" fsck
+		     option, remove -a where it appears
+		   * switch off swapping (kernel controlled open on rw)
+		otherwise the PCRs will usually be invalidated.
+		If unsure, say N.
+
+config IMA_MEM_BYPASS_PROTECTION
+	bool "Invalidate PCR on /dev/mem write"
+	depends on IMA_MEASURE
+	help
+		This setting enforces TPM PCR invalidation if /dev/mem
+                is written (bypass of measurements possible). X needs
+		currently to write directly to /dev/mem. For client systems,
+		you might want to chose N here. For server systems not running X,
+		it is safe to say yes. 
+		If unsure, say N.
+
+#endmenu
+
diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/Makefile linux-2.6.12-rc6-mm1-ima/security/ima/Makefile
--- linux-2.6.12-rc6-mm1_orig/security/ima/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.12-rc6-mm1-ima/security/ima/Makefile	2005-06-14 16:25:05.000000000 -0400
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_IMA_MEASURE) += ima_init.o ima_main.o \
+				ima_queue.o ima_lsmhooks.o ima_fs.o
+
+
diff -uprN linux-2.6.12-rc6-mm1_orig/security/Kconfig linux-2.6.12-rc6-mm1-ima/security/Kconfig
--- linux-2.6.12-rc6-mm1_orig/security/Kconfig	2005-06-06 11:22:29.000000000 -0400
+++ linux-2.6.12-rc6-mm1-ima/security/Kconfig	2005-06-14 16:25:05.000000000 -0400
@@ -86,6 +86,7 @@ config SECURITY_SECLVL
 	  If you are unsure how to answer this question, answer N.
 
 source security/selinux/Kconfig
+source security/ima/Kconfig
 
 endmenu
 
diff -uprN linux-2.6.12-rc6-mm1_orig/security/Makefile linux-2.6.12-rc6-mm1-ima/security/Makefile
--- linux-2.6.12-rc6-mm1_orig/security/Makefile	2005-06-06 11:22:29.000000000 -0400
+++ linux-2.6.12-rc6-mm1-ima/security/Makefile	2005-06-14 16:25:05.000000000 -0400
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_KEYS)			+= keys/
 subdir-$(CONFIG_SECURITY_SELINUX)	+= selinux
+subdir-$(CONFIG_IMA_MEASURE)		+= ima
 
 # if we don't select a security model, use the default capabilities
 ifneq ($(CONFIG_SECURITY),y)
@@ -14,6 +15,7 @@ endif
 obj-$(CONFIG_SECURITY)			+= security.o dummy.o
 # Must precede capability.o in order to stack properly.
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
+obj-$(CONFIG_IMA_MEASURE)		+= ima/built-in.o
 obj-$(CONFIG_SECURITY_CAPABILITIES)	+= commoncap.o capability.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
 obj-$(CONFIG_SECURITY_SECLVL)		+= seclvl.o





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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 14:40 [PATCH] 3 of 5 IMA: LSM-based measurement code Reiner Sailer
@ 2005-06-15 20:02 ` James Morris
  2005-06-15 20:49   ` serue
  2005-06-16  2:01 ` Chris Wright
  1 sibling, 1 reply; 18+ messages in thread
From: James Morris @ 2005-06-15 20:02 UTC (permalink / raw)
  To: Reiner Sailer
  Cc: LKML, LSM, Tom Lendacky, Greg KH, Chris Wright, Emily Rattlif,
	Kylene Hall

On Wed, 15 Jun 2005, Reiner Sailer wrote:

> This patch applies against linux-2.6.12-rc6-mm1 and provides the main
> Integrity Measurement Architecture code (LSM-based).

Why are you still trying to use LSM for this?

Last time, there was discussion on the issue, ending here:
http://marc.theaimsgroup.com/?l=linux-kernel&m=111665641301726&w=2


- James
-- 
James Morris
<jmorris@redhat.com>



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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 20:02 ` James Morris
@ 2005-06-15 20:49   ` serue
  2005-06-15 20:58     ` Stephen Smalley
  2005-06-15 20:59     ` Chris Wright
  0 siblings, 2 replies; 18+ messages in thread
From: serue @ 2005-06-15 20:49 UTC (permalink / raw)
  To: James Morris
  Cc: Reiner Sailer, LKML, LSM, Tom Lendacky, Greg KH, Chris Wright,
	Emily Rattlif, Kylene Hall

Quoting James Morris (jmorris@redhat.com):
> On Wed, 15 Jun 2005, Reiner Sailer wrote:
> 
> > This patch applies against linux-2.6.12-rc6-mm1 and provides the main
> > Integrity Measurement Architecture code (LSM-based).
> 
> Why are you still trying to use LSM for this?

A long, long time ago, Crispin defined LSM's purpose as:

>> Main goal : we have to design a generic framework to be able to use
>> better
>> security policies than the current ones (DAC and capabilities).
>
>Sort of. We have to design a generic interface that exports enough
>kernel
>functionality to allow security developers to go off and create these
>better
>security policy modules. 

Since IMA provides support for a new type of security policy,
specifically remote system integrity verification, I don't see
where LSM shouldn't necessarily be used.

I'm also curious about the current kernel development approach:
On the one hand, when filesystem auditing was introduced, Christoph
asked whether inotify and audit should be merged because they hook
some of the same places.  Can someone reconcile these points of view
for me, please?  If Reiner goes ahead and moves the IMA code straight
into the kernel, does anyone doubt that he'll be asked to merge it
with LSM?

I'm not pushing one way or the other - I don't care whether IMA is
an LSM or not :)  I just don't understand the current climate.

thanks,
-serge

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 20:49   ` serue
@ 2005-06-15 20:58     ` Stephen Smalley
  2005-06-15 21:48       ` serue
  2005-06-15 20:59     ` Chris Wright
  1 sibling, 1 reply; 18+ messages in thread
From: Stephen Smalley @ 2005-06-15 20:58 UTC (permalink / raw)
  To: serue
  Cc: James Morris, Tom Lendacky, Greg KH, LKML, LSM, Chris Wright,
	Reiner Sailer, Emily Rattlif, Kylene Hall

On Wed, 2005-06-15 at 15:49 -0500, serue@us.ibm.com wrote:
> A long, long time ago, Crispin defined LSM's purpose as:
> 
> >> Main goal : we have to design a generic framework to be able to use
> >> better
> >> security policies than the current ones (DAC and capabilities).
> >
> >Sort of. We have to design a generic interface that exports enough
> >kernel
> >functionality to allow security developers to go off and create these
> >better
> >security policy modules. 
> 
> Since IMA provides support for a new type of security policy,
> specifically remote system integrity verification, I don't see
> where LSM shouldn't necessarily be used.

IMA isn't an access control model.  Also, LSM is overkill for its needs
in many ways (IMA only needs a few LSM hooks) and is inadequate in other
ways (LSM lacks a hook needed by IMA for measuring modules, although one
might argue that there could be benefit in adding such a hook to LSM
itself for access control purposes).  

> I'm also curious about the current kernel development approach:
> On the one hand, when filesystem auditing was introduced, Christoph
> asked whether inotify and audit should be merged because they hook
> some of the same places.  Can someone reconcile these points of view
> for me, please?  If Reiner goes ahead and moves the IMA code straight
> into the kernel, does anyone doubt that he'll be asked to merge it
> with LSM?
> 
> I'm not pushing one way or the other - I don't care whether IMA is
> an LSM or not :)  I just don't understand the current climate.

If you look at the inotify patch, I think that they've moved the dnotify
hooks into a more generic set of fsnotify hooks that are leveraged by
both dnotify and inotify to reduce duplication in the core kernel.  The
same approach could be used for hooks that would be co-located by audit
and LSM or by integrity measurement and LSM.  Of course, you don't want
to blindly place the integrity measurement hooks at the same locations
if a different placement would be more optimal for your purposes, so
you'd want to give it some thought.

-- 
Stephen Smalley
National Security Agency


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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 20:49   ` serue
  2005-06-15 20:58     ` Stephen Smalley
@ 2005-06-15 20:59     ` Chris Wright
  2005-06-15 21:50       ` serue
  1 sibling, 1 reply; 18+ messages in thread
From: Chris Wright @ 2005-06-15 20:59 UTC (permalink / raw)
  To: serue
  Cc: James Morris, Reiner Sailer, LKML, LSM, Tom Lendacky, Greg KH,
	Chris Wright, Emily Rattlif, Kylene Hall

* serue@us.ibm.com (serue@us.ibm.com) wrote:
> Since IMA provides support for a new type of security policy,
> specifically remote system integrity verification, I don't see
> where LSM shouldn't necessarily be used.
> 
> I'm also curious about the current kernel development approach:
> On the one hand, when filesystem auditing was introduced, Christoph
> asked whether inotify and audit should be merged because they hook
> some of the same places.  Can someone reconcile these points of view
> for me, please?  If Reiner goes ahead and moves the IMA code straight
> into the kernel, does anyone doubt that he'll be asked to merge it
> with LSM?

The primary purpose of the hooks is access control.  Some of them, of
course, are helpers to keep labels coherent.  IIRC, James objected
because the measurement data was simply collected from these hooks.

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 20:58     ` Stephen Smalley
@ 2005-06-15 21:48       ` serue
  0 siblings, 0 replies; 18+ messages in thread
From: serue @ 2005-06-15 21:48 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: serue, James Morris, Tom Lendacky, Greg KH, LKML, LSM,
	Chris Wright, Reiner Sailer, Emily Rattlif, Kylene Hall

Quoting Stephen Smalley (sds@tycho.nsa.gov):
> On Wed, 2005-06-15 at 15:49 -0500, serue@us.ibm.com wrote:
> > A long, long time ago, Crispin defined LSM's purpose as:
> > 
> > >> Main goal : we have to design a generic framework to be able to use
> > >> better
> > >> security policies than the current ones (DAC and capabilities).
> > >
> > >Sort of. We have to design a generic interface that exports enough
> > >kernel
> > >functionality to allow security developers to go off and create these
> > >better
> > >security policy modules. 
> > 
> > Since IMA provides support for a new type of security policy,
> > specifically remote system integrity verification, I don't see
> > where LSM shouldn't necessarily be used.
> 
> IMA isn't an access control model.  Also, LSM is overkill for its needs
> in many ways (IMA only needs a few LSM hooks)

I don't think only needing a few of the hooks should disqualify it -
same is true of capability.  On the other hand the fact that it always
grants permission could be taken to imply LSM is overkill.  It seems
to come down to a question of aesthetics.

> and is inadequate in other
> ways (LSM lacks a hook needed by IMA for measuring modules, although one
> might argue that there could be benefit in adding such a hook to LSM
> itself for access control purposes).  

True.  In fact, since those hooks were originally dropped because there
wasn't a user for them, refusing a user because the hooks aren't there
would be hillarious!

> If you look at the inotify patch, I think that they've moved the dnotify
> hooks into a more generic set of fsnotify hooks that are leveraged by
> both dnotify and inotify to reduce duplication in the core kernel.  The

Oh, cool, I actually hadn't noticed that.  (Last I checked inotify was
in... november?)

> same approach could be used for hooks that would be co-located by audit
> and LSM or by integrity measurement and LSM.  Of course, you don't want

In that case it seems to further obfuscate the code, though.  We then
have a common update hook to just call integrity+LSM hooks, which
then call into their own subsystems...

Is there a distinct advantage to be gained by separating the two?

> to blindly place the integrity measurement hooks at the same locations
> if a different placement would be more optimal for your purposes, so
> you'd want to give it some thought.

That's true, of course.  Reiner, would any of the integrity measurement
hooks be moved to a better place than the current LSM hooks?  Is there a
preferred ordering - ie measurement should always happen before the LSM
modules, or always after?  Either of these would of course be clear
reasons to separate IMA into its own subsystem.

thanks,
-serge

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 20:59     ` Chris Wright
@ 2005-06-15 21:50       ` serue
  2005-06-15 21:53         ` Chris Wright
  2005-06-15 22:00         ` Casey Schaufler
  0 siblings, 2 replies; 18+ messages in thread
From: serue @ 2005-06-15 21:50 UTC (permalink / raw)
  To: Chris Wright
  Cc: serue, James Morris, Reiner Sailer, LKML, LSM, Tom Lendacky,
	Greg KH, Emily Rattlif, Kylene Hall

Quoting Chris Wright (chrisw@osdl.org):
> The primary purpose of the hooks is access control.  Some of them, of
> course, are helpers to keep labels coherent.  IIRC, James objected
> because the measurement data was simply collected from these hooks.

Ok, so to be clear, any module which does not directly impose some form
of access control is not eligible for an LSM?

(in that case that clearly settles the issue)

thanks,
-serge

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 21:50       ` serue
@ 2005-06-15 21:53         ` Chris Wright
  2005-06-15 22:42           ` Serge E. Hallyn
  2005-06-15 22:00         ` Casey Schaufler
  1 sibling, 1 reply; 18+ messages in thread
From: Chris Wright @ 2005-06-15 21:53 UTC (permalink / raw)
  To: serue
  Cc: Chris Wright, James Morris, Reiner Sailer, LKML, LSM,
	Tom Lendacky, Greg KH, Emily Rattlif, Kylene Hall

* serue@us.ibm.com (serue@us.ibm.com) wrote:
> Quoting Chris Wright (chrisw@osdl.org):
> > The primary purpose of the hooks is access control.  Some of them, of
> > course, are helpers to keep labels coherent.  IIRC, James objected
> > because the measurement data was simply collected from these hooks.
> 
> Ok, so to be clear, any module which does not directly impose some form
> of access control is not eligible for an LSM?

That's exactly the intention, yes.

thanks,
-chris

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 21:50       ` serue
  2005-06-15 21:53         ` Chris Wright
@ 2005-06-15 22:00         ` Casey Schaufler
  2005-06-15 22:38           ` Serge E. Hallyn
  1 sibling, 1 reply; 18+ messages in thread
From: Casey Schaufler @ 2005-06-15 22:00 UTC (permalink / raw)
  To: serue, Chris Wright
  Cc: Tom Lendacky, Greg KH, LSM, LKML, Reiner Sailer, Emily Rattlif,
	Kylene Hall



--- serue@us.ibm.com wrote:

> Ok, so to be clear, any module which does not
> directly impose some form
> of access control is not eligible for an LSM?

In particular, an additional access control.
LSM is not for changing the existing policy,
it is for imposing additional policy.

You could, of course, add code to act on the
integrity measurements you've made, in which
case you could be in conformance with the
stated eligibilty requirements.

> (in that case that clearly settles the issue)

It sure took the wind out of the sails for the
SGI audit implementation.



Casey Schaufler
casey@schaufler-ca.com

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 22:00         ` Casey Schaufler
@ 2005-06-15 22:38           ` Serge E. Hallyn
  2005-06-15 22:40             ` Chris Wright
  0 siblings, 1 reply; 18+ messages in thread
From: Serge E. Hallyn @ 2005-06-15 22:38 UTC (permalink / raw)
  To: Casey Schaufler
  Cc: Chris Wright, Tom Lendacky, Greg KH, LSM, LKML, Reiner Sailer,
	Emily Rattlif, Kylene Hall

Quoting Casey Schaufler (casey@schaufler-ca.com):
> You could, of course, add code to act on the
> integrity measurements you've made, in which
> case you could be in conformance with the
> stated eligibilty requirements.

:)

IIUC, another (separately implemented) module - whose
release I'm anxiously awaiting - will actually basically
be doing that...

-serge


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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 22:38           ` Serge E. Hallyn
@ 2005-06-15 22:40             ` Chris Wright
  2005-06-15 22:52               ` Serge E. Hallyn
  0 siblings, 1 reply; 18+ messages in thread
From: Chris Wright @ 2005-06-15 22:40 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: Casey Schaufler, Chris Wright, Tom Lendacky, Greg KH, LSM, LKML,
	Reiner Sailer, Emily Rattlif, Kylene Hall

* Serge E. Hallyn (serue@us.ibm.com) wrote:
> Quoting Casey Schaufler (casey@schaufler-ca.com):
> > You could, of course, add code to act on the
> > integrity measurements you've made, in which
> > case you could be in conformance with the
> > stated eligibilty requirements.
> 
> :)
> 
> IIUC, another (separately implemented) module - whose
> release I'm anxiously awaiting - will actually basically
> be doing that...

At which point we can consider it for LSM inclusion, so let's wait until
then, ok?

thanks,
-chris

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 21:53         ` Chris Wright
@ 2005-06-15 22:42           ` Serge E. Hallyn
  2005-06-15 22:49             ` Chris Wright
  0 siblings, 1 reply; 18+ messages in thread
From: Serge E. Hallyn @ 2005-06-15 22:42 UTC (permalink / raw)
  To: Chris Wright
  Cc: James Morris, Reiner Sailer, LKML, LSM, Tom Lendacky, Greg KH,
	Emily Rattlif, Kylene Hall

Quoting Chris Wright (chrisw@osdl.org):
> * serue@us.ibm.com (serue@us.ibm.com) wrote:
> > Quoting Chris Wright (chrisw@osdl.org):
> > > The primary purpose of the hooks is access control.  Some of them, of
> > > course, are helpers to keep labels coherent.  IIRC, James objected
> > > because the measurement data was simply collected from these hooks.
> > 
> > Ok, so to be clear, any module which does not directly impose some form
> > of access control is not eligible for an LSM?
> 
> That's exactly the intention, yes.

Ok, thanks.

I thought it was intended to be more general than that - in fact I
specifically thought it was not intended to be purely for single machine
authentication decisions within a single kernel module, but that anything
which would aid in enabling new security features, locally or remotely,
would be game.  (Which - it means nothing - but I would clearly have
preferred :)

Thanks for setting me straight.

-serge


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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 22:42           ` Serge E. Hallyn
@ 2005-06-15 22:49             ` Chris Wright
  0 siblings, 0 replies; 18+ messages in thread
From: Chris Wright @ 2005-06-15 22:49 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: Chris Wright, James Morris, Reiner Sailer, LKML, LSM,
	Tom Lendacky, Greg KH, Emily Rattlif, Kylene Hall

* Serge E. Hallyn (serue@us.ibm.com) wrote:
> Quoting Chris Wright (chrisw@osdl.org):
> > * serue@us.ibm.com (serue@us.ibm.com) wrote:
> > > Quoting Chris Wright (chrisw@osdl.org):
> > > > The primary purpose of the hooks is access control.  Some of them, of
> > > > course, are helpers to keep labels coherent.  IIRC, James objected
> > > > because the measurement data was simply collected from these hooks.
> > > 
> > > Ok, so to be clear, any module which does not directly impose some form
> > > of access control is not eligible for an LSM?
> > 
> > That's exactly the intention, yes.
> 
> Ok, thanks.
> 
> I thought it was intended to be more general than that - in fact I
> specifically thought it was not intended to be purely for single machine
> authentication decisions within a single kernel module, but that anything
> which would aid in enabling new security features, locally or remotely,
> would be game.  (Which - it means nothing - but I would clearly have
> preferred :)

The problem with being more general is it becomes a more attractive
target for abuse.

thanks,
-chris

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 22:40             ` Chris Wright
@ 2005-06-15 22:52               ` Serge E. Hallyn
  0 siblings, 0 replies; 18+ messages in thread
From: Serge E. Hallyn @ 2005-06-15 22:52 UTC (permalink / raw)
  To: Chris Wright
  Cc: Casey Schaufler, Tom Lendacky, Greg KH, LSM, LKML, Reiner Sailer,
	Emily Rattlif, Kylene Hall

Quoting Chris Wright (chrisw@osdl.org):
> > IIUC, another (separately implemented) module - whose
> > release I'm anxiously awaiting - will actually basically
> > be doing that...
> 
> At which point we can consider it for LSM inclusion, so let's wait until
> then, ok?

Absolutely, I just had to point that out cause it's kind of funny - as
though someone's out purely to "get something tcg-related into lsm"  :)


"Darnit, maybe if we just tweak this little bit to return -EPERM at 5am"


-serge


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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 14:40 [PATCH] 3 of 5 IMA: LSM-based measurement code Reiner Sailer
  2005-06-15 20:02 ` James Morris
@ 2005-06-16  2:01 ` Chris Wright
  1 sibling, 0 replies; 18+ messages in thread
From: Chris Wright @ 2005-06-16  2:01 UTC (permalink / raw)
  To: Reiner Sailer
  Cc: LKML, LSM, Chris Wright, Greg KH, Kylene Hall, Tom Lendacky,
	Emily Rattlif, Reiner Sailer

* Reiner Sailer (sailer@watson.ibm.com) wrote:
> This patch applies against linux-2.6.12-rc6-mm1 and provides the main
> Integrity Measurement Architecture code (LSM-based).

Aside of LSM issue, some comments below.

> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_fs.c	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_fs.c	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,432 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Kylene Hall <kjhall@us.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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 imafs 
> + *		for reporting measurement log and userspace measure requests
> + */
> +
> +#include <linux/init.h>
> +#include <linux/config.h>
> +#include <linux/kernel.h>
> +#include <linux/pagemap.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include <linux/string.h>
> +#include <linux/security.h>
> +#include <linux/major.h>
> +#include <linux/seq_file.h>
> +#include <linux/percpu.h>
> +#include <asm/uaccess.h>
> +#include <asm/semaphore.h>
> +#include <linux/file.h>
> +#include <linux/parser.h>
> +#include <linux/device.h>

Do you need all these headers (also, CodingStyle nitpick, move asm/
includes to after linux/ includes).  For example, pagemap, vmalloca,
init.h x 2, percpu...
> +
> +#include "ima.h"
> +
> +struct measure_request {
> +	int fd;
> +	u16 label;
> +};
> +
> +extern struct h_table htable;

needs header decl, htable is used in a few .c files.

> +/* based on selinux pseudo filesystem */
> +
> +#define TMPBUFLEN 12
> +static ssize_t ima_show_htable_value(char __user * buf, size_t count,
> +				     loff_t * ppos, atomic_t * val)
> +{
> +	char tmpbuf[TMPBUFLEN];
> +	ssize_t len;
> +
> +	len = scnprintf(tmpbuf, TMPBUFLEN, "%i\n", atomic_read(val));
> +	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_show_htable_clean_inode_hits(struct file *filp,
> +						char __user * buf,
> +						size_t count,
> +						loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.clean_inode_hits);
> +}
> +static struct file_operations ima_htable_clean_inode_hits_ops = {
> +	.read = ima_show_htable_clean_inode_hits
> +};
> +
> +static ssize_t ima_show_htable_clean_table_hits(struct file *filp,
> +						char __user * buf,
> +						size_t count,
> +						loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.clean_table_hits);
> +}
> +static struct file_operations ima_htable_clean_table_hits_ops = {
> +	.read = ima_show_htable_clean_table_hits
> +};
> +
> +static ssize_t ima_show_htable_dirty_table_hits(struct file *filp,
> +						char __user * buf,
> +						size_t count,
> +						loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.dirty_table_hits);
> +}
> +static struct file_operations ima_htable_dirty_table_hits_ops = {
> +	.read = ima_show_htable_dirty_table_hits
> +};
> +
> +static ssize_t ima_show_htable_changed_files(struct file *filp,
> +					     char __user * buf,
> +					     size_t count, loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.changed_files);
> +}
> +static struct file_operations ima_htable_changed_files_ops = {
> +	.read = ima_show_htable_changed_files
> +};
> +
> +static ssize_t ima_show_htable_user_measure(struct file *filp,
> +					    char __user * buf,
> +					    size_t count, loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.user_measure);
> +}
> +static struct file_operations ima_htable_user_measure_ops = {
> +	.read = ima_show_htable_user_measure
> +};
> +
> +static ssize_t ima_show_htable_kernel_measure(struct file *filp,
> +					      char __user * buf,
> +					      size_t count, loff_t * ppos)
> +{
> +	return ima_show_htable_value(buf, count, ppos,
> +				     &htable.kernel_measure);
> +}
> +static struct file_operations ima_htable_kernel_measure_ops = {
> +	.read = ima_show_htable_kernel_measure
> +};
> +
> +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, &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, &htable.len);
> +
> +}
> +static struct file_operations ima_measurements_count_ops = {
> +	.read = ima_show_measurements_count
> +};
> +
> +extern int measure_user_file(struct file *, u32 measure_flags);
> +extern int ima_enabled;

Aren't these already declared in header?  If not, they should be.

> +enum ima_inos {
> +	IMA_ROOT_INO = 1,
> +	IMA_MEASURE,		/* userspace measurement request */
> +	IMA_MEASUREMENTS,	/* measurement log in binary format */
> +	IMA_MEASUREMENTS_COUNT,	/* number of measurements in log */
> +	IMA_HTABLE_CLEAN_INODE_HITS,
> +	IMA_HTABLE_CLEAN_TABLE_HITS,
> +	IMA_HTABLE_DIRTY_TABLE_HITS,
> +	IMA_HTABLE_CHANGED_FILES,
> +	IMA_HTABLE_USER_MEASURE,
> +	IMA_HTABLE_KERNEL_MEASURE,
> +	IMA_HTABLE_VIOLATIONS,
> +};
> +
> +#define IMA_MAX_EVENT_SIZE 69
> +/* print format: 32bit-le=pcr#||char[20]=digest||flags||filename||'\0'  flags bits: 32-16 application flags, 15-3 kernel flags, 2-0 hook len(filename)<=40*/

Why not ascii?  Then you don't need another tool to interpret the
output?

> +static int print_measure_event_entry(struct measure_entry *e, char *buf,
> +				     int count)
> +{
> +	void *ptr = (void *) buf;

needless cast.

> +	int filename_len = strlen(e->file_name);
> +
> +	/* 1st: PCR used is always the same (config option) in little-endian format */
> +	*((u32 *) ptr) = (u32) CONFIG_IMA_MEASURE_PCR_IDX;
> +	ptr += 4;
> +
> +	/* 2nd: SHA1 */
> +	memcpy(ptr, e->digest, 20);
> +	ptr += 20;
> +
> +	/* 3rd: flags */
> +	*((u32 *)ptr) = e->measure_flags;
> +	ptr += 4;
> +
> +	/* 4th:  filename <= 40 + \'0' delimiter */
> +	if (filename_len > TCG_EVENT_NAME_LEN_MAX)
> +		filename_len = TCG_EVENT_NAME_LEN_MAX;
> +
> +	memcpy(ptr, e->file_name, filename_len);
> +	ptr += filename_len;
> +
> +	/* 4th: delimiter */
> +	*((char *) ptr) = '\0';
> +	ptr += 1;
> +
> +	return ((u32) ptr - (u32) buf);
> +}
> +
> +/* Position pointer is overrided to mean entry # rather than size in bytes */
> +static ssize_t ima_measurements_read(struct file *filp, char __user * buf,
> +				     size_t count, loff_t * ppos)
> +{
> +	struct queue_entry *qe;
> +	char *tmpbuf;
> +	int tmpsiz, i, ret = 0, len;
> +	loff_t pos = 0;
> +
> +	if (count < 0)
> +		return -EINVAL;
> +
> +	tmpsiz = (count < PAGE_SIZE) ? count : PAGE_SIZE;
> +	tmpbuf = kmalloc(tmpsiz, GFP_KERNEL);
> +	if (!tmpbuf)
> +		return -ENOMEM;
> +
> +
> +	down(&h_table_mutex);
> +
> +	/* fast forward to correct measurement for requested position */
> +	for (qe = first_measurement, i = 0; qe && qe->entry && i < *ppos;
> +	     qe = qe->later, i++);

looks like seq_file candidate.

> +	/* make sure the next entry fits completely */
> +	while ((tmpsiz >= IMA_MAX_EVENT_SIZE) && qe && qe->entry) {
> +		/* now fill rest of page */
> +		len =
> +		    print_measure_event_entry(qe->entry, tmpbuf + ret,
> +					      count);
> +		qe = qe->later;
> +		tmpsiz -= len;
> +		ret += len;
> +		*ppos += 1;
> +	}
> +	up(&h_table_mutex);
> +	len = simple_read_from_buffer(buf, count, &pos, tmpbuf, ret);
> +
> +	kfree(tmpbuf);
> +	return len;
> +}
> +
> +static ssize_t ima_measure_write(struct file *file,
> +				 const char __user * buf, size_t count,
> +				 loff_t * ppos)
> +{
> +	struct measure_request *mr;
> +	struct file *meas_file;
> +	int error = -EINVAL;
> +	char tmpbuf[sizeof(struct measure_request)];
> +
> +	atomic_inc(&htable.user_measure);
> +	if (count != sizeof(struct measure_request)) {
> +		ima_error("illegal request size (%d, expected %d).\n",
> +			  count, sizeof(struct measure_request));

no htable.user_measure cleanup, does that matter?  same with the other
error conditions.

> +		return -EIO;
> +	}
> +
> +	if (copy_from_user(tmpbuf, buf, count)) {
> +		ima_error("trouble copying request\n");
> +		return -EIO;
> +	}
> +
> +	mr = (struct measure_request *) tmpbuf;
> +	if (mr->fd < 0) {
> +		ima_error("bad descriptor request\n");
> +		return -EBADF;
> +	}
> +
> +	meas_file = fget(mr->fd);
> +	if (!meas_file) {
> +		ima_error("could not open request\n");
> +		return -EACCES;
> +	}
> +
> +	error = measure_user_file(meas_file, (u32)(((mr->label) << 16) | USER_MEASURE_FLAG));
> +	fput(meas_file);
> +	if (error) {
> +		ima_error("problem measuring request\n");
> +		return error;
> +	} else
> +		return count;
> +}
> +
> +static struct file_operations ima_measure_ops = {
> +	.write = ima_measure_write,
> +};
> +
> +static struct file_operations ima_measurements_ops = {
> +	.read = ima_measurements_read
> +};
> +
> +enum { Opt_uid, Opt_gid };
> +
> +static match_table_t tokens = {
> +	{Opt_uid, "uid=%u"},
> +	{Opt_gid, "gid=%u"}
> +};
> +
> +static int ima_remount(struct super_block *sb, int *flags, char *data)
> +{
> +	char *p;
> +	int option;
> +	int changed = 0, uid = 0, gid = 0;
> +	struct inode *inode;
> +
> +	if (!data)
> +		return 0;
> +
> +	while ((p = strsep(&data, ",")) != NULL) {
> +		substring_t args[MAX_OPT_ARGS];
> +		int token;
> +		if (!*p)
> +			continue;
> +
> +		token = match_token(p, tokens, args);
> +		switch (token) {
> +
> +		case Opt_uid:
> +			if (match_int(args, &option))
> +				return -EINVAL;
> +			uid = option;
> +			changed = 1;
> +			break;
> +
> +		case Opt_gid:
> +			if (match_int(args, &option))
> +				return -EINVAL;
> +			gid = option;
> +			changed = 1;
> +			break;
> +
> +		default:
> +			ima_error("ima_fs: unrecognized mount option\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (changed) {
> +		list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
> +			inode->i_uid = uid;
> +			inode->i_gid = gid;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/* imafs Filenames and Permissions are set here -- Double CHECK */
> +static int ima_fill_super(struct super_block *sb, void *data, int silent)
> +{
> +	static struct tree_descr ima_files[] = {
> +		[IMA_MEASURE] =
> +		    {"measurereq", &ima_measure_ops, S_IWUSR | S_IWGRP},
> +		[IMA_MEASUREMENTS] =
> +		    {"binary_measurements", &ima_measurements_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_MEASUREMENTS_COUNT] =
> +		    {"binary_measurements_count",
> +		     &ima_measurements_count_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_CLEAN_INODE_HITS] =
> +		    {"clean_inode_hits", &ima_htable_clean_inode_hits_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_CLEAN_TABLE_HITS] =
> +		    {"clean_hashtable_hits",
> +		     &ima_htable_clean_table_hits_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_DIRTY_TABLE_HITS] =
> +		    {"dirty_hashtable_hits",
> +		     &ima_htable_dirty_table_hits_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_CHANGED_FILES] =
> +		    {"changed_files", &ima_htable_changed_files_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_USER_MEASURE] =
> +		    {"user_count", &ima_htable_user_measure_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_KERNEL_MEASURE] =
> +		    {"kernel_count", &ima_htable_kernel_measure_ops,
> +		     S_IRUSR | S_IRGRP},
> +		[IMA_HTABLE_VIOLATIONS] =
> +		    {"violations", &ima_htable_violations_ops,
> +		     S_IRUSR | S_IRGRP},
> +
> +		/* last one */ {""}
> +	};
> +
> +	return simple_fill_super(sb, IMA_MAGIC, ima_files);
> +}
> +
> +static struct super_block *ima_get_sb(struct file_system_type *fs_type,
> +				      int flags, const char *dev_name,
> +				      void *data)
> +{
> +	struct super_block *sb;
> +	sb = get_sb_single(fs_type, flags, data, ima_fill_super);
> +
> +	sb->s_op->remount_fs = ima_remount;
> +
> +	return sb;
> +}
> +
> +static struct file_system_type ima_fs_type = {
> +	.name = "imafs",
> +	.get_sb = ima_get_sb,
> +	.kill_sb = kill_litter_super,
> +};
> +
> +struct vfsmount *imafs_mount;
> +
> +void ima_fs_init(void)
> +{
> +	int err;
> +
> +	if (!ima_enabled)
> +		return;
> +
> +	err = register_filesystem(&ima_fs_type);
> +	if (!err) {
> +		imafs_mount = kern_mount(&ima_fs_type);
> +		if (IS_ERR(imafs_mount)) {
> +			ima_error("imafs:  could not mount!\n");
> +			err = PTR_ERR(imafs_mount);
> +			imafs_mount = NULL;
> +		}
> +	}
> +	return;
> +}
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima.h linux-2.6.12-rc6-mm1-ima/security/ima/ima.h
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima.h	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima.h	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,242 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.	  
> + *
> + * 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
> + *             defs
> + */
> +#ifndef __LINUX_IMA_H
> +#define __LINUX_IMA_H
> +
> +#include <linux/types.h>
> +#include <linux/fs.h>
> +#include <linux/major.h>
> +#include <linux/crypto.h>
> +#include <linux/security.h>
> +
> +#define ima_printk(level, format, arg...)	\
> +	printk(level "ima (%s): " format ,__func__, ## arg)
> +       
> +#define ima_error(format, arg...)	\
> +	ima_printk(KERN_ERR, format, ## arg)
> +
> +#define ima_info(format, arg...)	\
> +	ima_printk(KERN_INFO, format, ## arg)
> +
> +/* if you cannot tolerate panic for the sake of attestation guarantees,
> + * then redefine IMA_PANIC to, e g., ima_error (see INSTALL documentation) */
> +#define IMA_PANIC   \
> +	panic
> +
> +/* set during registering as lsm */
> +extern unsigned char ima_terminating;
> +void invalidate_pcr(char *);
> +
> +#define IMA_MEASURE_MODULE_NAME	"IMA"
> +#define TCG_EVENT_NAME_LEN_MAX	40
> +
> +/* file systems we expect to change without
> + * our inode_permission hook being called (nfs, remote fs) */
> +#define NFS_SUPER_MAGIC		0x6969
> +
> +/* file systems we won't measure */
> +#define IMA_MAGIC 		0x9999
> +
> +/* Flags for measurement entries (identifying hook) */
> +#define FLAG_HOOK_MASK		0x0f
> +#define MMAP_MEASURE_FLAG 	0x01
> +#define MODULE_MEASURE_FLAG 	0x02
> +#define USER_MEASURE_FLAG 	0x04
> +
> +#define MEASURE_HTABLE_SIZE	512
> +#define HASH_KEY(inode_number) ((inode_number) % MEASURE_HTABLE_SIZE)
> +#define SHA_KEY(sha_value) (((sha_value)[18] << 8 | (sha_value)[19]) % MEASURE_HTABLE_SIZE)
> +typedef enum { CLEAN, DIRTY, CHANGED } ima_entry_flags;
> +
> +/* security structure appended to inodes */
> +struct ima_inode {
> +	atomic_t measure_count;	/* # processes currently using this file in measure-mode */
> +	ima_entry_flags dirty;
> +};
> +
> +/* security structure appended to measured files*/
> +struct ima_file {
> +	char is_measuring;	/* identify fds that are "measuring" */
> +};
> +
> +/* get/store security state information;
> + * if stacking were to be implemented, this would be the place */
> +#define ima_get_inode_security(inode) \
> +	((struct ima_inode *) ((inode)->i_security))
> +
> +#define ima_store_inode_security(inode,sec_struct) \
> +	((inode)->i_security = (sec_struct))
> +
> +#define ima_get_file_security(file) \
> +	((struct ima_file *) ((file)->f_security))
> +
> +#define ima_store_file_security(file, sec_struct) \
> +	((file)->f_security = (sec_struct))
> +
> +struct measure_entry {
> +	u32 measure_flags;
> +	unsigned long inode_nr;
> +	dev_t dev_id;
> +	ima_entry_flags dirty;
> +	u8 digest[20];				  /* sha1 measurement hash */
> +	char file_name[TCG_EVENT_NAME_LEN_MAX+1]; /* name + \0 */
> +	struct super_block *super_block;	/* super block link (for umount-dirty flagging) */
> +};
> +
> +struct sha_entry {
> +	struct sha_entry *next;
> +	u8 *digest;
> +	struct measure_entry *m_entry;
> +};
> +
> +struct queue_entry {
> +	struct queue_entry *next;
> +	struct queue_entry *later;
> +	struct measure_entry *entry;
> +};
> +
> +extern struct queue_entry *first_measurement;	/* for printing */
> +extern struct queue_entry *latest_measurement;	/* for adding */
> +
> +/* hash table to keep fast access to past measurements 
> + * uses one global lock for now (read/write) */
> +extern struct semaphore h_table_mutex;
> +
> +struct h_table {
> +	atomic_t len;
> +	atomic_t user_measure; /* # measurements requested from userspace */
> +	atomic_t kernel_measure; /* # measurements performed from kernel */
> +	atomic_t clean_inode_hits; /* times we find an inode clean when measuring */
> +	atomic_t clean_table_hits; /* times we find a clean htable hit */
> +	atomic_t dirty_table_hits; /* times we find a dirty htable hit */
> +	atomic_t changed_files;	 /* times we realize a dirty marked entry really changed */
> +	atomic_t violations;

Why are all these atomic?

> +	unsigned int max_htable_size;
> +	struct queue_entry *queue[MEASURE_HTABLE_SIZE];
> +	atomic_t queue_len[MEASURE_HTABLE_SIZE];
> +};
> +
> +struct sha_table {
> +	atomic_t len;
> +	unsigned int max_htable_size;
> +	struct sha_entry *queue[MEASURE_HTABLE_SIZE];
> +	atomic_t queue_len[MEASURE_HTABLE_SIZE];
> +};
> +
> +#define MEM_MINOR	1
> +#define KMEM_MINOR	2

This looks like a bad idea.  Granted, these numbers aren't likely
candidates to be dynamic (while others device numbers are).
If you have to do this, perhaps it could be done from userspace,
i.e. read from the sysfs entries of the devices you care about (like
/sys/class/mem/kmem/dev), and tell your module to handle those specially,
instead of all these magic config options.  Puts that policy in userspace
where it belongs.

> +#ifdef CONFIG_IMA_KMEM_BYPASS_PROTECTION
> +static inline void check_kmem_bypass(struct inode *inode)
> +{
> +	if ((imajor(inode) == MEM_MAJOR) 
> +	    && S_ISCHR(inode->i_mode) && (iminor(inode) == KMEM_MINOR))
> +		invalidate_pcr("/dev/kmem write violation");
> +}
> +#else
> +static inline void check_kmem_bypass(struct inode *inode)
> +{
> +	return;
> +}
> +#endif
> +
> +#ifdef CONFIG_IMA_MEM_BYPASS_PROTECTION
> +static inline void check_mem_bypass(struct inode *inode)
> +{
> +	if ((imajor(inode) == MEM_MAJOR) 
> +	    && S_ISCHR(inode->i_mode) && (iminor(inode) == MEM_MINOR))
> +		invalidate_pcr("/dev/mmem write violation");
> +}
> +#else
> +static inline void check_mem_bypass(struct inode *inode)
> +{
> +	return;
> +}
> +#endif
> +
> +#ifdef CONFIG_IMA_RAM_BYPASS_PROTECTION
> +static inline void check_ram_bypass(struct inode *inode)
> +{
> +	if ((imajor(inode) == RAMDISK_MAJOR) && S_ISBLK(inode->i_mode))
> +		invalidate_pcr("/dev/ram write violation");
> +}
> +#else
> +static inline void check_ram_bypass(struct inode *inode)
> +{
> +	return;
> +}
> +#endif
> +
> +#ifdef CONFIG_IMA_HD_SD_BYPASS_PROTECTION
> +static inline void check_hd_sd_bypass(struct inode *inode)
> +{
> +	if ((imajor(inode) == HD_MAJOR) && S_ISBLK(inode->i_mode))
> +		invalidate_pcr("/dev/hdx write violation");

This makes it trivial for user to invalidate pcr (think pluggable media,
etc).  Sounds problematic.

> +	else if ((imajor(inode) == SCSI_DISK0_MAJOR) && S_ISBLK(inode->i_mode))
> +		invalidate_pcr("/dev/sdx write violation");
> +}
> +#else
> +static inline void check_hd_sd_bypass(struct inode *inode)
> +{
> +	return;
> +}
> +#endif
> +
> +/* configuration options*/
> +extern int ima_test_mode;
> +extern int skip_boot_aggregate;
> +
> +static inline void read_configs(void)
> +{
> +#ifdef CONFIG_IMA_TEST_MODE
> +	ima_test_mode = 1;
> +#else
> +	ima_test_mode = 0;
> +#endif
> +
> +#ifdef CONFIG_IMA_SKIP_BOOT_AGGREGATE
> +	skip_boot_aggregate = 1;
> +#else
> +	skip_boot_aggregate = 0;
> +#endif

Why is this a function?  These are just global variables.  They could be
either be conditionally defined, or directly assigned to Kconfig ints.

> +}
> +
> +#ifdef CONFIG_TCG_TPM

These do not look like they belong here.  That's tpm stuff (esp. the
conditionally defined tpm_ interfaces).

> +struct tpm_chip;
> +
> +extern ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz);
> +
> +extern struct tpm_chip *tpm_chip_lookup(int chip_num);
> +#else
> +struct tpm_chip {
> +	char dummy;
> +};
> +
> +static inline ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz)
> +{
> +	return 0;
> +}
> +
> +static inline struct tpm_chip *tpm_chip_lookup(int chip_num)
> +{
> +	return NULL;
> +}
> +#endif
> +
> +
> +#endif
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_init.c	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_init.c	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,191 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer      <sailer@watson.ibm.com>
> + *
> + * Contributions:
> + * Leendert van Doorn <leendert@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.	  
> + *
> + * 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
> + *             init functions to start up IBM IMA as LSM
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/linkage.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <asm/uaccess.h>
> +#include <linux/file.h>
> +#include <linux/slab.h>
> +#include <linux/errno.h>
> +#include <linux/crypto.h>
> +#include <linux/fs.h>
> +#include <linux/init.h>
> +#include "ima.h"
> +#include "ima_tpm_pcrread.h"
> +#include "ima_tpm_extend.h"
> +
> +/* name for boot aggregate entry */
> +char *boot_aggregate_name = "boot_aggregate";
> +
> +extern struct h_table htable;
> +
> +/* These identify the driver base version and may not be removed. */
> +static const char version[] = "v4.0 06/15/2005";
> +static const char illegal_pcr[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
> +
> +/* configuration parameters */
> +int ima_test_mode;
> +int skip_boot_aggregate;
> +
> +void create_htable(void);
> +void create_sha_htable(void);
> +void ima_lsm_init(void);
> +void ima_fs_init(void);
> +int ima_add_measure_entry(struct measure_entry *);
> +
> +int ima_enabled = 0;

Wouldn't you want this to default to on?

> +struct tpm_chip *ima_used_chip;
> +
> +static int __init ima_enabled_setup(char *str)
> +{
> +	ima_enabled = simple_strtol(str, NULL, 0);
> +	return 1;
> +}
> +
> +__setup("ima=", ima_enabled_setup);
> +
> +
> +void ima_add_boot_aggregate(void)
> +{
> +	/* cumulative sha1 the first 8 tpm registers */
> +	struct measure_entry *entry;
> +	size_t count;
> +
> +	/* create new entry for boot aggregate */
> +	entry = (struct measure_entry *)
> +	    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
> +	if (entry == NULL) {
> +		invalidate_pcr("error allocating new measurement entry");
> +		return;
> +	}
> +	entry->inode_nr = 0;	/* 0,0 are special (no files) */
> +	entry->dev_id = 0;
> +	entry->measure_flags = 0;
> +	entry->dirty = DIRTY;
> +	entry->super_block = NULL;
> +	memset(entry->digest, 0, 20);
> +	if ((count = strlen(boot_aggregate_name)) > TCG_EVENT_NAME_LEN_MAX)
> +		count = TCG_EVENT_NAME_LEN_MAX;
> +	memcpy(entry->file_name, boot_aggregate_name, count);
> +	entry->file_name[count] = '\0';	/* ez-print */
> +	if (ima_used_chip != NULL) {
> +		int i;
> +		u8 pcr_i[20];
> +		struct crypto_tfm *tfm;
> +		
> +		tfm = crypto_alloc_tfm("sha1", 0);
> +		if (tfm == NULL) {
> +			ima_error("Digest init failed ERROR.\n");

leaks entry

> +			return;
> +		}
> +		crypto_digest_init(tfm);
> +
> +		for (i = 0; i < 8; i++) {
> +			tpm_pcrread(i, pcr_i);
> +			/* now accumulate with current aggregate */
> +			tfm->__crt_alg->cra_digest.
> +			    dia_update(crypto_tfm_ctx(tfm), pcr_i, 20);
> +		}
> +		crypto_digest_final(tfm, entry->digest);
> +		crypto_free_tfm(tfm);
> +	} else
> +		memset(entry->digest, 0xff, 20);
> +
> +	/* now add measurement; if TPM bypassed, we have a ff..ff entry */
> +	if (ima_add_measure_entry(entry) < 0) {
> +		kfree(entry);
> +		invalidate_pcr("error adding boot aggregate");
> +	} else
> +		tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest);
> +}
> +
> +
> +/* general invalidation function called by the measurement code */
> +void invalidate_pcr(char *cause)
> +{
> +	/* extend pcr with illegal digest (no digest yields 0) */
> +	/* extending twice is obviously flagging the exception condition... */
> +	ima_error("INVALIDATING PCR AGGREGATE. Cause=%s.\n", cause);
> +	tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr);
> +	tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, illegal_pcr);
> +	atomic_inc(&htable.violations); /* can overflow into 0; this is an indicator only */
> +}
> +
> +
> +static int __init measure_init(void)
> +{
> +	struct security_operations null_ops;
> +
> +	printk(KERN_INFO "IBM Integrity Measurement Architecture (IBM IMA %s).\n", 
> +	       version);
> +	read_configs();
> +
> +	/* check pre-conditions and dependencies */
> +	if (!ima_test_mode)
> +		ima_enabled = 1;	/* unconditionally */

This can be done statically.

> +	else {
> +		if (!ima_enabled) {
> +			printk(KERN_INFO "    IMA (not enabled in kernel command line) aborting!\n");
> +			return 0;
> +		}
> +		printk(KERN_INFO "    IMA (test mode)\n");
> +	}
> +	ima_used_chip = tpm_chip_lookup(0);
> +	if (ima_used_chip == NULL) {
> +		if (ima_test_mode)
> +			printk(KERN_INFO "    IMA (TPM/BYPASS - no TPM chip found)\n");
> +		else
> +			/* no way to invalidate pcr and inform remote party */
> +			IMA_PANIC("IMA: TPM/no support and IMA not in test mode!\n");
> +	}
> +	/* check for LSM availability */
> +	memset(&null_ops, 0, sizeof(struct security_operations));
> +	if (!register_security(&null_ops))
> +		unregister_security(&null_ops);

Why would you do this?

> +	else {
> +		if (ima_test_mode) {
> +			ima_enabled = 0;
> +			printk(KERN_INFO "    IMA (LSM/not free) aborting!\n");
> +			return -EFAULT;
> +		} else
> +			invalidate_pcr("LSM/not free in real mode!\n");
> +	}
> +	create_htable(); /* for measurements */
> +	create_sha_htable();
> +	/* boot aggregate must be very first entry */
> +	if (!skip_boot_aggregate)
> +		ima_add_boot_aggregate();
> +	ima_lsm_init();
> +	ima_fs_init();
> +	return 0;
> +}
> +
> +__initcall(measure_init);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Reiner Sailer <sailer@watson.ibm.com>");
> +MODULE_DESCRIPTION
> +    ("Run-time LSM-based IBM Integrity Measurement Architecture");
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_lsmhooks.c	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_lsmhooks.c	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,181 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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_lsmhooks.c
> + *             implements Linux Security Modules hooks that call into
> + *             into the measurement functions
> + */
> +#include <linux/config.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <asm/mman.h>
> +#include <linux/fs.h>
> +#include <linux/mount.h>
> +#include <linux/namei.h>
> +#include "ima.h"
> +
> +extern struct h_table htable;
> +
> +/* if set, then hooks do nothing 
> + * (controls non-lsm module hook as well) */
> +unsigned char ima_terminating = 1;

This looks unsafe (hmm, but, then it's not used either).

> +struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t);
> +void measure_mmap_file(struct file *, u32 flags);
> +int measure_dirty_flag_super(struct super_block *);
> +
> +/* measure files mmapped with exec permission */
> +int ima_file_mmap(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags)
> +{
> +	/* filter interesting calls that actually map files executable */
> +	if (!(reqprot & PROT_EXEC))
> +		return 0;
> +
> +	/* now check protection  */
> +	if (reqprot & MAP_SHARED & PROT_EXEC & PROT_WRITE) {
> +		ima_error("MMAP protection flag error!!!\n");
> +		invalidate_pcr("MMAP protection flag violation!");
> +	}
> +	atomic_inc(&htable.kernel_measure);
> +	measure_mmap_file(file, (u32)MMAP_MEASURE_FLAG);
> +	/* IMA is non-intrusive, so we always map */
> +	return 0;
> +}
> +
> +/* dirty flags on open with MAY_WRITE|MAY_APPEND */
> +int ima_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
> +{
> +	struct ima_inode *i_security = NULL;
> +	struct measure_entry *entry;
> +
> +	/* dirty-flagging applies to changing files */
> +	if (!(mask & (MAY_WRITE | MAY_APPEND)) || !inode)
> +		return 0;
> +	
> +	/* general checks against bypassing dirty-flagging */
> +	check_kmem_bypass(inode);
> +	check_mem_bypass(inode);
> +	check_ram_bypass(inode);
> +	check_hd_sd_bypass(inode);
> +	
> +	/* files that are written to are usually not executed (measured),
> +	   optimize this path */
> +	down(&h_table_mutex);

This just serialized most file actions.  It won't scale well.

> +	if ((entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev)) == NULL)
> +		goto out; /* not a measured file */
> +	if (entry->dirty == CLEAN)
> +		entry->dirty = DIRTY;
> +	/* dirty flag inode */
> +	if ((i_security = ima_get_inode_security(inode)) != NULL) {
> +		if (atomic_read(&(i_security->measure_count))) {
> +			/* write permission on measured file was granted! */
> +			invalidate_pcr("ToMToU violation");

Heh, new term on me (vs. tocttou ;-)

> +			ima_error("VIOLATION: Writing to measured file (%s) while it is being used!\n", 
> +				  entry->file_name);	
> +		}
> +		if (i_security->dirty == CLEAN)
> +			i_security->dirty = DIRTY;
> +	}
> + out:
> +	up(&h_table_mutex);
> +	return 0;
> +}
> +
> +/* dirty flag files on an umounted file system */
> +static int ima_sb_umount(struct vfsmount *mnt, int flags)
> +{
> +	/* mark all clean entries with this superblock dirty */
> +	struct queue_entry *qe;
> +	struct super_block *super = mnt->mnt_sb;
> +	int j;
> +
> +	down(&h_table_mutex);
> +	for (j = 0; j < htable.max_htable_size; j++) {
> +		qe = htable.queue[j];
> +		while (qe != NULL) {
> +			if (qe->entry->super_block == super)
> +				if (qe->entry->dirty == CLEAN)
> +					qe->entry->dirty = DIRTY;
> +			qe = qe->next;
> +		}
> +	}
> +	up(&h_table_mutex);
> +	return 0;
> +}
> +
> +/* free security structure if applies */
> +static void ima_inode_free_security(struct inode *inode)
> +{
> +	struct ima_inode *i_security = ima_get_inode_security(inode);
> +
> +	if (i_security) {
> +		kfree(i_security);
> +		ima_store_inode_security(inode, NULL);

It'd make sense to reverse the order (also, both can be done
unconditionally).

> +	}
> +}
> +
> +static void ima_file_free_security(struct file *file)
> +{
> +	struct ima_file *f_security;
> +	struct ima_inode *i_security = NULL;
> +
> +	if ((f_security = ima_get_file_security(file)) == NULL)
> +		return;
> +	/* decrease measure count if file is measured */
> +	i_security = ima_get_inode_security(file->f_dentry->d_inode);
> +	if (i_security && (f_security->is_measuring))
> +			atomic_dec(&(i_security->measure_count));
> +	kfree(f_security);
> +	ima_store_file_security(file, NULL);
> +}
> +
> +/* module stacking operations */
> +int ima_register_security(const char *name, struct security_operations *ops)
> +{
> +	/* no stacking */
> +	return -EFAULT;
> +}
> +
> +int ima_unregister_security(const char *name, struct security_operations *ops)
> +{
> +	/* no stacking */
> +	return -EFAULT;
> +}
> +
> +struct security_operations ima_ops;
> +
> +/* IMA requires early initialization in order measure
> +   all executables etc from the very beginning. */
> +void ima_lsm_init(void)
> +{
> +	/* prepare ima_ops struct */
> +	memset(&ima_ops, 0, sizeof(struct security_operations));
> +	/* set the few non-null elements */
> +	ima_ops.file_mmap = ima_file_mmap;
> +	ima_ops.file_free_security = ima_file_free_security;
> +	ima_ops.inode_permission = ima_inode_permission;
> +	ima_ops.inode_free_security = ima_inode_free_security;
> +	ima_ops.sb_umount = ima_sb_umount;
> +	ima_ops.register_security = ima_register_security;
> +	ima_ops.unregister_security = ima_unregister_security;

These should be statically initialized.

> +	/* rest will be taken care of by registration (fixup) */
> +	if (register_security(&ima_ops)) {
> +		invalidate_pcr("IMA: Unable to register with kernel.\n");
> +		return;
> +	}
> +	/* module measurement hook becomes hot */
> +	ima_terminating = 0;
> +}
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_main.c	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_main.c	2005-06-14 21:58:13.000000000 -0400
> @@ -0,0 +1,462 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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 run-time measurements
> + */
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/linkage.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <asm/uaccess.h>
> +#include <asm/atomic.h>
> +#include <linux/file.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +
> +#include "ima.h"
> +#include "ima_tpm_extend.h"
> +
> +extern struct h_table htable;
> +extern struct sha_table sha_htable;
> +
> +struct sha_entry *ima_lookup_sha_entry(u8 * sha_value);
> +struct measure_entry *ima_lookup_measure_entry(unsigned long, dev_t);
> +int ima_add_measure_entry(struct measure_entry *);
> +int measure_dirty_flag_inode(struct inode *);
> +extern struct tpm_chip *ima_used_chip;
> +
> +DEFINE_SPINLOCK(ima_measure_file_lock);
> +
> +/* 
> + * Returns the dirty flag setting for an inode
> + * (nfs, since we don't control changes)
> + */
> +static inline ima_entry_flags get_default_dirty_setting(struct inode *inode)
> +{
> +	switch (inode->i_sb->s_magic) {
> +	case NFS_SUPER_MAGIC:
> +		return DIRTY;	/* dirty */
> +		break;
> +	default:		/* local fs etc. */
> +		return CLEAN;	/* clean */
> +	}
> +}
> +
> +/* returns >0 if measurement must be skipped
> + * returns =0 if measurement allowed 
> + */
> +static inline int skip_measurement(struct inode *inode)
> +{
> +	/* measuring only regular files; can't measure IMA files */
> +	if (S_ISREG(inode->i_mode) && (inode->i_sb->s_magic != IMA_MAGIC))
> +		return 0;       	/* measure */
> +	else
> +		return 1;       	/* skip */	
> +}
> +
> +
> +/*  measures new file and adds it to measurement list */
> +static struct measure_entry *do_measure_file(struct file *file, struct inode *inode)
> +{
> +	struct ima_inode *i_security = NULL;
> +	mm_segment_t oldfs;
> +	int error = 0;
> +	loff_t offset = 0;
> +	size_t count;
> +	struct crypto_tfm *tfm;
> +	struct measure_entry *entry;
> +
> +	char *bufp = NULL;
> +	/* create read buffer */
> +	if ((bufp =
> +	     (char *) kmalloc(PAGE_SIZE, GFP_KERNEL)) == 0) {
> +		ima_error("no memory for read buffer\n");
> +		error = -ENOMEM;
> +		goto out;	/* invalidate pcr */
> +	}
> +	/* create new entry and measure */
> +	entry = (struct measure_entry *)
> +	    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
> +	if (entry == NULL) {
> +		error = -ENOMEM;
> +		ima_error("error allocating new measurement entry");
> +		kfree(bufp);
> +		goto out;	/* invalidate pcr */
> +	}
> +	entry->inode_nr = inode->i_ino;
> +	entry->dev_id = inode->i_rdev;
> +	entry->dirty = get_default_dirty_setting(inode);
> +	entry->super_block = inode->i_sb;
> +	if ((count = file->f_dentry->d_name.len) > TCG_EVENT_NAME_LEN_MAX)
> +		count = TCG_EVENT_NAME_LEN_MAX;
> +	memcpy(entry->file_name, file->f_dentry->d_name.name, count);
> +	entry->file_name[count] = '\0';	/* ez-print */
> +	error = 0;
> +	/* second add sha1 over file contents */
> +	/* init context */
> +	tfm = crypto_alloc_tfm("sha1", 0);
> +	if (tfm == NULL) {
> +		ima_error("Digest init failed ERROR.\n");
> +		goto outm;
> +	}
> +	crypto_digest_init(tfm);
> +
> +	/* set fs so that kernel writes into kernel segment */
> +	oldfs = get_fs();
> +	set_fs(KERNEL_DS);
> +	do {
> +		if ((count =
> +		     (file->f_op->read) (file,
> +					 (char __user *) bufp,
> +					 PAGE_SIZE,
> +					 &offset)) < 0) {
> +			error = count;
> +			ima_error("Error reading from file (%d)\n", error);
> +			goto outf;
> +		}
> +		/* update hash with this part */
> +		tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm),
> +						      bufp, count);
> +	} while (count);
> +	set_fs(oldfs);
> +
> +	/* complete hash */
> +	crypto_digest_final(tfm, entry->digest);
> +	crypto_free_tfm(tfm);
> +	/* before returning, replicate important information into inode->i_security */
> +	i_security = ima_get_inode_security(inode);
> +	if (i_security != NULL) {
> +		/* update */
> +		i_security->dirty = entry->dirty;
> +	} else {
> +		ima_error("error No security structure in measure!\n");
> +		goto outm;
> +	}
> +
> +	kfree(bufp);
> +	return (entry);
> +
> +	/* error exits */
> +      outf:
> +	set_fs(oldfs);
> +      outm:
> +	kfree(entry);
> +	kfree(bufp);
> +      out:
> +	return (NULL);
> +}
> +
> +/* measure memory (kernel module; still the exact copy of the object file) */
> +int do_measure_memory(void *start, unsigned long len, u32 measure_flags, char *name)
> +{
> +	struct crypto_tfm *tfm;
> +	u8 mem_digest[20];
> +	int error = 0;
> +	struct measure_entry *entry;
> +	int count;
> +
> +	/* init context */
> +	tfm = crypto_alloc_tfm("sha1", 0);
> +	if (tfm == NULL) {
> +		invalidate_pcr("No SHA1 available");
> +		return -EFAULT;
> +	}
> +	crypto_digest_init(tfm);
> +	/* now measure the memory ... */
> +	tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm), start,
> +					      len);
> +	crypto_digest_final(tfm, mem_digest);
> +	crypto_free_tfm(tfm);
> +
> +	down(&h_table_mutex);
> +	if (!ima_lookup_sha_entry(mem_digest)) {
> +		/* create new entry and measure */
> +		entry = (struct measure_entry *)
> +		    kmalloc(sizeof(struct measure_entry), GFP_KERNEL);
> +		if (entry == NULL) {
> +			invalidate_pcr("OUT OF MEMORY");
> +			error = -EFAULT;
> +			goto out;
> +		}
> +		entry->inode_nr = 0;	/* special entries, no file entries */
> +		entry->dev_id = 0;
> +		entry->dirty = DIRTY;
> +		entry->super_block = NULL;
> +		memcpy(entry->digest, mem_digest, 20);
> +		if ((count = strlen(name)) > TCG_EVENT_NAME_LEN_MAX)
> +			count = TCG_EVENT_NAME_LEN_MAX;
> +		strncpy(entry->file_name, name, count);
> +		entry->file_name[count] = '\0';
> +		entry->measure_flags = measure_flags;
> +		if ((error = ima_add_measure_entry(entry)) < 0) {
> +			kfree(entry);
> +			invalidate_pcr
> +			    ("error adding new measurement entry");;

Too many semi-colons ;-)

> +			goto out;
> +		} else {	/* extend PCR */
> +			tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, entry->digest);
> +		}
> +	}			/* else we already have this hash value from an exec/file that was running earlier */
> +	up(&h_table_mutex);
> +	return 0;
> +      out:
> +	up(&h_table_mutex);
> +	return -EFAULT;
> +}
> +
> +static unsigned int find_mod_sec(Elf_Ehdr * hdr, Elf_Shdr * sechdrs, const char *secstrings, const char *name)
> +{
> +	unsigned int i;
> +	for (i = 1; i < hdr->e_shnum; i++)
> +		/* Alloc bit cleared means "here is nothing to look for (ignore)" */
> +		if ((sechdrs[i].sh_flags & SHF_ALLOC)
> +		    && strcmp(secstrings + sechdrs[i].sh_name, name) == 0)
> +			return i;
> +	return 0;
> +}
> +
> +/* Measure kernel modules in-memory before relocation */
> +void measure_kernel_module(void *start, unsigned long len, const char __user * uargs)
> +{
> +	Elf_Ehdr *hdr;
> +	Elf_Shdr *sechdrs;
> +	struct module *mod;
> +	unsigned int modindex;
> +	char *args, *secstrings;
> +	long arglen;
> +
> +	arglen = strlen_user(uargs);
> +	if (!arglen) {
> +		invalidate_pcr("ERROR measuring kernel module!");
> +		return;
> +	}
> +	args = kmalloc(arglen, GFP_KERNEL);
> +	if (!args) {
> +		invalidate_pcr("OUT OF MEMORY measuring kernel module!");
> +		return;
> +	}
> +	if (copy_from_user(args, uargs, arglen) != 0) {
> +		invalidate_pcr("ERROR measuring kernel module!");
> +		return;

leaks args

> +	}
> +	/* get the module name for entry */
> +	hdr = (Elf_Ehdr *) start;
> +	sechdrs = (void *) hdr + hdr->e_shoff;
> +	secstrings = (void *) hdr + sechdrs[hdr->e_shstrndx].sh_offset;
> +
> +	modindex = find_mod_sec(hdr, sechdrs, secstrings,
> +				".gnu.linkonce.this_module");

You can't search by section type?  Really, we could use some common elf
handling...wait, this is in kernel/module.c alrready.  Don't copy and
paste this kind of stuff.  We already have enough headaches with elf
parsing (albeit typically on exec).  And, why do you care about the
name?

> +	if (!modindex) {
> +		ima_error("No module found in object\n");
> +		invalidate_pcr("Module without name?!");
> +		return;

leaks args

> +	}
> +	mod = (void *) ((size_t) hdr + sechdrs[modindex].sh_offset);
> +	atomic_inc(&htable.kernel_measure); /* CHECK */
> +	do_measure_memory(start, len, (u32)MODULE_MEASURE_FLAG, mod->name);

leaks args, and you never used args.

> +	return;
> +}
> +
> +
> +
> +static void measure_file (struct file *file, u32 measure_flags, struct inode *inode, struct ima_inode *i_security)
> +{
> +	struct measure_entry *entry, *new_entry;
> +
> +	down(&h_table_mutex);
> +	entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev);
> +	if ((entry != NULL) && (entry->dirty == CLEAN)) {
> +		i_security->dirty = CLEAN;
> +		atomic_inc(&htable.clean_table_hits);
> +		goto out; /* done */
> +	}
> +	new_entry = do_measure_file(file, inode);
> +	/* now we adjust the entry table:
> +	 * -- if there was no entry, we just add the new one
> +	 * -- if there was one but different hash, we add the new one
> +	 * -- if there was one and same hash, we clear dirty bit on existing one
> +	 */
> +	if (!new_entry) {
> +		/* internal error, make sure attestation fails from now on */
> +		invalidate_pcr("error measuring file");
> +		goto out;
> +	}
> +	new_entry->measure_flags = measure_flags;
> +	if (entry == NULL) {	/* no old entry for this inode found */
> +		/* add if this hash is new (i.e., no copy measured yet) */
> +		if (!ima_lookup_sha_entry(new_entry->digest)) {
> +			if (ima_add_measure_entry(new_entry) < 0) {
> +				kfree(new_entry);
> +				invalidate_pcr("error adding measurement entry");
> +			} else	
> +				tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest);
> +		}
> +		goto out;
> +	}
> +	/* old entry exists (!= clean) */
> +	if (!memcmp(entry->digest, new_entry->digest, 20)) {
> +		/* set with default (no clean-flag for nfs) */
> +		entry->dirty = get_default_dirty_setting(inode);
> +		i_security->dirty = entry->dirty;
> +		atomic_inc(&htable.dirty_table_hits);
> +		kfree(new_entry);
> +	} else {
> +		/* dirty and look whether to add new entry */
> +		entry->dirty = CHANGED;
> +		atomic_inc(&htable.changed_files);
> +		if (!ima_lookup_sha_entry(new_entry->digest)) {
> +			if (ima_add_measure_entry(new_entry) < 0) {
> +				kfree(new_entry);
> +				invalidate_pcr("error adding measurement entry");
> +			} else
> +				tpm_extend(CONFIG_IMA_MEASURE_PCR_IDX, new_entry->digest);
> +		}
> +	}
> + out:
> +	up(&h_table_mutex);
> +}
> +
> +
> +/* Measure user space file descriptor, protect file from being
> + * written until all measureing processes have closed the file
> + */
> +int measure_user_file(struct file *file, u32 measure_flags)
> +{
> +	struct inode *inode;
> +	struct ima_file *f_security = NULL;
> +	struct ima_inode *i_security = NULL;
> +
> +	if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode)
> +		return -EACCES;
> +
> +	inode = file->f_dentry->d_inode;
> +
> +	/* here we skip unnecessary measurements */
> +	if (skip_measurement(inode))
> +		return -EACCES; /* not allowed to measure; user apps to handle error */
> + 
> +	/* a) if there is already a writer on this file --> error! */
> +	if (atomic_read(&(inode->i_writecount)) > 0) {
> +		struct measure_entry *entry;
> +		invalidate_pcr("ToMToU violation");
> +		down(&h_table_mutex);
> +		entry = ima_lookup_measure_entry(inode->i_ino, inode->i_rdev);
> +		ima_error("VIOLATION: Measured file (%s) has writers!\n",
> +			  (entry != NULL) ? entry->file_name : "most likely measuring file opened rw");
> +		up(&h_table_mutex);
> +		return -EACCES;
> +	}
> +	inode = file->f_dentry->d_inode;
> +	/* mark this file as measuring (increases measurement-refcount on inode) */
> +	if ((f_security = ima_get_file_security(file)) != NULL) {
> +		i_security = ima_get_inode_security(file->f_dentry->d_inode);
> +		if (i_security == NULL) {
> +			invalidate_pcr("Internal error (f_security not free but no i_security).\n");
> +			return -EFAULT;
> +		}
> +	} else {
> +		/* create f_security and if necessary i_security */
> +		f_security = kmalloc(sizeof(struct ima_file), GFP_KERNEL);
> +		if (f_security == NULL) {
> +			invalidate_pcr("out of memory error");
> +			return -ENOMEM;
> +		} else {
> +			f_security->is_measuring = 1;
> +			ima_store_file_security(file, f_security);
> +		}
> +		/* we maintain an inode copy of clean etc. to speed up clean hits */
> +		i_security = ima_get_inode_security(inode);
> +		if (i_security != NULL)
> +			atomic_inc(&(i_security->measure_count));
> +		else {
> +			spin_lock(&ima_measure_file_lock);
> +			if ((i_security = ima_get_inode_security(inode)))
> +				goto dontalloc;
> +			i_security =
> +				kmalloc(sizeof(struct ima_inode), GFP_KERNEL);
> +			if (i_security == NULL) {
> +				spin_unlock(&ima_measure_file_lock);
> +				invalidate_pcr("out of memory error");
> +				return -EFAULT;
> +			} else {
> +				i_security->dirty = DIRTY;
> +				/* is reset later after measuring file */
> +				atomic_set(&(i_security->measure_count), 1);
> +				ima_store_inode_security(inode, i_security);
> +			}
> +dontalloc:
> +			spin_unlock(&ima_measure_file_lock);	
> +		}
> +	}
> +	/* catch most cases */
> +	if (i_security->dirty == CLEAN)
> +		atomic_inc(&htable.clean_inode_hits);
> +	else
> +		measure_file(file, measure_flags, inode, i_security);
> +	return 0;
> +}
> +
> +
> +/* Measure files mapped as executable */
> +void measure_mmap_file(struct file *file, u32 measure_flags)
> +{
> +	struct inode *inode;
> +	struct ima_inode *i_security = NULL;
> +
> +	if (!file || !file->f_op || !file->f_dentry || !file->f_dentry->d_inode)
> +		return;
> +	
> +	inode = file->f_dentry->d_inode;
> +
> +	/* here we skip non-allowed measurements */
> +	if (skip_measurement(inode))
> +		return;
> +
> +	/* if there is already a writer on this file --> error! */
> +	if (atomic_read(&(inode->i_writecount)) > 0) {
> +		invalidate_pcr("Measured file has writers.");
> +		return;
> +	}
> +	/* we maintain an inode copy of clean etc. to speed up clean hits */
> +	i_security = ima_get_inode_security(inode);
> +	if (!i_security) {
> +		spin_lock(&ima_measure_file_lock);
> +		if ((i_security = ima_get_inode_security(inode)))
> +			goto dontalloc;
> +		i_security = kmalloc(sizeof(struct ima_inode), GFP_KERNEL);
> +		if (i_security == NULL) {
> +			spin_unlock(&ima_measure_file_lock);
> +			invalidate_pcr("out of memory error");
> +			return;
> +		} else {
> +			i_security->dirty = DIRTY;
> +			/* is reset later after measuring file */
> +			atomic_set(&(i_security->measure_count), 0);
> +			ima_store_inode_security(inode, i_security);
> +		}
> +dontalloc:
> +		spin_unlock(&ima_measure_file_lock);
> +	}
> +	/* catch most cases */
> +	if (i_security->dirty == CLEAN)
> +		atomic_inc(&htable.clean_inode_hits);
> +	else
> +		measure_file(file, measure_flags, inode, i_security);
> +}
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_queue.c	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_queue.c	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,185 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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 for run-time measurement
> + *             functions based on SHA1
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/linkage.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +#include <linux/fcntl.h>
> +#include <asm/uaccess.h>
> +#include <linux/file.h>
> +#include <linux/slab.h>
> +#include <linux/crypto.h>
> +
> +#include "ima.h"
> +
> +/* pointer to very first and latest measurement (time-ordered) */
> +struct queue_entry *first_measurement = NULL;	/* for printing */
> +struct queue_entry *latest_measurement = NULL;	/* for adding */
> +
> +struct h_table htable;		/* key: inode (before secure-hashing a file) */
> +struct sha_table sha_htable;	/* key: hash (after secure-hashing a file) */
> +int ima_add_sha_entry(struct measure_entry *);
> +
> +DECLARE_MUTEX_LOCKED(h_table_mutex);

locking looks broken.  or not really used...ah, guess it's used in other
file.

> +void create_sha_htable(void)
> +{
> +	int i;
> +
> +	atomic_set(&sha_htable.len, 0);
> +	sha_htable.max_htable_size = MEASURE_HTABLE_SIZE;
> +	for (i = 0; i < sha_htable.max_htable_size; i++) {
> +		sha_htable.queue[i] = NULL;
> +		atomic_set(&sha_htable.queue_len[i], 0);
> +	}
> +}
> +
> +void create_htable(void)
> +{
> +	int i;
> +
> +	init_MUTEX_LOCKED(&h_table_mutex);
> +	first_measurement = NULL;
> +	latest_measurement = NULL;
> +	atomic_set(&htable.len, 0);
> +	atomic_set(&htable.user_measure, 0);
> +	atomic_set(&htable.kernel_measure, 0);
> +	atomic_set(&htable.clean_inode_hits, 0);
> +	atomic_set(&htable.clean_table_hits, 0);
> +	atomic_set(&htable.dirty_table_hits, 0);
> +	atomic_set(&htable.changed_files, 0);
> +	atomic_set(&htable.violations, 0);
> +	htable.max_htable_size = MEASURE_HTABLE_SIZE;
> +	for (i = 0; i < htable.max_htable_size; i++) {
> +		htable.queue[i] = NULL;
> +		atomic_set(&htable.queue_len[i], 0);
> +	}
> +	up(&h_table_mutex);
> +}
> +
> +/* 
> + * also sets clean and dirty table hit marks 
> + */
> +struct measure_entry *ima_lookup_measure_entry(unsigned long inode_number, dev_t dev_number)
> +{
> +	struct queue_entry *qe;
> +	struct measure_entry *me;
> +
> +	/* fill in later */
> +	qe = htable.queue[HASH_KEY(inode_number)];
> +	while ((qe != NULL) && ((qe->entry->inode_nr != inode_number)
> +				|| (qe->entry->dev_id != dev_number)))
> +		qe = qe->next;
> +
> +	if (qe != NULL) {
> +		if (qe->entry->dirty != CLEAN)
> +			atomic_inc(&htable.dirty_table_hits);
> +		else
> +			atomic_inc(&htable.clean_table_hits);
> +
> +		me = qe->entry;
> +	} else {
> +		me = NULL;
> +	}
> +	return me;
> +}
> +
> +
> +
> +struct sha_entry *ima_lookup_sha_entry(u8 * sha_value)
> +{
> +	struct sha_entry *se;
> +	unsigned int key;
> +
> +	key = SHA_KEY(sha_value);
> +	se = sha_htable.queue[key];
> +	while ((se != NULL) && (memcmp(se->digest, sha_value, 20)))
> +		se = se->next;
> +	return se;
> +}
> +
> +
> +int ima_add_measure_entry(struct measure_entry *entry)
> +{
> +	unsigned int key;
> +	struct queue_entry *qe;
> +	int error = 0;
> +
> +	/* calculate key */
> +	key = HASH_KEY(entry->inode_nr);
> +
> +	/* create queue_entry */
> +	if ((qe = kmalloc(sizeof(struct queue_entry), GFP_KERNEL)) == NULL) {
> +		ima_error("OUT OF MEMORY in %s.\n", __func__);
> +		error = -ENOMEM;
> +		goto out;
> +	}
> +	qe->entry = entry;
> +
> +	/* insert entry at beginning of queue */
> +	qe->next = htable.queue[key];
> +	qe->later = NULL;
> +	htable.queue[key] = qe;
> +	atomic_inc(&htable.queue_len[key]);
> +	/* update later list */
> +	if (first_measurement == NULL)
> +		first_measurement = qe;
> +	else
> +		latest_measurement->later = qe;
> +	
> +	latest_measurement = qe;
> +	atomic_inc(&htable.len);
> +	/* now add to sha hash table, too */
> +	if (ima_add_sha_entry(entry))
> +		error = -ENOMEM;
> +      out:
> +	return error;
> +}
> +
> +
> +
> +int ima_add_sha_entry(struct measure_entry *entry)
> +{
> +	unsigned int key;
> +	struct sha_entry *se;
> +
> +	/* calculate key */
> +	key = SHA_KEY(entry->digest);
> +	/* create queue_entry */
> +	if ((se = kmalloc(sizeof(struct sha_entry), GFP_KERNEL)) == NULL)
> +		goto out;
> +	se->m_entry = entry;
> +	se->digest = entry->digest;
> +	se->next = NULL;
> +
> +	/* insert entry at beginning of queue */
> +	se->next = sha_htable.queue[key];
> +	sha_htable.queue[key] = se;
> +	atomic_inc(&sha_htable.queue_len[key]);
> +	/* update later list */
> +	atomic_inc(&sha_htable.len);
> +	return 0;
> +
> +      out:
> +	ima_error("OUT OF MEMORY ERROR creating queue entry.\n");
> +	return -ENOMEM;
> +}
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_extend.h	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_extend.h	2005-06-14 20:32:39.000000000 -0400
> @@ -0,0 +1,65 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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_tpm_extend.h
> + *             implements glue code to connect IMA to the TPM driver
> + *             (glues to tpmdd on www.sourceforge.net/tpmdd)
> + */
> +#ifndef __LINUX_IMA_TPM_EXTEND_H
> +#define __LINUX_IMA_TPM_EXTEND_H
> +
> +#define TPM_BUFSIZE 2048
> +
> +extern struct tpm_chip *ima_used_chip;
> +
> +static const u8 extend[] = {
> +	0, 193,			/* TPM_TAG_RQU_COMMAND */
> +	0, 0, 0, 34,		/* length */
> +	0, 0, 0, 20,		/* TPM_ORD_Extend */
> +	0, 0, 0, 0		/* PCR index */
> +};

This a tpm command, why aren't they wrapped and exported directly
from tpm core?

> +static void tpm_extend(int index, const u8 * digest)
> +{
> +	u8 *data;
> +	u32 i;
> +	int len;
> +
> +	if (ima_used_chip == NULL)
> +		return;
> +
> +	if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL)
> +		goto error;
> +
> +	memcpy(data, extend, sizeof(extend));
> +	i = cpu_to_be32(index);
> +	memcpy(data+10, &i, 4);
> +	memcpy(data + 14, digest, 20);
> +	if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) {
> +		memcpy(&i, data + 6, 4); /* return code */
> +		if (be32_to_cpu(i) == 0)
> +			goto out; /* ok */
> +	}
> + error:
> +	if (!ima_test_mode)
> +		IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n");
> +	else
> +		ima_error("Error Communicating to TPM chip\n");
> + out:
> +	if (data != NULL)
> +		kfree(data);
> +}
> +
> +#endif
> diff -uprN linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h
> --- linux-2.6.12-rc6-mm1_orig/security/ima/ima_tpm_pcrread.h	1969-12-31 19:00:00.000000000 -0500
> +++ linux-2.6.12-rc6-mm1-ima/security/ima/ima_tpm_pcrread.h	2005-06-14 16:25:05.000000000 -0400
> @@ -0,0 +1,67 @@
> +/*
> + * Copyright (C) 2005 IBM Corporation
> + *
> + * Authors:
> + * Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * Maintained by: Reiner Sailer <sailer@watson.ibm.com>
> + *
> + * LSM IBM Integrity Measurement Architecture.		  
> + *
> + * 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_tpm_pcrread.h
> + *             implements glue code to connect IMA to the TPM driver
> + *             (glues to tpmdd on www.sourceforge.net/tpmdd)
> + */
> +#ifndef __LINUX_IMA_TPM_PCRREAD_H
> +#define __LINUX_IMA_TPM_PCRREAD_H
> +
> +#define TPM_BUFSIZE 2048
> +
> +extern struct tpm_chip *ima_used_chip;
> +
> +static const u8 pcrread[] = {
> +	0, 193,			/* TPM_TAG_RQU_COMMAND */
> +	0, 0, 0, 14,		/* length */
> +	0, 0, 0, 21,		/* TPM_ORD_PcrRead */
> +	0, 0, 0, 0		/* PCR index */
> +};

Same here, why doesn't tpm core provide this?

> +static void tpm_pcrread(int index, u8 * hash)
> +{
> +	u8 *data;
> +	u32 i;
> +	ssize_t len;
> +
> +	if (ima_used_chip == NULL)
> +		return;
> +
> +	if ((data = kmalloc(TPM_BUFSIZE, GFP_KERNEL)) == NULL)
> +		goto error;
> +
> +	memcpy(data, pcrread, sizeof(pcrread));
> +	i = cpu_to_be32(index);
> +	memcpy(data+10, &i, 4);
> +	if ((len = tpm_transmit(ima_used_chip, data, TPM_BUFSIZE)) >= 30) {
> +		memcpy(&i, data + 6, 4); /* return code */
> +		if (be32_to_cpu(i) == 0) {
> +			memcpy(hash, data + 10, 20);
> +			goto out; /* ok */
> +		}
> +	}
> + error:
> +	if (!ima_test_mode)
> +		IMA_PANIC("IMA: Error Communicating to TPM chip and IMA not in test mode!\n");
> +	else
> +		ima_error("Error Communicating to TPM chip\n");
> + out:
> +	if (data != NULL)
> +		kfree(data);
> +}
> +
> +#endif

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
  2005-06-15 22:44 Reiner Sailer
@ 2005-06-15 22:59 ` Chris Wright
  0 siblings, 0 replies; 18+ messages in thread
From: Chris Wright @ 2005-06-15 22:59 UTC (permalink / raw)
  To: Reiner Sailer
  Cc: Chris Wright, serue, James Morris, Reiner Sailer, LKML, LSM,
	Toml, Greg KH, Emilyr, kylene

* Reiner Sailer (sailer@us.ibm.com) wrote:
> Access control is a very broad term. Before I go into details, I would 
> like to make clear that I do not have a preference for or against LSM. We 
> are working hard to make the functionality available and it does not 
> matter to the user where IMA will be located. The true potential of 
> Trusted Computing will only show with experimenting going on outside 
> the research labs. IMA can help by being one modest building block 
> for experiments only if it is broadly available.

Yeah, understood.

> Regarding the access control discussion, one can map (almost) anything 
> onto access control. There are (many) people that teach today that the 
> whole security issue is about access control. The question is: 
> controlling access of whom to what?

OK, let's look at it another way.  Say your access control model used
kernel profiling data as part of policy.  It still makes sense to let
oprofile do that collection, and the LSM is just a consumer of that
data when it makes an acces control decision.  Perhaps a klunky analogy,
but do you see the idea?

> IMA does control access by forcing measurements on executables
> before they are loaded. Access control is more than saying yes or no at 
> some point on the code path. IMA enables remote parties to figure out 
> whether a system has some (usage dependent) properties. This can serve as 
> the basis for controlling such systems' access to resources. IMA supplies 
> input into a remote Access Control Decision Function.

Right, the measurement data collection stand alone, which no access
control decisions in sight (talking about the IMA LSM now), is what
tipped the scale.

thanks,
-chris

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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
@ 2005-06-15 22:48 Reiner Sailer
  0 siblings, 0 replies; 18+ messages in thread
From: Reiner Sailer @ 2005-06-15 22:48 UTC (permalink / raw)
  To: serue
  Cc: Stephen Smalley, James Morris, Toml, Greg KH, LKML, LSM,
	Chris Wright, Reiner Sailer, Emilyr, Kylene


Serge E. Hallyn (serue@us.ibm.com) wrote:
>
> That's true, of course.  Reiner, would any of the integrity measurement
> hooks be moved to a better place than the current LSM hooks?  Is there a
> preferred ordering - ie measurement should always happen before the LSM
> modules, or always after?  Either of these would of course be clear
> reasons to separate IMA into its own subsystem.
> 
> thanks,
> -serge

Originally, IMA was not an LSM. However, when moving to a 2.6 kernel, we 
moved it to LSM and have found very easily the current hooks. I don't think 
the hook position would change when moving from LSM to non-LSM.

Thanks
Reiner


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

* Re: [PATCH] 3 of 5 IMA: LSM-based measurement code
@ 2005-06-15 22:44 Reiner Sailer
  2005-06-15 22:59 ` Chris Wright
  0 siblings, 1 reply; 18+ messages in thread
From: Reiner Sailer @ 2005-06-15 22:44 UTC (permalink / raw)
  To: Chris Wright
  Cc: serue, James Morris, Reiner Sailer, LKML, LSM, Toml, Greg KH,
	Emilyr, kylene


Chris Wright <chrisw@osdl.org> wrote on 06/15/2005 05:53:01 PM:

> * serue@us.ibm.com (serue@us.ibm.com) wrote:
> > Quoting Chris Wright (chrisw@osdl.org):
> > > The primary purpose of the hooks is access control.  Some of them, of
> > > course, are helpers to keep labels coherent.  IIRC, James objected
> > > because the measurement data was simply collected from these hooks.
> > 
> > Ok, so to be clear, any module which does not directly impose some form
> > of access control is not eligible for an LSM?
> 
> That's exactly the intention, yes.

Chris,

Access control is a very broad term. Before I go into details, I would 
like to make clear that I do not have a preference for or against LSM. We 
are working hard to make the functionality available and it does not 
matter to the user where IMA will be located. The true potential of 
Trusted Computing will only show with experimenting going on outside 
the research labs. IMA can help by being one modest building block 
for experiments only if it is broadly available.

Regarding the access control discussion, one can map (almost) anything 
onto access control. There are (many) people that teach today that the 
whole security issue is about access control. The question is: 
controlling access of whom to what?

IMA does control access by forcing measurements on executables
before they are loaded. Access control is more than saying yes or no at 
some point on the code path. IMA enables remote parties to figure out 
whether a system has some (usage dependent) properties. This can serve as 
the basis for controlling such systems' access to resources. IMA supplies 
input into a remote Access Control Decision Function.

These properties neither justify IMA to be excluded as LSM, nor force IMA 
to be an LSM.


> thanks,
> -chris

Thanks 
Reiner


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

end of thread, other threads:[~2005-06-16  2:03 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-06-15 14:40 [PATCH] 3 of 5 IMA: LSM-based measurement code Reiner Sailer
2005-06-15 20:02 ` James Morris
2005-06-15 20:49   ` serue
2005-06-15 20:58     ` Stephen Smalley
2005-06-15 21:48       ` serue
2005-06-15 20:59     ` Chris Wright
2005-06-15 21:50       ` serue
2005-06-15 21:53         ` Chris Wright
2005-06-15 22:42           ` Serge E. Hallyn
2005-06-15 22:49             ` Chris Wright
2005-06-15 22:00         ` Casey Schaufler
2005-06-15 22:38           ` Serge E. Hallyn
2005-06-15 22:40             ` Chris Wright
2005-06-15 22:52               ` Serge E. Hallyn
2005-06-16  2:01 ` Chris Wright
2005-06-15 22:44 Reiner Sailer
2005-06-15 22:59 ` Chris Wright
2005-06-15 22:48 Reiner Sailer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).