[PATCH] 3 of 5 IMA: LSM-based measurement code

From: Reiner Sailer (sailer@private)
Date: Wed Jun 15 2005 - 07:40:13 PDT


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@private>
---

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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Contributions:
+ * Leendert van Doorn <leendert@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>");
+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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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@private>
+ *
+ * Maintained by: Reiner Sailer <sailer@private>
+ *
+ * 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



This archive was generated by hypermail 2.1.3 : Wed Jun 15 2005 - 07:38:53 PDT