[PATCH 4/6] integrity: IMA display

From: Mimi Zohar
Date: Tue Dec 02 2008 - 16:49:36 EST


Make the measurement lists available through securityfs.

Signed-off-by: Mimi Zohar <zohar@xxxxxxxxxx>
---
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index d9d4405..959ae66 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -5,5 +5,5 @@

obj-$(CONFIG_IMA) += ima.o

-ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_iint.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b727a5e..795b552 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -64,6 +64,9 @@ extern const struct template_operations ima_template_ops;

/* Internal IMA function definitions */
int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
void ima_create_htable(void);
int ima_add_template_entry(struct ima_template_entry *entry, int violation);
int ima_calc_hash(struct file *file, char *digest);
@@ -117,6 +120,8 @@ int ima_collect_measurement(void *d);
int ima_appraise_measurement(void *d);
void ima_store_measurement(void *d);
int ima_store_template(void *d);
+void ima_template_show(struct seq_file *m, void *e,
+ enum integrity_show_type show);

/* For use with ima_must_measure() */
struct ima_measure_data {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 83ab012..f2b53c7 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -21,6 +21,7 @@ const struct template_operations ima_template_ops = {
.collect_measurement = ima_collect_measurement,
.store_measurement = ima_store_measurement,
.store_template = ima_store_template,
+ .display_template = ima_template_show
};

/**
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
new file mode 100644
index 0000000..2627e56
--- /dev/null
+++ b/security/integrity/ima/ima_fs.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@xxxxxxxxxx>
+ * Reiner Sailer <sailer@xxxxxxxxxx>
+ * Mimi Zohar <zohar@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_fs.c
+ * implemenents security file system for reporting
+ * current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+
+#include "../integrity.h"
+#include "ima.h"
+
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+ loff_t *ppos, atomic_long_t *val)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t len;
+
+ len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+ .read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+ .read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+ struct list_head *lpos;
+ loff_t l = *pos;
+ /* we need a lock since pos could point beyond last element */
+ rcu_read_lock();
+ __list_for_each_rcu(lpos, &ima_measurements) {
+ if (!l--) {
+ rcu_read_unlock();
+ return lpos;
+ }
+ }
+ rcu_read_unlock();
+ return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ /* lock protects when reading beyond last element
+ * against concurrent list-extension */
+ struct list_head *lpos = (struct list_head *)v;
+
+ rcu_read_lock();
+ lpos = rcu_dereference(lpos->next);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (lpos == &ima_measurements) ? NULL : lpos;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+/* print format:
+ * 32bit-le=pcr#
+ * char[20]=template digest
+ * 32bit-le=template size
+ * 32bit-le=template name size
+ * eventdata[n] = template name
+ *
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct list_head *lpos = v;
+ struct ima_queue_entry *qe;
+ struct ima_template_entry *e;
+ struct ima_inode_measure_entry *entry;
+ struct template_list_entry *template_entry;
+ int templatename_len;
+ int i;
+ u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+ char data[4];
+
+ /* get entry */
+ qe = list_entry(lpos, struct ima_queue_entry, later);
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /*
+ * 1st: PCRIndex
+ * PCR used is always the same (config option) in
+ * little-endian format
+ */
+ memcpy(data, &pcr, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ /* 2nd: template digest */
+ for (i = 0; i < 20; i++)
+ seq_putc(m, e->digest[i]);
+
+ /* 3rd: template name size */
+ templatename_len = strlen(e->template_name);
+ if (templatename_len > IMA_EVENT_NAME_LEN_MAX)
+ templatename_len = IMA_EVENT_NAME_LEN_MAX;
+
+ memcpy(data, &templatename_len, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+
+ /* 4th: template name */
+ for (i = 0; i < templatename_len; i++)
+ seq_putc(m, e->template_name[i]);
+
+ /* 5th: template dependent */
+ entry = (struct ima_inode_measure_entry *)e->template;
+ template_entry = integrity_find_get_template(e->template_name);
+ if (template_entry) {
+ const struct template_operations *ops;
+
+ ops = template_entry->template_ops;
+ ops->display_template(m, entry, INTEGRITY_SHOW_BINARY);
+ kref_put(&template_entry->refcount, template_release);
+ } else
+ seq_printf(m, " \n");
+ return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+ .open = ima_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+void ima_template_show(struct seq_file *m, void *e,
+ enum integrity_show_type show)
+{
+ struct ima_inode_measure_entry *entry =
+ (struct ima_inode_measure_entry *)e;
+ int filename_len;
+ char data[4];
+ int i;
+
+ /* Display file digest */
+ for (i = 0; i < 20; i++) {
+ switch (show) {
+ case INTEGRITY_SHOW_ASCII:
+ seq_printf(m, "%02x", entry->digest[i]);
+ break;
+ case INTEGRITY_SHOW_BINARY:
+ seq_putc(m, entry->digest[i]);
+ default:
+ break;
+ }
+ }
+
+ switch (show) {
+ case INTEGRITY_SHOW_ASCII:
+ seq_printf(m, " %s\n", entry->file_name);
+ break;
+ case INTEGRITY_SHOW_BINARY:
+ filename_len = strlen(entry->file_name);
+ if (filename_len > IMA_EVENT_NAME_LEN_MAX)
+ filename_len = IMA_EVENT_NAME_LEN_MAX;
+
+ memcpy(data, &filename_len, 4);
+ for (i = 0; i < 4; i++)
+ seq_putc(m, data[i]);
+ for (i = 0; i < filename_len; i++)
+ seq_putc(m, entry->file_name[i]);
+ default:
+ break;
+ }
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct list_head *lpos = v;
+ struct ima_queue_entry *qe;
+ struct ima_template_entry *e;
+ struct ima_inode_measure_entry *entry;
+ struct template_list_entry *template_entry;
+ int i;
+
+ /* get entry */
+ qe = list_entry(lpos, struct ima_queue_entry, later);
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /* 1st: PCR used (config option) */
+ seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+ /* 2nd: SHA1 template hash */
+ for (i = 0; i < 20; i++)
+ seq_printf(m, "%02x", e->digest[i]);
+
+ /* 3th: template name */
+ seq_printf(m, " %s ", e->template_name);
+
+ /* 4th: filename <= max + \'0' delimiter */
+ entry = (struct ima_inode_measure_entry *)e->template;
+ template_entry = integrity_find_get_template(e->template_name);
+ if (template_entry) {
+ const struct template_operations *ops;
+
+ ops = template_entry->template_ops;
+ ops->display_template(m, entry, INTEGRITY_SHOW_ASCII);
+ kref_put(&template_entry->refcount, template_release);
+ } else
+ seq_printf(m, " \n");
+
+ return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+ .open = ima_ascii_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+
+int ima_fs_init(void)
+{
+ ima_dir = securityfs_create_dir("ima", NULL);
+ if (!ima_dir || IS_ERR(ima_dir))
+ return -1;
+
+ binary_runtime_measurements =
+ securityfs_create_file("binary_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_ops);
+ if (!binary_runtime_measurements || IS_ERR(binary_runtime_measurements))
+ goto out;
+
+ ascii_runtime_measurements =
+ securityfs_create_file("ascii_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_ascii_measurements_ops);
+ if (!ascii_runtime_measurements || IS_ERR(ascii_runtime_measurements))
+ goto out;
+
+ runtime_measurements_count =
+ securityfs_create_file("runtime_measurements_count",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_count_ops);
+ if (!runtime_measurements_count || IS_ERR(runtime_measurements_count))
+ goto out;
+
+ violations =
+ securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+ ima_dir, NULL, &ima_htable_violations_ops);
+ if (!violations || IS_ERR(violations))
+ goto out;
+
+ return 0;
+
+out:
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+ return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+ securityfs_remove(violations);
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 374b368..de78f93 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -86,5 +86,11 @@ int ima_init(void)
ima_create_htable(); /* for measurements */
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
ima_init_policy();
- return 0;
+
+ return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+ ima_fs_cleanup();
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2dcc3b5..8672f86 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -265,6 +265,7 @@ static void __exit cleanup_ima(void)
{
integrity_unregister_template("ima");
unregister_integrity(&ima_integrity_ops);
+ ima_cleanup();
}

late_initcall(init_ima); /* Start IMA after the TPM is available */
--
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/