Skip to contentSkip to conversation

integrity

PATCH In review Mimi Zohar 11 participants 39 messages Dec 2, 2008 – Dec 5, 2008

Subsystems (10)
lore
Share & tools
lore

Mimi Zohar

author
#0 lore raw

cover integrity

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

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

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

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

author
#1 lore raw

PATCH 1/6 integrity: TPM internel kernel interface

This patch adds internal kernel support for:
- reading/extending a pcr value
- looking up the tpm_chip for a given chip number and type

---

drivers/char/tpm/tpm.c +137 -3
index 9c47dc4..17d2849 100644
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2004,2007,2008 IBM Corporation
  *
  * Authors:
  * Leendert van Doorn <[email protected]>
  * Dave Safford <[email protected]>
  * Reiner Sailer <[email protected]>
  * Kylene Hall <[email protected]>
+ * Debora Velarde <[email protected]>
  *
  * Maintained by: <[email protected]>
  *
@@ -28,6 +29,14 @@
 #include <linux/spinlock.h>
 #include <linux/smp_lock.h>
 
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/scatterlist.h>
+#include <linux/rcupdate.h>
+#include <asm/unaligned.h>
 #include "tpm.h"
 
 enum tpm_const {
@@ -50,6 +59,8 @@ enum tpm_duration {
 static LIST_HEAD(tpm_chip_list);
 static DEFINE_SPINLOCK(driver_lock);
 static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
+#define TPM_CHIP_NUM_MASK       0x0000ffff
+#define TPM_CHIP_TYPE_SHIFT     16
 
 /*
  * Array with one entry per ordinal defining the maximum amount
@@ -366,8 +377,7 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
 /*
  * Internal kernel interface to transmit TPM commands
  */
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
-			    size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, char *buf, size_t bufsiz)
 {
 	ssize_t rc;
 	u32 count, ordinal;
@@ -425,6 +435,7 @@ out:
 	mutex_unlock(&chip->tpm_mutex);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm_transmit);
 
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
@@ -717,6 +728,7 @@ ssize_t tpm_show_temp_deactivated(struct device * dev,
 }
 EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
+#define READ_PCR_RESULT_SIZE 30
 static const u8 pcrread[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
 	0, 0, 0, 14,		/* length */
@@ -772,6 +784,128 @@ out:
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
+/*
+ * tpm_chip_lookup - return tpm_chip for given chip number and type
+ *
+ * Must be called with rcu_read_lock.
+ */
+static struct tpm_chip *tpm_chip_lookup(int chip_num, int chip_typ)
+{
+	struct tpm_chip *pos;
+	int rc;
+
+	list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+		rc = (chip_num == TPM_ANY_NUM || pos->dev_num == chip_num)
+		    && (chip_typ == TPM_ANY_TYPE);
+		if (rc)
+			return pos;
+	}
+	return NULL;
+}
+
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to retrieve
+ * @res_buf: 	TPM_PCR value
+ * 		size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf)
+{
+	u8 data[READ_PCR_RESULT_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();
+
+	BUILD_BUG_ON(sizeof(pcrread) > READ_PCR_RESULT_SIZE);
+	memcpy(data, pcrread, sizeof(pcrread));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	if (rc == 0 && res_buf)
+		memcpy(res_buf, data + 10, TPM_DIGEST_SIZE);
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+#define EXTEND_PCR_SIZE 34
+static const u8 pcrextend[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 34,		/* length */
+	0, 0, 0, 20,		/* TPM_ORD_Extend */
+	0, 0, 0, 0		/* PCR index */
+};
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to extend
+ * @hash: 	hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash)
+{
+	u8 data[EXTEND_PCR_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();
+
+	BUILD_BUG_ON(sizeof(pcrextend) > EXTEND_PCR_SIZE);
+	memcpy(data, pcrextend, sizeof(pcrextend));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);
+	memcpy(data + 14, hash, TPM_DIGEST_SIZE);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
 #define  READ_PUBEK_RESULT_SIZE 314
 static const u8 readpubek[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
drivers/char/tpm/tpm.h +3 -1
index 8e30df4..e0ffddb 100644
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2004, 2007, 2008 IBM Corporation
  *
  * Authors:
  * Leendert van Doorn <[email protected]>
@@ -26,6 +26,7 @@
 #include <linux/miscdevice.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/tpm.h>
 
 enum tpm_timeout {
 	TPM_TIMEOUT = 5,	/* msecs */
@@ -128,6 +129,7 @@ extern void tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
 extern void tpm_continue_selftest(struct tpm_chip *);
 extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32);
+ssize_t tpm_transmit(struct tpm_chip *chip, char *buf, size_t bufsiz);
 extern struct tpm_chip* tpm_register_hardware(struct device *,
 				 const struct tpm_vendor_specific *);
 extern int tpm_open(struct inode *, struct file *);
drivers/char/tpm/tpm_tis.c +3 -0
index 717af7a..bc26116 100644
@@ -642,6 +642,9 @@ static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
 
 static struct pnp_driver tis_pnp_driver = {
 	.name = "tpm_tis",
+	.driver = {
+		.owner = THIS_MODULE,
+	},
 	.id_table = tpm_pnp_tbl,
 	.probe = tpm_tis_pnp_init,
 	.suspend = tpm_tis_pnp_suspend,
include/linux/tpm.h new file +49 -0

49 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#2 lore raw

Re: [PATCH 1/6] integrity: TPM internel kernel interface

Quoted 87 lines · Mimi Zohar

This patch adds internal kernel support for:
- reading/extending a pcr value
- looking up the tpm_chip for a given chip number and type

---

drivers/char/tpm/tpm.c +23 -3
index 9c47dc4..17d2849 100644
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2004,2007,2008 IBM Corporation
  *
  * Authors:
  * Leendert van Doorn <[email protected]>
  * Dave Safford <[email protected]>
  * Reiner Sailer <[email protected]>
  * Kylene Hall <[email protected]>
+ * Debora Velarde <[email protected]>
  *
  * Maintained by: <[email protected]>
  *
@@ -28,6 +29,14 @@
 #include <linux/spinlock.h>
 #include <linux/smp_lock.h>
 
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/scatterlist.h>
+#include <linux/rcupdate.h>
+#include <asm/unaligned.h>
 #include "tpm.h"
 
 enum tpm_const {
@@ -50,6 +59,8 @@ enum tpm_duration {
 static LIST_HEAD(tpm_chip_list);
 static DEFINE_SPINLOCK(driver_lock);
 static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
+#define TPM_CHIP_NUM_MASK       0x0000ffff
+#define TPM_CHIP_TYPE_SHIFT     16
 
 /*
  * Array with one entry per ordinal defining the maximum amount
@@ -366,8 +377,7 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
 /*
  * Internal kernel interface to transmit TPM commands
  */
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
-			    size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, char *buf, size_t bufsiz)
 {
 	ssize_t rc;
 	u32 count, ordinal;
@@ -425,6 +435,7 @@ out:
 	mutex_unlock(&chip->tpm_mutex);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm_transmit);
 
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
@@ -717,6 +728,7 @@ ssize_t tpm_show_temp_deactivated(struct device * dev,
 }
 EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
+#define READ_PCR_RESULT_SIZE 30
 static const u8 pcrread[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
 	0, 0, 0, 14,		/* length */
@@ -772,6 +784,128 @@ out:
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
+/*
+ * tpm_chip_lookup - return tpm_chip for given chip number and type
+ *
+ * Must be called with rcu_read_lock.
+ */
+static struct tpm_chip *tpm_chip_lookup(int chip_num, int chip_typ)
+{
+	struct tpm_chip *pos;

drivers/char/tpm/tpm.c out: · line 804

Quoted 10 lines
drivers/char/tpm/tpm.c +10 -0
+	int rc;
+
+	list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+		rc = (chip_num == TPM_ANY_NUM || pos->dev_num == chip_num)
+		    && (chip_typ == TPM_ANY_TYPE);
+		if (rc)
+			return pos;
+	}
+	return NULL;
+}

If you have to respin these patches could you consider simplifying that loop? I find that really hard to read. I think it's much easier to read if written out something like this:

	/* Dunno why they *must* specify TPM_ANY_TYPE, but they do */
	if (chip_typ != TPM_ANY_TYPE)
		continue;

	if (chip_num == TPM_ANY_NUM)
		return pos;
	if (pos->dev_num == chip_num)
		return pos;
Quoted 23 lines
drivers/char/tpm/tpm.c +23 -0
+
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to retrieve
+ * @res_buf: 	TPM_PCR value
+ * 		size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf)
+{
+	u8 data[READ_PCR_RESULT_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();

drivers/char/tpm/tpm.c out: · line 837

Quoted 10 lines
drivers/char/tpm/tpm.c +10 -0
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();

This little bit of lookup, check for NULL, and try_module_get() looks cut-n-pasted in the next two functions. Should be consolidated.

Also, if you need to shift down the chip_id every time anyway, why not just do it inside the lookup function?

Quoted 50 lines
drivers/char/tpm/tpm.c +50 -0
+	BUILD_BUG_ON(sizeof(pcrread) > READ_PCR_RESULT_SIZE);
+	memcpy(data, pcrread, sizeof(pcrread));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	if (rc == 0 && res_buf)
+		memcpy(res_buf, data + 10, TPM_DIGEST_SIZE);
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+#define EXTEND_PCR_SIZE 34
+static const u8 pcrextend[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 34,		/* length */
+	0, 0, 0, 20,		/* TPM_ORD_Extend */
+	0, 0, 0, 0		/* PCR index */
+};
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to extend
+ * @hash: 	hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash)
+{
+	u8 data[EXTEND_PCR_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}

drivers/char/tpm/tpm.c out: · line 898

Quoted 10 lines
drivers/char/tpm/tpm.c +10 -0
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();
+
+	BUILD_BUG_ON(sizeof(pcrextend) > EXTEND_PCR_SIZE);
+	memcpy(data, pcrextend, sizeof(pcrextend));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);

This bit of code looks duplicated too. I really wish these 10's and 14's weren't magic numbers, especially since they're used twice.

drivers/char/tpm/tpm.c out: · line 906

Quoted 8 lines
drivers/char/tpm/tpm.c +8 -0
+	memcpy(data + 14, hash, TPM_DIGEST_SIZE);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}

Looking at this, I can't help but think a couple of nicely laid out structs with a union or two could make this all look nicer. For instance, is the return code from the tpm_transmit() function always returned in the 6th byte?

It looks to me like there is a TPM_RET_CODE_IDX in drivers/char/tpm/tpm.c. Why on earth isn't that being used? That also makes me question all these other magic numbers.

Why not just integrate that rc tinkering into tpm_transmit(), or a variant of it. There appear to be at least three or four other users that could benefit from such a function. If you decide to mess with it further than just exporting it, please break that out into a separate patch, btw.

drivers/char/tpm/tpm.c out: · line 908

Quoted 3 lines
drivers/char/tpm/tpm.c +3 -0
+enum tpm_chip_num {
+	TPM_ANY_NUM = 0xFFFF,
+};

Why bother even checking this sucker if there's only one value?

drivers/char/tpm/tpm.c out:

Quoted 6 lines
drivers/char/tpm/tpm.c +6 -0
+#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
+
+extern int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash);
+#endif
+#endif

The " || defined(CONFIG_TCG_TPM_MODULE)" doesn't do anything. CONFIG_TCG_TPM is still true even when CONFIG_TCG_TPM_MODULE.

I also think so many authors on the header is a bit excessive. 5 authors for 2 enums and 2 function declarations. :)

-- Dave

#3 lore raw

Re: [PATCH 1/6] integrity: TPM internel kernel interface

Quoted 103 lines · Dave Hansen
Quoted 87 lines · Mimi Zohar

This patch adds internal kernel support for:
- reading/extending a pcr value
- looking up the tpm_chip for a given chip number and type

---

drivers/char/tpm/tpm.c +23 -3
index 9c47dc4..17d2849 100644
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2004,2007,2008 IBM Corporation
  *
  * Authors:
  * Leendert van Doorn <[email protected]>
  * Dave Safford <[email protected]>
  * Reiner Sailer <[email protected]>
  * Kylene Hall <[email protected]>
+ * Debora Velarde <[email protected]>
  *
  * Maintained by: <[email protected]>
  *
@@ -28,6 +29,14 @@
 #include <linux/spinlock.h>
 #include <linux/smp_lock.h>
 
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/scatterlist.h>
+#include <linux/rcupdate.h>
+#include <asm/unaligned.h>
 #include "tpm.h"
 
 enum tpm_const {
@@ -50,6 +59,8 @@ enum tpm_duration {
 static LIST_HEAD(tpm_chip_list);
 static DEFINE_SPINLOCK(driver_lock);
 static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
+#define TPM_CHIP_NUM_MASK       0x0000ffff
+#define TPM_CHIP_TYPE_SHIFT     16
 
 /*
  * Array with one entry per ordinal defining the maximum amount
@@ -366,8 +377,7 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
 /*
  * Internal kernel interface to transmit TPM commands
  */
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
-			    size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, char *buf, size_t bufsiz)
 {
 	ssize_t rc;
 	u32 count, ordinal;
@@ -425,6 +435,7 @@ out:
 	mutex_unlock(&chip->tpm_mutex);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm_transmit);
 
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
@@ -717,6 +728,7 @@ ssize_t tpm_show_temp_deactivated(struct device * dev,
 }
 EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
+#define READ_PCR_RESULT_SIZE 30
 static const u8 pcrread[] = {
 	0, 193,			/* TPM_TAG_RQU_COMMAND */
 	0, 0, 0, 14,		/* length */
@@ -772,6 +784,128 @@ out:
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
+/*
+ * tpm_chip_lookup - return tpm_chip for given chip number and type
+ *
+ * Must be called with rcu_read_lock.
+ */
+static struct tpm_chip *tpm_chip_lookup(int chip_num, int chip_typ)
+{
+	struct tpm_chip *pos;

drivers/char/tpm/tpm.c out: · line 804

Quoted 10 lines
drivers/char/tpm/tpm.c +10 -0
+	int rc;
+
+	list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+		rc = (chip_num == TPM_ANY_NUM || pos->dev_num == chip_num)
+		    && (chip_typ == TPM_ANY_TYPE);
+		if (rc)
+			return pos;
+	}
+	return NULL;
+}

If you have to respin these patches could you consider simplifying that loop? I find that really hard to read. I think it's much easier to read if written out something like this:

Quoted 9 lines
	/* Dunno why they *must* specify TPM_ANY_TYPE, but they do */
	if (chip_typ != TPM_ANY_TYPE)
		continue;

	if (chip_num == TPM_ANY_NUM)
		return pos;
	if (pos->dev_num == chip_num)
		return pos;

If that's so confusing, it will be included in the next patchset.

Quoted 28 lines
Quoted 28 lines
drivers/char/tpm/tpm.c +28 -0
+
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to retrieve
+ * @res_buf: 	TPM_PCR value
+ * 		size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf)
+{
+	u8 data[READ_PCR_RESULT_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
Quoted 9 lines

drivers/char/tpm/tpm.c out: · line 837

Quoted 5 lines
drivers/char/tpm/tpm.c +5 -0
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();

This little bit of lookup, check for NULL, and try_module_get() looks cut-n-pasted in the next two functions. Should be consolidated.

Same here.

Quoted 2 lines

Also, if you need to shift down the chip_id every time anyway, why not just do it inside the lookup function?

tpm_chip_lookup() only needs the chip type, not the entire chip_id, so its usage is probably clearer if written this way.

Quoted 55 lines
Quoted 55 lines
drivers/char/tpm/tpm.c +55 -0
+	BUILD_BUG_ON(sizeof(pcrread) > READ_PCR_RESULT_SIZE);
+	memcpy(data, pcrread, sizeof(pcrread));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	if (rc == 0 && res_buf)
+		memcpy(res_buf, data + 10, TPM_DIGEST_SIZE);
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+#define EXTEND_PCR_SIZE 34
+static const u8 pcrextend[] = {
+	0, 193,			/* TPM_TAG_RQU_COMMAND */
+	0, 0, 0, 34,		/* length */
+	0, 0, 0, 20,		/* TPM_ORD_Extend */
+	0, 0, 0, 0		/* PCR index */
+};
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to extend
+ * @hash: 	hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash)
+{
+	u8 data[EXTEND_PCR_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();
Quoted 9 lines

drivers/char/tpm/tpm.c out: · line 898

Quoted 5 lines
drivers/char/tpm/tpm.c +5 -0
+
+	BUILD_BUG_ON(sizeof(pcrextend) > EXTEND_PCR_SIZE);
+	memcpy(data, pcrextend, sizeof(pcrextend));
+	index = cpu_to_be32(pcr_idx);
+	memcpy(data + 10, &index, 4);

This bit of code looks duplicated too. I really wish these 10's and 14's weren't magic numbers, especially since they're used twice.

10 is pcrextend's size, and 14 is pcrextend's size + pcr_idx's size. Probably this little math will be clearer by defining this values previously, assigning a meaningful name to them..

Quoted 14 lines

drivers/char/tpm/tpm.c out: · line 906

Quoted 8 lines
drivers/char/tpm/tpm.c +8 -0
+	memcpy(data + 14, hash, TPM_DIGEST_SIZE);
+	rc = tpm_transmit(chip, data, sizeof(data));
+	if (rc > 0)
+		rc = get_unaligned_be32((__be32 *) (data + 6));
+
+	module_put(chip->dev->driver->owner);
+	return rc;
+}

Looking at this, I can't help but think a couple of nicely laid out structs with a union or two could make this all look nicer. For instance, is the return code from the tpm_transmit() function always returned in the 6th byte?

Quoted 4 lines

It looks to me like there is a TPM_RET_CODE_IDX in drivers/char/tpm/tpm.c. Why on earth isn't that being used? That also makes me question all these other magic numbers.

It will be used, replacing the magical 6.

Quoted 22 lines

Why not just integrate that rc tinkering into tpm_transmit(), or a variant of it. There appear to be at least three or four other users that could benefit from such a function. If you decide to mess with it further than just exporting it, please break that out into a separate patch, btw.

drivers/char/tpm/tpm.c out: · line 908

Quoted 3 lines
drivers/char/tpm/tpm.c +3 -0
+enum tpm_chip_num {
+	TPM_ANY_NUM = 0xFFFF,
+};

Why bother even checking this sucker if there's only one value?

drivers/char/tpm/tpm.c out:

Quoted 6 lines
drivers/char/tpm/tpm.c +6 -0
+#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
+
+extern int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash);
+#endif
+#endif

The " || defined(CONFIG_TCG_TPM_MODULE)" doesn't do anything. CONFIG_TCG_TPM is still true even when CONFIG_TCG_TPM_MODULE.

Quoted 3 lines

I also think so many authors on the header is a bit excessive. 5 authors for 2 enums and 2 function declarations. :)

We just added another 1, Debora Velarde, the new maintainer, and it's for the whole tpm.c, not just this patch.

Thanks!

Rajiv

Quoted 7 lines

-- Dave

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

Signature · 3 lines
Rajiv Andrade <[email protected]>
Security Development
IBM Linux Technology Center
#4 lore raw

Re: [PATCH 1/6] integrity: TPM internel kernel interface

Quoted 38 lines
Quoted 28 lines
Quoted 28 lines
drivers/char/tpm/tpm.c +28 -0
+
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_id: 	tpm chip identifier
+ * 		Upper 2 bytes: ANY, HW_ONLY or SW_ONLY
+ * 		Lower 2 bytes: tpm idx # or AN&
+ * @pcr_idx:	pcr idx to retrieve
+ * @res_buf: 	TPM_PCR value
+ * 		size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf)
+{
+	u8 data[READ_PCR_RESULT_SIZE];
+	int rc;
+	__be32 index;
+	int chip_num = chip_id & TPM_CHIP_NUM_MASK;
+	struct tpm_chip *chip;
+
+	rcu_read_lock();
+	chip = tpm_chip_lookup(chip_num, chip_id >> TPM_CHIP_TYPE_SHIFT);
+	if (chip == NULL) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
Quoted 9 lines

drivers/char/tpm/tpm.c out: · line 837

Quoted 5 lines
drivers/char/tpm/tpm.c +5 -0
+	if (!try_module_get(chip->dev->driver->owner)) {
+		rcu_read_unlock();
+		return -ENODEV;
+	}
+	rcu_read_unlock();

This little bit of lookup, check for NULL, and try_module_get() looks cut-n-pasted in the next two functions. Should be consolidated.

Quoted 8 lines

Same here.

Quoted 2 lines

Also, if you need to shift down the chip_id every time anyway, why not just do it inside the lookup function?

tpm_chip_lookup() only needs the chip type, not the entire chip_id, so its usage is probably clearer if written this way.

Wait.. chip_num, the other parameter, depends on chip_id and a previously defined constant, so, you are right, it's saner to just pass chip_id to it.. sorry, and thanks. This will be included also on the next patchset I'm about to send.

Rajiv

#5 lore raw

Re: [PATCH 1/6] integrity: TPM internel kernel interface

In reply to Mimi Zohar
Quoted 72 lines · Mimi Zohar

This patch adds internal kernel support for:
- reading/extending a pcr value
- looking up the tpm_chip for a given chip number and type

---

drivers/char/tpm/tpm.c +15 -3
index 9c47dc4..17d2849 100644
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2004 IBM Corporation
+ * Copyright (C) 2004,2007,2008 IBM Corporation
  *
  * Authors:
  * Leendert van Doorn <[email protected]>
  * Dave Safford <[email protected]>
  * Reiner Sailer <[email protected]>
  * Kylene Hall <[email protected]>
+ * Debora Velarde <[email protected]>
  *
  * Maintained by: <[email protected]>
  *
@@ -28,6 +29,14 @@
 #include <linux/spinlock.h>
 #include <linux/smp_lock.h>
 
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/fs.h>
+#include <linux/scatterlist.h>
+#include <linux/rcupdate.h>
+#include <asm/unaligned.h>
 #include "tpm.h"
 
 enum tpm_const {
@@ -50,6 +59,8 @@ enum tpm_duration {
 static LIST_HEAD(tpm_chip_list);
 static DEFINE_SPINLOCK(driver_lock);
 static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
+#define TPM_CHIP_NUM_MASK       0x0000ffff
+#define TPM_CHIP_TYPE_SHIFT     16
 
 /*
  * Array with one entry per ordinal defining the maximum amount
@@ -366,8 +377,7 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
 /*
  * Internal kernel interface to transmit TPM commands
  */
-static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
-			    size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, char *buf, size_t bufsiz)
 {
 	ssize_t rc;
 	u32 count, ordinal;
@@ -425,6 +435,7 @@ out:
 	mutex_unlock(&chip->tpm_mutex);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(tpm_transmit);
 
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
@@ -717,6 +728,7 @@ ssize_t tpm_show_temp_deactivated(struct device * dev,
 }
 EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
+#define READ_PCR_RESULT_SIZE 30

[...]

Quoted 18 lines
drivers/char/tpm/tpm.c +18 -0
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+#define PCI_DEVICE_ID_AMD_8111_LPC    0x7468
+
+/*
+ * Chip type is one of these values in the upper two bytes of chip_id
+ */
+enum tpm_chip_type {
+	TPM_HW_TYPE = 0x0,
+	TPM_SW_TYPE = 0x1,
+	TPM_ANY_TYPE = 0xFFFF,
+};
+
+/*
+ * Chip num is this value or a valid tpm idx in lower two bytes of chip_id
+ */
+enum tpm_chip_num {

drivers/char/tpm/tpm.c

Quoted 10 lines
drivers/char/tpm/tpm.c +10 -0
+	TPM_ANY_NUM = 0xFFFF,
+};
+
+
+#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
+
+extern int tpm_pcr_read(u32 chip_id, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_id, int pcr_idx, const u8 *hash);
+#endif
+#endif

Minor nits:

* this is a bit schizophrenic with regards to defining named constants; sometimes enum is used, other times #define is used. It would be better to just use enum, which is what the current code does (see top of pre-patched tpm.c)

* you really shouldn't hide PCI_DEVICE_ID_xxx constants in places other than pci_ids.h.

* furthermore, is that constant even used? used in a later patch? if the constant is only used once, e.g. in a pci_device_list list, then consider eliminating the constant altogether and directly using the PCI device id hexidecimal number in the target location.

Jeff

maintainer Acked-by Serge Hallyn on v1 1/6
#6 lore raw

Re: [PATCH 1/6] integrity: TPM internel kernel interface

In reply to Mimi Zohar
Quoted 6 lines · Mimi Zohar

This patch adds internal kernel support for:
- reading/extending a pcr value
- looking up the tpm_chip for a given chip number and type

(/me wonders why I can't seem to get any direct emails from you)

Dave's comments are good, and I'm sure you'll address them in a followup :), but the basic chip lookup idiom (which as Dave says should be consolidated) does look correct.

-serge

author
#7 lore raw

PATCH 2/6 integrity: Linux Integrity Module(LIM)

In reply to Mimi Zohar

Based on comments on the mailing list, this patch:
- replaces the integrity_find_template() with integrity_find_get_template(), which finds the template and increments the template refcount.
- replaces the local template tget()/tput() calls with kref calls.

This patch provides an integrity framework(api and hooks) and placement of the integrity hooks in the appropriate places in the fs directory. Collecting, appraising, and storing of file and other types of integrity data is supported. Multiple integrity templates, which implement the integrity API, may register themselves.

---

Documentation/kernel-parameters.txt +6 -0
index e0f346d..f98984e 100644
@@ -44,6 +44,7 @@ parameter is applicable:
 	FB	The frame buffer device is enabled.
 	HW	Appropriate hardware is enabled.
 	IA-64	IA-64 architecture is enabled.
+	INTEGRITY Integrity support is enabled.
 	IOSCHED	More than one I/O scheduler is enabled.
 	IP_PNP	IP DHCP, BOOTP, or RARP is enabled.
 	ISAPNP	ISA PnP code is enabled.
@@ -896,6 +897,11 @@ and is between 256 and 4096 characters. It is defined in the file
 	inport.irq=	[HW] Inport (ATI XL and Microsoft) busmouse driver
 			Format: <irq>
 
+	integrity_audit= [INTEGRITY]
+                        Format: { "0" | "1" }
+                        0 -- integrity auditing messages. (Default)
+                        1 -- enable informational integrity auditing messages.
+
 	inttest=	[IA64]
 
 	iommu=		[x86]
fs/exec.c +7 -0
index 4e834f1..3494c4c 100644
@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mount.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/syscalls.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
@@ -129,6 +130,9 @@ asmlinkage long sys_uselib(const char __user * library)
 	error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN);
 	if (error)
 		goto exit;
+	error = integrity_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+	if (error)
+		goto exit;
 
 	file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
 	error = PTR_ERR(file);
@@ -1199,6 +1203,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
 	retval = security_bprm_check(bprm);
 	if (retval)
 		return retval;
+	retval = integrity_bprm_check(bprm);
+	if (retval)
+		return retval;
 
 	/* kernel module loader fixup */
 	/* so we don't try to load run modprobe in kernel space. */
fs/file_table.c +2 -0
index 5ad0eca..db32740 100644
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/eventpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/mount.h>
@@ -276,6 +277,7 @@ void __fput(struct file *file)
 	if (file->f_op && file->f_op->release)
 		file->f_op->release(inode, file);
 	security_file_free(file);
+	integrity_file_free(file);
 	if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
 		cdev_put(inode->i_cdev);
 	fops_put(file->f_op);
fs/inode.c +16 -6
index 0487ddb..9fb48e7 100644
@@ -17,6 +17,7 @@
 #include <linux/hash.h>
 #include <linux/swap.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
@@ -143,12 +144,13 @@ static struct inode *alloc_inode(struct super_block *sb)
 		inode->i_cdev = NULL;
 		inode->i_rdev = 0;
 		inode->dirtied_when = 0;
-		if (security_inode_alloc(inode)) {
-			if (inode->i_sb->s_op->destroy_inode)
-				inode->i_sb->s_op->destroy_inode(inode);
-			else
-				kmem_cache_free(inode_cachep, (inode));
-			return NULL;
+		if (security_inode_alloc(inode))
+			goto out_free_inode;
+
+		/* allocate and initialize an i_integrity */
+		if (integrity_inode_alloc(inode)) {
+			security_inode_free(inode);
+			goto out_free_inode;
 		}
 
 		spin_lock_init(&inode->i_lock);
@@ -185,12 +187,20 @@ static struct inode *alloc_inode(struct super_block *sb)
 		inode->i_mapping = mapping;
 	}
 	return inode;
+
+out_free_inode:
+	if (inode->i_sb->s_op->destroy_inode)
+		inode->i_sb->s_op->destroy_inode(inode);
+	else
+		kmem_cache_free(inode_cachep, (inode));
+	return NULL;
 }
 
 void destroy_inode(struct inode *inode) 
 {
 	BUG_ON(inode_has_buffers(inode));
 	security_inode_free(inode);
+	integrity_inode_free(inode);
 	if (inode->i_sb->s_op->destroy_inode)
 		inode->i_sb->s_op->destroy_inode(inode);
 	else
fs/namei.c +6 -0
index d34e0f9..7e27006 100644
@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
@@ -1523,6 +1524,11 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
 	error = vfs_permission(nd, acc_mode);
 	if (error)
 		return error;
+
+	error = integrity_path_check(&nd->path, acc_mode
+				     & (MAY_READ | MAY_WRITE | MAY_EXEC));
+	if (error)
+		return error;
 	/*
 	 * An append-only file must be opened in append mode for writing.
 	 */
include/linux/audit.h +5 -0
index 6272a39..63bb3ec 100644
@@ -123,6 +123,11 @@
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
 
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 
include/linux/integrity.h new file +172 -0

172 lines not on this page · raw message

mm/mmap.c +4 -0
index d4855a6..7cdfd3a 100644
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/integrity.h>
 #include <linux/hugetlb.h>
 #include <linux/profile.h>
 #include <linux/module.h>
@@ -1050,6 +1051,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
 	error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
 	if (error)
 		return error;
+	error = integrity_file_mmap(file, prot);
+	if (error)
+		return error;
 
 	return mmap_region(file, addr, len, flags, vm_flags, pgoff,
 			   accountable);
security/Kconfig +2 -0
index d9f47ce..4c602be 100644
@@ -126,5 +126,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
 source security/selinux/Kconfig
 source security/smack/Kconfig
 
+source security/integrity/Kconfig
+
 endmenu
 
security/Makefile +4 -0
index c05c127..db9efb5 100644
@@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/built-in.o
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/built-in.o
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
+
+# Object integrity file lists
+subdir-$(CONFIG_INTEGRITY)		+= integrity
+obj-$(CONFIG_INTEGRITY)			+= integrity/built-in.o
security/integrity/Kconfig new file +24 -0
new file mode 100644
index 0000000..2123dfc
@@ -0,0 +1,24 @@
+#
+# Integrity configuration
+#
+
+config INTEGRITY
+	bool "Enable different integrity models"
+	help
+	  This allows you to choose different integrity modules to be
+	  configured into your kernel. Integrity models can collect,
+	  verify, and record measurements, such as of a file's data
+	  and metadata before it is read or executed.
+
+	  If you are unsure how to answer this question, answer N.
+
+config INTEGRITY_AUDIT
+	bool "Integrity audit boot parameter"
+	depends on INTEGRITY
+	default n
+	help
+	  This option adds a kernel parameter 'integrity_audit', which
+	  allows informational integrity auditing messages to be enabled
+	  at boot.  If this option is selected, informational integrity
+	  auditing messages can be enabled with 'integrity_audit=1' on
+	  the kernel command line.
security/integrity/Makefile new file +6 -0
new file mode 100644
index 0000000..a6e1405
@@ -0,0 +1,6 @@
+#
+# Makefile for the kernel integrity code
+#
+
+# Object file lists
+obj-$(CONFIG_INTEGRITY) += integrity.o integrity_audit.o
security/integrity/integrity.c new file +329 -0

329 lines not on this page · raw message

security/integrity/integrity.h new file +19 -0
new file mode 100644
index 0000000..6ead5ed
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <[email protected]>
+ *
+ * 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.
+ */
+
+#define TEMPLATE_NAME_LEN_MAX 12
+struct template_list_entry {
+	struct list_head template;
+	char template_name[TEMPLATE_NAME_LEN_MAX + 1];
+	const struct template_operations *template_ops;
+	struct kref refcount;
+};
+
+extern void template_release(struct kref *kref);
+extern struct template_list_entry *integrity_find_get_template(const char *);
security/integrity/integrity_audit.c new file +78 -0

78 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#8 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 22 lines · Mimi Zohar
fs/inode.c +7 -6
@@ -143,12 +144,13 @@ static struct inode *alloc_inode(struct super_block *sb)
 		inode->i_cdev = NULL;
 		inode->i_rdev = 0;
 		inode->dirtied_when = 0;
-		if (security_inode_alloc(inode)) {
-			if (inode->i_sb->s_op->destroy_inode)
-				inode->i_sb->s_op->destroy_inode(inode);
-			else
-				kmem_cache_free(inode_cachep, (inode));
-			return NULL;
+		if (security_inode_alloc(inode))
+			goto out_free_inode;
+
+		/* allocate and initialize an i_integrity */
+		if (integrity_inode_alloc(inode)) {
+			security_inode_free(inode);
+			goto out_free_inode;
 		}
 
 		spin_lock_init(&inode->i_lock);
@@ -185,12 +187,20 @@ static struct inode *alloc_inode(struct super_block *sb)
 		inode->i_mapping = mapping;

fs/inode.c static struct inode *alloc_inode(struct super_block *sb) · line 197

Quoted 10 lines
fs/inode.c +7 -0
 	}
 	return inode;
+
+out_free_inode:
+	if (inode->i_sb->s_op->destroy_inode)
+		inode->i_sb->s_op->destroy_inode(inode);
+	else
+		kmem_cache_free(inode_cachep, (inode));
+	return NULL;
 }

You were saying that this is a very hard patch set to break up. So, I'm trying to find some places that could trim a line or two here and there. Stuff like this is a primary example.

Pulling that security_inode_alloc() 'if' condition out and sticking it at the bottom of the function is a change that can stand on its own. You could put it up at the top of your series, or even send it separately. It makes this patch smaller and more obvious then.

include/linux/integrity.h line 172

Quoted 2 lines
include/linux/integrity.h new file +2 -0
+#endif
+#endif

Personally, I love to see comments on these suckers after a long header file. My memory sucks.

security/integrity/integrity.c line 41

Quoted 7 lines
security/integrity/integrity.c new file +7 -0
+int register_integrity(const struct integrity_operations *ops)
+{
+	if (integrity_ops != NULL)
+		return -EAGAIN;
+	integrity_ops = ops;
+	return 0;
+}

Is there some locking to keep this from racing and two integrity modules both thinking they succeeded? Does it matter?

Quoted 24 lines
security/integrity/integrity.c new file +24 -0
+/**
+ * integrity_register_template - registers an integrity template with the kernel
+ * @template_name: a pointer to a string containing the template name.
+ * @template_ops: a pointer to the template functions
+ *
+ * Register a set of functions to collect, appraise, store, and display
+ * a template measurement, and a means to decide whether to do them.
+ * Unlike integrity modules, any number of templates may be registered.
+ *
+ * Returns 0 on success, an error code on failure.
+ */
+int integrity_register_template(const char *template_name,
+				const struct template_operations *template_ops)
+{
+	int template_len;
+	struct template_list_entry *entry;
+
+	template_len = strlen(template_name);
+	if (template_len > TEMPLATE_NAME_LEN_MAX)
+		return -EINVAL;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;

security/integrity/integrity.c line 93

Quoted 10 lines
security/integrity/integrity.c new file +10 -0
+	INIT_LIST_HEAD(&entry->template);
+
+	kref_set(&entry->refcount, 1);
+	strcpy(entry->template_name, template_name);
+	entry->template_ops = template_ops;
+
+	mutex_lock(&integrity_templates_mutex);
+	list_add_rcu(&entry->template, &integrity_templates);
+	mutex_unlock(&integrity_templates_mutex);
+	synchronize_rcu();

What's the synchronize_rcu() for here?

Quoted 9 lines
security/integrity/integrity.c new file +9 -0
+int integrity_unregister_template(const char *template_name)
+{
+	struct template_list_entry *entry;
+
+	mutex_lock(&integrity_templates_mutex);
+	list_for_each_entry(entry, &integrity_templates, template) {
+		if (strncmp(entry->template_name, template_name,
+			    strlen(entry->template_name)) == 0) {
+			list_del_rcu(&entry->template);

security/integrity/integrity.c line 131

Quoted 10 lines
security/integrity/integrity.c new file +10 -0
+			mutex_unlock(&integrity_templates_mutex);
+			synchronize_rcu();
+			kref_put(&entry->refcount, template_release);
+			return 0;
+		}
+	}
+	mutex_unlock(&integrity_templates_mutex);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(integrity_unregister_template);

Is this frequently called? If so, it might be better to use call_rcu().

Quoted 15 lines
security/integrity/integrity.c new file +15 -0
+/**
+ * integrity_find_get_template - search the integrity_templates list
+ * @template_name: a pointer to a string containing the template name.
+ *
+ * Returns a pointer to an entry in the template list on success, NULL
+ * on failure.
+ */
+struct template_list_entry *integrity_find_get_template(const char
+							*template_name)
+{
+	struct template_list_entry *entry, *template_entry = NULL;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(entry, &integrity_templates, template) {
+		if (strncmp(entry->template_name, template_name,

security/integrity/integrity.c line 157

Quoted 10 lines
security/integrity/integrity.c new file +10 -0
+			    strlen(entry->template_name)) == 0) {
+			template_entry = entry;
+			break;
+		}
+	}
+	if (template_entry)
+		kref_get(&template_entry->refcount);
+	rcu_read_unlock();
+	return template_entry;
+}

Is there a reason not to do the kref_get() inside the loop? Would save a line of code.

Quoted 4 lines
security/integrity/integrity.c new file +4 -0
+int integrity_collect_measurement(const char *template_name, void *data)
+{
+	struct template_list_entry *template_entry;
+	int rc = -EINVAL;

security/integrity/integrity.c line 183

Quoted 10 lines
security/integrity/integrity.c new file +10 -0
+
+	template_entry = integrity_find_get_template(template_name);
+	if (template_entry) {
+		rc = template_entry->template_ops->collect_measurement(data);
+		kref_put(&template_entry->refcount, template_release);
+	}
+	return rc;
+}
+EXPORT_SYMBOL_GPL(integrity_collect_measurement);
+

It's kinda a shame to see 5 or 6 functions which are such carbon copies of each other. Could you do one of these, and just pass in the ops function as well as 'data'?

You would have one of these:

+int integrity_generic_template(const char *template_name,
+ void (*func)(void *data), void *data)
+{
+ struct template_list_entry *template_entry;
+ int rc = -EINVAL;
+
+ template_entry = integrity_find_get_template(template_name);
+ if (template_entry) {
+ rc = func(data);
+ kref_put(&template_entry->refcount, template_release);
+ }
+ return rc;
+}

And each measurement function could be something silly like:

int integrity_collect_measurement(const char *template_name, void *data)

{
	return integrity_generic_template(template_name,
		 template_entry->template_ops->collect_measurement,
		 data);
}

-- Dave

author
#9 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 6 lines · Dave Hansen

include/linux/integrity.h line 172

Quoted 2 lines · Mimi Zohar
include/linux/integrity.h new file +2 -0
+#endif
+#endif

Personally, I love to see comments on these suckers after a long header file. My memory sucks.

ok

Quoted 10 lines

security/integrity/integrity.c line 41

Quoted 7 lines
security/integrity/integrity.c new file +7 -0
+int register_integrity(const struct integrity_operations *ops)
+{
+	if (integrity_ops != NULL)
+		return -EAGAIN;
+	integrity_ops = ops;
+	return 0;
+}

Is there some locking to keep this from racing and two integrity modules both thinking they succeeded? Does it matter?

Wouldn't this be a Kconfig issue.

Quoted 28 lines
Quoted 28 lines
security/integrity/integrity.c new file +28 -0
+/**
+ * integrity_register_template - registers an integrity template with the kernel
+ * @template_name: a pointer to a string containing the template name.
+ * @template_ops: a pointer to the template functions
+ *
+ * Register a set of functions to collect, appraise, store, and display
+ * a template measurement, and a means to decide whether to do them.
+ * Unlike integrity modules, any number of templates may be registered.
+ *
+ * Returns 0 on success, an error code on failure.
+ */
+int integrity_register_template(const char *template_name,
+				const struct template_operations *template_ops)
+{
+	int template_len;
+	struct template_list_entry *entry;
+
+	template_len = strlen(template_name);
+	if (template_len > TEMPLATE_NAME_LEN_MAX)
+		return -EINVAL;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&entry->template);
+
+	kref_set(&entry->refcount, 1);
+	strcpy(entry->template_name, template_name);
Quoted 8 lines

security/integrity/integrity.c line 93

Quoted 6 lines
security/integrity/integrity.c new file +6 -0
+	entry->template_ops = template_ops;
+
+	mutex_lock(&integrity_templates_mutex);
+	list_add_rcu(&entry->template, &integrity_templates);
+	mutex_unlock(&integrity_templates_mutex);
+	synchronize_rcu();

What's the synchronize_rcu() for here?

good question.

Quoted 14 lines
Quoted 14 lines
security/integrity/integrity.c new file +14 -0
+int integrity_unregister_template(const char *template_name)
+{
+	struct template_list_entry *entry;
+
+	mutex_lock(&integrity_templates_mutex);
+	list_for_each_entry(entry, &integrity_templates, template) {
+		if (strncmp(entry->template_name, template_name,
+			    strlen(entry->template_name)) == 0) {
+			list_del_rcu(&entry->template);
+			mutex_unlock(&integrity_templates_mutex);
+			synchronize_rcu();
+			kref_put(&entry->refcount, template_release);
+			return 0;
+		}
Quoted 8 lines

security/integrity/integrity.c line 131

Quoted 5 lines
security/integrity/integrity.c new file +5 -0
+	}
+	mutex_unlock(&integrity_templates_mutex);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(integrity_unregister_template);

Is this frequently called? If so, it might be better to use call_rcu().

I don't expect that Templates would be added/removed frequently, but I could be wrong. Only time will tell.

Quoted 20 lines
Quoted 20 lines
security/integrity/integrity.c new file +20 -0
+/**
+ * integrity_find_get_template - search the integrity_templates list
+ * @template_name: a pointer to a string containing the template name.
+ *
+ * Returns a pointer to an entry in the template list on success, NULL
+ * on failure.
+ */
+struct template_list_entry *integrity_find_get_template(const char
+							*template_name)
+{
+	struct template_list_entry *entry, *template_entry = NULL;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(entry, &integrity_templates, template) {
+		if (strncmp(entry->template_name, template_name,
+			    strlen(entry->template_name)) == 0) {
+			template_entry = entry;
+			break;
+		}
+	}
Quoted 8 lines

security/integrity/integrity.c line 157

Quoted 5 lines
security/integrity/integrity.c new file +5 -0
+	if (template_entry)
+		kref_get(&template_entry->refcount);
+	rcu_read_unlock();
+	return template_entry;
+}

Is there a reason not to do the kref_get() inside the loop? Would save a line of code.

none, will do.

Quoted 37 lines
Quoted 4 lines
security/integrity/integrity.c new file +4 -0
+int integrity_collect_measurement(const char *template_name, void *data)
+{
+	struct template_list_entry *template_entry;
+	int rc = -EINVAL;

security/integrity/integrity.c line 183

Quoted 10 lines
security/integrity/integrity.c new file +10 -0
+
+	template_entry = integrity_find_get_template(template_name);
+	if (template_entry) {
+		rc = template_entry->template_ops->collect_measurement(data);
+		kref_put(&template_entry->refcount, template_release);
+	}
+	return rc;
+}
+EXPORT_SYMBOL_GPL(integrity_collect_measurement);
+

It's kinda a shame to see 5 or 6 functions which are such carbon copies of each other. Could you do one of these, and just pass in the ops function as well as 'data'?

You would have one of these:

security/integrity/integrity.c new file +13 -0
+int integrity_generic_template(const char *template_name,
+			      void (*func)(void *data), void *data)
+{
+       struct template_list_entry *template_entry;
+       int rc = -EINVAL;
+
+       template_entry = integrity_find_get_template(template_name);
+       if (template_entry) {
+               rc = func(data);
+               kref_put(&template_entry->refcount, template_release);
+       }
+       return rc;
+}

And each measurement function could be something silly like:

Quoted 8 lines

int integrity_collect_measurement(const char *template_name, void *data)

{
	return integrity_generic_template(template_name,
		 template_entry->template_ops->collect_measurement,
		 data);
}

-- Dave

Yes, will re-factor the code. Thanks!

Mimi

#10 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 11 lines · Mimi Zohar
Quoted 10 lines

security/integrity/integrity.c line 41

Quoted 7 lines
security/integrity/integrity.c new file +7 -0
+int register_integrity(const struct integrity_operations *ops)
+{
+	if (integrity_ops != NULL)
+		return -EAGAIN;
+	integrity_ops = ops;
+	return 0;
+}

Is there some locking to keep this from racing and two integrity modules both thinking they succeeded? Does it matter?

Quoted 1 line

Wouldn't this be a Kconfig issue.

If it is a Kconfig issue, then we don't really need all the function pointers and ops things. Just pick which integrity infrastructure you want at compile time. You also wouldn't need that integrity_ops function at all.

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
security/integrity/integrity.c new file +3 -0
+int integrity_register_template(const char *template_name,
+				const struct template_operations *template_ops)
+{

...

Quoted 8 lines
Quoted 6 lines

security/integrity/integrity.c line 93

Quoted 4 lines
security/integrity/integrity.c new file +4 -0
+	mutex_lock(&integrity_templates_mutex);
+	list_add_rcu(&entry->template, &integrity_templates);
+	mutex_unlock(&integrity_templates_mutex);
+	synchronize_rcu();

What's the synchronize_rcu() for here?

good question.

So, you'll go investigate this, fix it up in a future patch and if you decide it needs to be kept, it will be commented, right?

Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
security/integrity/integrity.c new file +14 -0
+int integrity_unregister_template(const char *template_name)
+{
+	struct template_list_entry *entry;
+
+	mutex_lock(&integrity_templates_mutex);
+	list_for_each_entry(entry, &integrity_templates, template) {
+		if (strncmp(entry->template_name, template_name,
+			    strlen(entry->template_name)) == 0) {
+			list_del_rcu(&entry->template);
+			mutex_unlock(&integrity_templates_mutex);
+			synchronize_rcu();
+			kref_put(&entry->refcount, template_release);
+			return 0;
+		}
Quoted 8 lines

security/integrity/integrity.c line 131

Quoted 5 lines
security/integrity/integrity.c new file +5 -0
+	}
+	mutex_unlock(&integrity_templates_mutex);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(integrity_unregister_template);

Is this frequently called? If so, it might be better to use call_rcu().

Quoted 2 lines

I don't expect that Templates would be added/removed frequently, but I could be wrong. Only time will tell.

synchronize_rcu() is an "expensive" operation in wall clock time. It won't cost you a lot of CPU or anything, but it could cause this code to block for a bit of time.

What you've said there is:

1. delete the template from the list
2. sleep until no one can possibly see the template any more
3. delete template

Call RCU would, instead, queue the template (and the kref_put()) in a list. That list gets called in a batched manner once a grace period has elapsed. The batching speeds things up, and it also doesn't block in the caller.

I do see some other use like this in the kernel, but I have a suspicion that there's a better way to do it. Could you have Paul McKenney take a quick look? Dipankar or Josh Triplett seem to also love to look at RCU. :)

-- Dave

#11 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

In reply to Mimi Zohar
Quoted 26 lines · Mimi Zohar

+/*
+ * Integrity API calls:
+ *
+ * @collect_measurement:
+ * Collect template specific measurement data.
+ * @data contains template specific data used for collecting the
+ * measurement.
+ * Return 0 if operation was successful.
+ *
+ * @appraise_measurement:
+ * Appraise the integrity of the template specific measurement data.
+ * @data contains template specific data used for appraising the
+ * measurement.
+ * Return 0 if operation was successful.
+ *
+ * @store_measurement:
+ * Store the template specific data.
+ * @data contains template specific data used for storing the
+ * measurement.
+ *
+ * @store_template:
+ * Store the entry containing the template specific data.
+ * @data contains template name, data length, and data.
+ *
+ * @must_measure:
+ * Measurement decision based on an integrity policy.

Quoted 8 lines

+ * @data contains template specific data used for making policy
+ * decision.
+ * Return 0 if operation was successful.
+ *
+ * @display_template:
+ * Display template specific data.
+ *
+ */

Can you explain what all this template stuff is about? The only method of these ever called is display_template, and that seems to be better implented directly as a securityfs file, without the indirection.

author
#12 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 36 lines · Christoph Hellwig
Quoted 26 lines · Mimi Zohar

+/*
+ * Integrity API calls:
+ *
+ * @collect_measurement:
+ * Collect template specific measurement data.
+ * @data contains template specific data used for collecting the
+ * measurement.
+ * Return 0 if operation was successful.
+ *
+ * @appraise_measurement:
+ * Appraise the integrity of the template specific measurement data.
+ * @data contains template specific data used for appraising the
+ * measurement.
+ * Return 0 if operation was successful.
+ *
+ * @store_measurement:
+ * Store the template specific data.
+ * @data contains template specific data used for storing the
+ * measurement.
+ *
+ * @store_template:
+ * Store the entry containing the template specific data.
+ * @data contains template name, data length, and data.
+ *
+ * @must_measure:
+ * Measurement decision based on an integrity policy.

Quoted 8 lines

+ * @data contains template specific data used for making policy
+ * decision.
+ * Return 0 if operation was successful.
+ *
+ * @display_template:
+ * Display template specific data.
+ *
+ */

Quoted 3 lines

Can you explain what all this template stuff is about? The only method of these ever called is display_template, and that seems to be better implented directly as a securityfs file, without the indirection.

IMA originally supported measurement and attestation only for file data. Templates provide an abstraction to add different types of integrity messages to the TPM based measurement list. Each type of integrity code knows how to format/display its own messages, while the TPM measurement list code remains generic.

Mimi

#13 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 5 lines · Mimi Zohar

IMA originally supported measurement and attestation only for file data. Templates provide an abstraction to add different types of integrity messages to the TPM based measurement list. Each type of integrity code knows how to format/display its own messages, while the TPM measurement list code remains generic.

I have a bit of a problem parsing the above, and it certainly doesn't look like a justification for keeping all that unused code around.

author
#14 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 9 lines · Christoph Hellwig
Quoted 5 lines · Mimi Zohar

IMA originally supported measurement and attestation only for file data. Templates provide an abstraction to add different types of integrity messages to the TPM based measurement list. Each type of integrity code knows how to format/display its own messages, while the TPM measurement list code remains generic.

I have a bit of a problem parsing the above, and it certainly doesn't look like a justification for keeping all that unused code around.

The purpose of LIM is to provide an integrity infrastructure to support different types of integrity data. IMA implements both the LIM API for it's own internal use, and exports it for others to call.

As Dave Safford pointed out in http://lkml.org/lkml/2008/11/17/362,
there are other projects that want to add differently structured measurements to the TPM measurement list. The template abstraction is critical to allowing these differently formatted messages to be added to the list.

Mimi

#15 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 7 lines · Mimi Zohar
Quoted 2 lines

I have a bit of a problem parsing the above, and it certainly doesn't look like a justification for keeping all that unused code around.

The purpose of LIM is to provide an integrity infrastructure to support different types of integrity data. IMA implements both the LIM API for it's own internal use, and exports it for others to call.

Quoted 5 lines

As Dave Safford pointed out in http://lkml.org/lkml/2008/11/17/362,
there are other projects that want to add differently structured measurements to the TPM measurement list. The template abstraction is critical to allowing these differently formatted messages to be added to the list.

I think we're talking past each other.

In integrity.h there are two operation vectors defines:

- struct integrity_operations delcares the operations called from the VFS. This one is actually used. While I don't agree to Dave's argument, because we don't put bloat in just because people might eventually some day use it when they are in the right mood and the sun shines, thisn't isn't the one I'm talking about in this thread.
- struct template_operations on the others is not only really badly named for appearing in a global header but also not used in a meaningfull way. There is one single instace of it, ima_template_ops, and while there are five helpers added in the second patch that use it (integrity_collect_measurement, integrity_appraise_measurement, integrity_store_measurement, integrity_store_template, integrity_must_measure) none of them is used at all during the patch series. There are two direct uses of these template added in the third path, to implement the show operations for the "binary_runtime_measurements" and "ascii_runtime_measurements" files ins securityfs, but given that those are inside ima there no reason for the indirection at all.

Quoted 5 lines

Mimi

---end quoted text---

maintainer
#16 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 27 lines · Christoph Hellwig
Quoted 7 lines · Mimi Zohar
Quoted 2 lines

I have a bit of a problem parsing the above, and it certainly doesn't look like a justification for keeping all that unused code around.

The purpose of LIM is to provide an integrity infrastructure to support different types of integrity data. IMA implements both the LIM API for it's own internal use, and exports it for others to call.

Quoted 5 lines

As Dave Safford pointed out in http://lkml.org/lkml/2008/11/17/362,
there are other projects that want to add differently structured measurements to the TPM measurement list. The template abstraction is critical to allowing these differently formatted messages to be added to the list.

I think we're talking past each other.

In integrity.h there are two operation vectors defines:

- struct integrity_operations delcares the operations called from the VFS. This one is actually used. While I don't agree to Dave's argument, because we don't put bloat in just because people might eventually some day use it when they are in the right mood and the sun shines, thisn't isn't the one I'm talking about in this thread.
- struct template_operations on the others is not only really badly named for appearing in a global header but also not used in a meaningfull way. There is one single instace of it, ima_template_ops, and while there are five helpers added in the

Quoted 8 lines

second patch that use it (integrity_collect_measurement, integrity_appraise_measurement, integrity_store_measurement, integrity_store_template, integrity_must_measure) none of them is used at all during the patch series. There are two direct uses of these template added in the third path, to implement the show operations for the "binary_runtime_measurements" and "ascii_runtime_measurements" files ins securityfs, but given that those are inside ima there no reason for the indirection at all.

Yeah I can definately see that.

Mimi, you used to have another template (I thought) which just tracked security_ops to try and prevent subversion of the LSM hooks. Or something like that. That was a separate template_ops, right? Can you post that again? That might answer both Christoph's query about the usefulness of the indirection, and Dave's question about "how could I use this, anyway".

If you do repost it, please be very clear about what it is expected to do/protect against, and how, using no acronyms which you don't define on first use :)

thanks,
-serge

#17 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

In reply to Christoph Hellwig
Quoted 2 lines · Christoph Hellwig

In integrity.h there are two operation vectors defines:

In the past, the integrity hooks were discussed with the LSM/security community, and others expressed interest in using them, and we were trying to accommodate their requests. On the other hand, we have separately asked them to chime in here to defend these hooks, and have heard nothing. As everyone has pointed out, if no one uses them, they are bloat, and if someone else wants them in the future, they can easily be added back. In a little more detail:

Quoted 5 lines

- struct integrity_operations delcares the operations called from the VFS. This one is actually used. While I don't agree to Dave's argument, because we don't put bloat in just because people might eventually some day use it when they are in the right mood and the sun shines, thisn't isn't the one I'm talking about in this thread.

These hooks were for alternate integrity modules, and since no one else has defended them, we have to agree that they should be replaced with direct calls.

Quoted 4 lines

- struct template_operations on the others is not only really badly named for appearing in a global header but also not used in a meaningfull way. There is one single instace of it, ima_template_ops, and while there are five helpers added in the

Quoted 8 lines

second patch that use it (integrity_collect_measurement, integrity_appraise_measurement, integrity_store_measurement, integrity_store_template, integrity_must_measure) none of them is used at all during the patch series. There are two direct uses of these template added in the third path, to implement the show operations for the "binary_runtime_measurements" and "ascii_runtime_measurements" files ins securityfs, but given that those are inside ima there no reason for the indirection at all.

These hooks were for potential use by LSM modules to inquire about the integrity of files, and for other modules to be able to anchor data in the TPM list. Again, since no one has chimed in to defend the hooks, we have no problem removing them.

Sorry about arguing too long on this, and thanks for all the reviews...

dave

maintainer
#18 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 9 lines · david safford
Quoted 5 lines

- struct integrity_operations delcares the operations called from the VFS. This one is actually used. While I don't agree to Dave's argument, because we don't put bloat in just because people might eventually some day use it when they are in the right mood and the sun shines, thisn't isn't the one I'm talking about in this thread.

These hooks were for alternate integrity modules, and since no one else has defended them, we have to agree that they should be replaced with direct calls.

If you know of other modules which are planned to be ported to this framework, merged upstream and supported, then this would be similar to the situation when LSM was initially developed.

You've previously mentioned some active projects here:
http://lkml.org/lkml/2008/11/17/362

Are there any definite committments to push these upstream when the integrity framework is merged?

- James

Signature · 2 lines
James Morris
<[email protected]>
#19 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 10 lines · James Morris
Quoted 3 lines · david safford

These hooks were for alternate integrity modules, and since no one else has defended them, we have to agree that they should be replaced with direct calls.

If you know of other modules which are planned to be ported to this framework, merged upstream and supported, then this would be similar to the situation when LSM was initially developed.

Quoted 5 lines

You've previously mentioned some active projects here:
http://lkml.org/lkml/2008/11/17/362

Are there any definite commitments to push these upstream when the integrity framework is merged?

All of the projects listed in that posting were ones depending on IMA, with no requirements for alternate modules. I do hope that there will be other integrity modules in addition to the TPM oriented IMA, and I do know of several research projects in this space, but I don't know if/when any of these are planning on submission. If others are submitted, it would certainly be simple to add the hooks back in.

dave

#20 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 17 lines · david safford
Quoted 10 lines · James Morris
Quoted 3 lines · david safford

These hooks were for alternate integrity modules, and since no one else has defended them, we have to agree that they should be replaced with direct calls.

If you know of other modules which are planned to be ported to this framework, merged upstream and supported, then this would be similar to the situation when LSM was initially developed.

Quoted 5 lines

You've previously mentioned some active projects here:
http://lkml.org/lkml/2008/11/17/362

Are there any definite commitments to push these upstream when the integrity framework is merged?

Quoted 6 lines

All of the projects listed in that posting were ones depending on IMA, with no requirements for alternate modules. I do hope that there will be other integrity modules in addition to the TPM oriented IMA, and I do know of several research projects in this space, but I don't know if/when any of these are planning on submission. If others are submitted, it would certainly be simple to add the hooks back in.

Too bad the main trusted knoppix site appears to be dead. Was it actually making use of the templating api?

-serge

#21 lore raw

Re: [PATCH 2/6] integrity: Linux Integrity Module(LIM)

Quoted 25 lines · Serge E. Hallyn
Quoted 17 lines · david safford
Quoted 10 lines · James Morris
Quoted 3 lines · david safford

These hooks were for alternate integrity modules, and since no one else has defended them, we have to agree that they should be replaced with direct calls.

If you know of other modules which are planned to be ported to this framework, merged upstream and supported, then this would be similar to the situation when LSM was initially developed.

Quoted 5 lines

You've previously mentioned some active projects here:
http://lkml.org/lkml/2008/11/17/362

Are there any definite commitments to push these upstream when the integrity framework is merged?

Quoted 6 lines

All of the projects listed in that posting were ones depending on IMA, with no requirements for alternate modules. I do hope that there will be other integrity modules in addition to the TPM oriented IMA, and I do know of several research projects in this space, but I don't know if/when any of these are planning on submission. If others are submitted, it would certainly be simple to add the hooks back in.

Quoted 4 lines

Too bad the main trusted knoppix site appears to be dead. Was it actually making use of the templating api?

-serge

I believe they are using an older version of IMA without templating, but either way really shouldn't affect them.

dave

author
#22 lore raw

PATCH 3/6 integrity: IMA as an integrity service provider

In reply to Mimi Zohar

Based on suggestions on the mailing list:

- Due to the large patch size, IMA is broken up into three smaller patches:
ima-service-provider, ima-display, and ima-policy.
- Replaced skip_measurement() function with single S_ISREG() call.
- Simplified ima_calc_hash() and removed update_file_hash() by eliminating the 'path' parameter. Getting a 'file' handle is now done by the caller.
- Removed tiny wrappers ima_inode_alloc_integrity()/free_integrity(). Now calls ima_iint_alloc()/delete() directly.
- Removed all references to d_iname.
- Corrected ima_path_check() mask comparisons, removing MAY_APPEND.
- For readability, removed audit_type and lsm_type defines and usage.
- Replaced the generic 'struct ima_args_data' with integrity API function specific structures. (definitions in include/linux/ima.h and "ima.h")
- ima_store_measurement() now only forms an "ima" specific template measurement
- Defined a new integrity API call store_template().
- ima_store_template wraps a template specific measurement with a header
containing: template hash, template name, template data length and data.
- Replaced local iint_get/iint_put calls with kref calls.

IMA provides hardware (TPM) based measurement and attestation for both files and other types of template measurements. As the Trusted Computing (TPM) model requires, IMA measures all files before they are accessed in any way (on the bprm_check_integrity, path_check_integrity, and file_mmap hooks), and commits the measurements to the TPM. In addition, IMA maintains a list of these hash values, which can be used to validate the aggregate PCR value. The TPM can sign these measurements, and thus the system can prove to itself and to a third party these measurements in a way that cannot be circumvented by malicious or compromised software.

---

Documentation/kernel-parameters.txt +5 -0
index f98984e..aff7933 100644
@@ -44,6 +44,7 @@ parameter is applicable:
 	FB	The frame buffer device is enabled.
 	HW	Appropriate hardware is enabled.
 	IA-64	IA-64 architecture is enabled.
+	IMA     Integrity measurement architecture is enabled.
 	INTEGRITY Integrity support is enabled.
 	IOSCHED	More than one I/O scheduler is enabled.
 	IP_PNP	IP DHCP, BOOTP, or RARP is enabled.
@@ -880,6 +881,10 @@ and is between 256 and 4096 characters. It is defined in the file
 	ihash_entries=	[KNL]
 			Set number of hash buckets for inode cache.
 
+	ima_hash=	[IMA] runtime ability to define hash crypto algorithm.
+			Format: { "MD5" | "SHA1" }
+			Default is "SHA1".
+
 	in2000=		[HW,SCSI]
 			See header of drivers/scsi/in2000.c.
 
include/linux/ima.h new file +24 -0
new file mode 100644
index 0000000..6453100
@@ -0,0 +1,24 @@
+/*
+ * ima.h
+ *
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#ifndef _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+/* Store template measurements for use with ima_store_template() */
+struct ima_store_template_data {
+	char *name;
+	int len;
+	char *data;
+	int violation;
+	struct inode *inode;
+};
+
+#endif
security/Kconfig +2 -1
index 4c602be..665a1b7 100644
@@ -55,7 +55,8 @@ config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	help
 	  This will build the securityfs filesystem.  It is currently used by
-	  the TPM bios character driver.  It is not used by SELinux or SMACK.
+	  the TPM bios character driver and IMA, an integrity provider.  It
+	  is not used by SELinux or SMACK.
 
 	  If you are unsure how to answer this question, answer N.
 
security/integrity/Kconfig +2 -0
index 2123dfc..57213d6 100644
@@ -22,3 +22,5 @@ config INTEGRITY_AUDIT
 	  at boot.  If this option is selected, informational integrity
 	  auditing messages can be enabled with 'integrity_audit=1' on
 	  the kernel command line.
+
+source security/integrity/ima/Kconfig
security/integrity/Makefile +1 -0
index a6e1405..107dafd 100644
@@ -4,3 +4,4 @@
 
 # Object file lists
 obj-$(CONFIG_INTEGRITY) += integrity.o integrity_audit.o
+obj-$(CONFIG_IMA) += ima/
security/integrity/ima/Kconfig new file +34 -0
new file mode 100644
index 0000000..6c6fcd9
@@ -0,0 +1,34 @@
+#
+# IBM Integrity Measurement Architecture
+#
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on INTEGRITY
+	depends on ACPI
+	select SECURITYFS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select TCG_TPM
+	select TCG_TIS
+	help
+	  The Trusted Computing Group(TCG) runtime Integrity
+	  Measurement Architecture(IMA) maintains a list of hash
+	  values of executables and other sensitive system files
+	  loaded into the run-time of this system.  If your system
+	  has a TPM chip, then IMA also maintains an aggregate
+	  integrity value over this list inside the TPM hardware.
+	  These measurements and the aggregate (signed inside the
+	  TPM) can be retrieved and presented to remote parties to
+	  establish system properties. If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+	int "PCR for Aggregate (8 <= Index <= 14)"
+	depends on IMA
+	range 8 14
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.
security/integrity/ima/Makefile new file +9 -0
new file mode 100644
index 0000000..d9d4405
@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+obj-$(CONFIG_IMA) += ima.o
+
+ima-y := ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+	 ima_policy.o ima_iint.o
security/integrity/ima/ima.h new file +169 -0

169 lines not on this page · raw message

security/integrity/ima/ima_api.c new file +309 -0

309 lines not on this page · raw message

security/integrity/ima/ima_crypto.c new file +102 -0

102 lines not on this page · raw message

security/integrity/ima/ima_iint.c new file +71 -0

71 lines not on this page · raw message

security/integrity/ima/ima_init.c new file +90 -0

90 lines not on this page · raw message

security/integrity/ima/ima_main.c new file +274 -0

274 lines not on this page · raw message

security/integrity/ima/ima_policy.c new file +124 -0

124 lines not on this page · raw message

security/integrity/ima/ima_queue.c new file +125 -0

125 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#23 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 18 lines · Mimi Zohar

index 0000000..6c6fcd9

security/integrity/ima/Kconfig new file +14 -0
@@ -0,0 +1,34 @@
+#
+# IBM Integrity Measurement Architecture
+#
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on INTEGRITY
+	depends on ACPI
+	select SECURITYFS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select TCG_TPM
+	select TCG_TIS

security/integrity/ima/Kconfig line 24

Quoted 10 lines
security/integrity/ima/Kconfig new file +10 -0
+	help
+	  The Trusted Computing Group(TCG) runtime Integrity
+	  Measurement Architecture(IMA) maintains a list of hash
+	  values of executables and other sensitive system files
+	  loaded into the run-time of this system.  If your system
+	  has a TPM chip, then IMA also maintains an aggregate
+	  integrity value over this list inside the TPM hardware.
+	  These measurements and the aggregate (signed inside the
+	  TPM) can be retrieved and presented to remote parties to
+	  establish system properties. If unsure, say N.

This still doesn't tell me how it helps me. "If an attacker managed to change the contents of an important system file being measured, we can tell." Right?

security/integrity/ima/Kconfig line 34

Quoted 9 lines
security/integrity/ima/Kconfig new file +9 -0
+config IMA_MEASURE_PCR_IDX
+	int "PCR for Aggregate (8 <= Index <= 14)"
+	depends on IMA
+	range 8 14
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.

Why would you want to change this? Can it be done at runtime instead of compile time? I don't know what a PCR is. :(

security/integrity/ima/Kconfig

Quoted 8 lines
security/integrity/ima/Kconfig new file +8 -0
+#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)

Please don't. Can you use pr_debug() and friends?

security/integrity/ima/Kconfig

Quoted 2 lines
security/integrity/ima/Kconfig new file +2 -0
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE		20

When another algorithm (with a longer digest) is added, will we detect that without this just plain breaking?

security/integrity/ima/Kconfig

Quoted 13 lines
security/integrity/ima/Kconfig new file +13 -0
+struct ima_h_table {
+	atomic_long_t len;	/* number of stored measurements in the list */
+	atomic_long_t violations;
+	unsigned int max_htable_size;
+	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+	atomic_t queue_len[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long IMA_HASH_KEY(u8 *digest)
+{
+	return (hash_long(*digest, IMA_HASH_BITS));
+}

'return' isn't a function. :)

Also, please lower-case this sucker so we know it isn't a macro.

security/integrity/ima/Kconfig

Quoted 11 lines
security/integrity/ima/Kconfig new file +11 -0
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       char *op, char *cause)
+{
+	int result, namelen;
+	struct ima_inode_measure_entry measure_entry;
+	struct ima_store_template_data template = {
+		.name = "ima",
+		.len = sizeof(measure_entry),
+		.data = (char *)&measure_entry,
+		.violation = 1,
+	};

If '.data' is a char*, perhaps it should be a void*. If it already is a void*, you don't need a cast.

security/integrity/ima/Kconfig

Quoted 3 lines
security/integrity/ima/Kconfig new file +3 -0
+int ima_must_measure(void *data)
+{
+	struct ima_measure_data *mdata = (struct ima_measure_data *)data;

No need to cast a void*. You have several of these. Please find all of them and fix them up.

security/integrity/ima/Kconfig

Quoted 13 lines
security/integrity/ima/Kconfig new file +13 -0
+	struct ima_iint_cache *iint;
+	int must_measure = -EACCES;
+
+	if (!S_ISREG(mdata->inode->i_mode))
+		return -EPERM;
+	if ((mdata->mask & MAY_WRITE) || (mdata->mask & MAY_APPEND))
+		return -EPERM;
+
+	rcu_read_lock();
+	iint = ima_iint_lookup(mdata->inode);
+	if (iint)
+		kref_get(&iint->refcount);
+	rcu_read_unlock();

All of ima_iint_lookup()'s callers do the exact same thing. Please just make it ima_iint_find_get(), and do the RCU and kref_get() internally and once.

Quoted 11 lines
security/integrity/ima/Kconfig new file +11 -0
+	if (!iint) {
+		int rc;
+
+		/* Most insertions are done at inode_alloc,
+		 * except those allocated before late_initcall.
+		 * Can't initialize at security_initcall,
+		 * got to wait at least until proc_init.
+		 */
+		rc = ima_iint_insert(mdata->inode);
+		if (rc < 0)
+			return rc;

security/integrity/ima/Kconfig

Quoted 10 lines
security/integrity/ima/Kconfig new file +10 -0
+
+		rcu_read_lock();
+		iint = ima_iint_lookup(mdata->inode);
+		if (!iint) {
+			rcu_read_unlock();
+			return -ENOMEM;
+		}
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+	}

How about a retry goto instead of just copying the code again? Better yet, can you just stick all of this in a helper function?

security/integrity/ima/Kconfig

Quoted 6 lines
security/integrity/ima/Kconfig new file +6 -0
+int ima_iint_insert(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+	int rc = 0;
+
+	iint = kzalloc(sizeof(*iint), GFP_KERNEL);

Does this basically get done for every inode, or only special ones? I just wonder if having a dedicated slab with a constructor to do redundant things like mutex_init() would be helpful.

security/integrity/ima/Kconfig

Quoted 10 lines
security/integrity/ima/Kconfig new file +10 -0
+void ima_iint_delete(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+
+	spin_lock(&ima_iint_lock);
+	iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+	spin_unlock(&ima_iint_lock);
+	if (iint)
+		kref_put(&iint->refcount, iint_free);
+}

If you're looking for another way to split your patch out, you could just stick use inode->integrity for the first patch, then introduce your radix-tree in a subsequent patch. It would be functionally fine, and more clearly separate out the ideas.

Quoted 42 lines
security/integrity/ima/ima_init.c new file +36 -0
new file mode 100644
index 0000000..374b368
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer      <[email protected]>
+ * Leendert van Doorn <[email protected]>
+ * Mimi Zohar         <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_init.c
+ *             initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+static void ima_add_boot_aggregate(void)
+{
+	struct ima_inode_measure_entry measure_entry;
+	struct ima_store_template_data template = {
+		.name = "ima",
+		.len = sizeof(measure_entry),
+		.data = (char *)&measure_entry,
+	};
+	int namelen, result;
+
+	memset(&measure_entry, 0, sizeof measure_entry);
+	namelen = strlen(boot_aggregate_name);

security/integrity/ima/ima_init.c line 46

Quoted 10 lines
security/integrity/ima/ima_init.c new file +10 -0
+	if (namelen > IMA_EVENT_NAME_LEN_MAX)
+		namelen = IMA_EVENT_NAME_LEN_MAX;
+	memcpy(measure_entry.file_name, boot_aggregate_name, namelen);
+
+	if (ima_used_chip) {
+		int i;
+		u8 pcr_i[IMA_DIGEST_SIZE];
+		struct hash_desc desc;
+		struct crypto_hash *tfm;
+		struct scatterlist sg;

All of this stack stuff with very important, large sounding names makes me nervous. Can you reassure me?

security/integrity/ima/ima_init.c line 58

Quoted 11 lines
security/integrity/ima/ima_init.c new file +11 -0
+		tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+		if (!tfm || IS_ERR(tfm)) {
+			ima_error("error initializing digest.\n");
+			return;
+		}
+		desc.tfm = tfm;
+		desc.flags = 0;
+		crypto_hash_init(&desc);
+
+		/* cumulative sha1 over tpm registers 0-7 */
+		for (i = 0; i < 8; i++) {

Surely there's a NR_TPM_REGISTERS or similar somewhere.

Quoted 16 lines
security/integrity/ima/ima_init.c new file +16 -0
+			ima_pcrread(i, pcr_i, sizeof(pcr_i));
+			/* now accumulate with current aggregate */
+			sg_init_one(&sg, (u8 *) pcr_i, IMA_DIGEST_SIZE);
+			crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+		}
+		crypto_hash_final(&desc, measure_entry.digest);
+		crypto_free_hash(tfm);
+	} else
+		template.violation = 1;
+
+	/* now add measurement; if TPM bypassed, we have a ff..ff entry */
+	result = ima_store_template(&template);
+}
+
+int ima_init(void)
+{

security/integrity/ima/ima_init.c line 84

Quoted 10 lines
security/integrity/ima/ima_init.c new file +10 -0
+	int rc;
+
+	ima_used_chip = 0;
+	rc = tpm_pcr_read(IMA_TPM, 0, NULL);
+	if (rc == 0)
+		ima_used_chip = 1;
+
+	if (!ima_used_chip)
+		ima_info("No TPM chip found(rc = %d), activating TPM-bypass!\n",
+			 rc);

For the record, I think this is the kind of place that it's worth going over 80 chars.

Quoted 15 lines
security/integrity/ima/ima_init.c new file +15 -0
+static void ima_file_free(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (S_ISDIR(inode->i_mode))
+		return;
+	if ((file->f_mode & FMODE_WRITE) &&
+	    (atomic_read(&inode->i_writecount) == 1)) {
+		rcu_read_lock();
+		iint = ima_iint_lookup(inode);
+		if (!iint) {
+			rcu_read_unlock();
+			return;
+		}

security/integrity/ima/ima_init.c

Quoted 10 lines
security/integrity/ima/ima_init.c new file +10 -0
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+
+		mutex_lock(&iint->mutex);
+		if (iint->version != inode->i_version)
+			iint->flags &= ~IMA_MEASURED;
+		mutex_unlock(&iint->mutex);
+		kref_put(&iint->refcount, iint_free);
+	}
+}

I'm also wondering if there's a way to wrap up the mutex operations since this seems to be done the exact same way every time. Dunno, maybe it is too much locking obfuscation for just a few lines saved.

security/integrity/ima/ima_init.c

Quoted 11 lines
security/integrity/ima/ima_init.c new file +11 -0
+static int ima_path_check_integrity(struct path *path, int mask)
+{
+	struct ima_measure_data mdata;
+
+	memset(&mdata, 0, sizeof mdata);
+	mdata.inode = path->dentry->d_inode;
+	mdata.mask = mask;
+	mdata.function = PATH_CHECK;
+
+	/* Invalidate PCR, if a measured file is already open for read */
+	if ((mdata.mask & (MAY_WRITE | MAY_READ)) == MAY_WRITE) {

It would warm my heart to see something like this:

int mdata_is_write_only(struct ima_measure_data *mdata)

{
	if (mdata.mask & MAY_READ)
		return 0;
	return mdata.mask & MAY_WRITE;
}

I don't know about you, but I find that immeasurably nicer. Is it even right?

security/integrity/ima/ima_init.c

Quoted 13 lines
security/integrity/ima/ima_init.c new file +13 -0
+		int rc;
+
+		mdata.mask = MAY_READ;
+		rc = ima_must_measure(&mdata);
+		if (!rc || rc == -EEXIST) {
+			if (atomic_read(&(path->dentry->d_count)) - 1 >
+			    atomic_read(&(mdata.inode->i_writecount)))
+				ima_add_violation(mdata.inode,
+						  path->dentry->d_name.name,
+						  "invalid_pcr", "ToMToU");
+		}
+		return 0;
+	}

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

Could you please break this out into a separate function where you have some room to comment it and don't have to wrap it at 80 columns so badly?

Quoted 30 lines
security/integrity/ima/ima_init.c new file +30 -0
+	/* measure executables later */
+	if ((mdata.mask & (MAY_WRITE | MAY_READ)) == MAY_READ) {
+		int rc;
+
+		rc = ima_must_measure(&mdata);
+		if (!rc || rc == -EEXIST) {
+			/* Invalidate PCR, if a measured file is
+			 * already open for write.
+			 */
+			if (atomic_read(&(mdata.inode->i_writecount)) > 0)
+				ima_add_violation(mdata.inode,
+						  path->dentry->d_name.name,
+						  "invalid_pcr",
+						  "open_writers");
+		}
+		if (!rc) {
+			struct dentry *de = dget(path->dentry);
+			struct vfsmount *mnt = mntget(path->mnt);
+			struct file *file;
+
+			file = dentry_open(de, mnt, O_RDONLY);
+			if (IS_ERR(file)) {
+				ima_info("%s dentry_open failed\n",
+					 de->d_name.name);
+			} else if (file) {
+				rc = ima_collect_measurement(file);
+				if (!rc) {
+					struct ima_store_data sdata;
+
+					sdata.file = file;

security/integrity/ima/ima_init.c

Quoted 10 lines
security/integrity/ima/ima_init.c new file +10 -0
+					sdata.filename =
+					    file->f_dentry->d_name.name;
+					ima_store_measurement(&sdata);
+				}
+				fput(file);
+			}
+		}
+	}
+	return 0;
+}

I really think this hurts readability. I also see three callers to ima_collect_measurement() that do the exact same thing.

Mimi, I'm really getting the idea that this patch hasn't had much work put into its structure. I'm going to stop reviewing it now because I'm running into the same issues again and again and I think these issues are really slowing down my ability to look at it.

Please take a really, really hard look at these patches, all 3000 lines of it, and try to rework them. Find common bits of code that are duplicated or copy-n-pasted around. Find functions that have gotten too long and break them up. I bet you can knock a couple hundred lines of code off this sucker, easy.

If I've misunderstood things in your code, please go back and comment them. If I got confused, surely someone else could.

-- Dave

#24 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 4 lines · Dave Hansen

If you're looking for another way to split your patch out, you could just stick use inode->integrity for the first patch, then introduce your radix-tree in a subsequent patch. It would be functionally fine, and more clearly separate out the ideas.

This was done earlier and is a really bad thing for review. Just as the breakout happening in this round.

Folks, breaking out logical changes is good, but splitting new code for the sake of it is just a bloody stupid idea. It makes reviewing things much harder and makes the submitter to stupid work. If a single new driver really is large splitting the patch doesn't help.

Quoted 6 lines

security/integrity/ima/ima_init.c line 84

Quoted 3 lines
security/integrity/ima/ima_init.c new file +3 -0
+	if (!ima_used_chip)
+		ima_info("No TPM chip found(rc = %d), activating TPM-bypass!\n",
+			 rc);

For the record, I think this is the kind of place that it's worth going over 80 chars.

Or just don't bother printing the useless and ugly rc value and you're under it immediately..

Quoted 14 lines
Quoted 14 lines
security/integrity/ima/ima_main.c new file +14 -0
+	if ((file->f_mode & FMODE_WRITE) &&
+	    (atomic_read(&inode->i_writecount) == 1)) {
+		rcu_read_lock();
+		iint = ima_iint_lookup(inode);
+		if (!iint) {
+			rcu_read_unlock();
+			return;
+		}
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+
+		mutex_lock(&iint->mutex);
+		if (iint->version != inode->i_version)
+			iint->flags &= ~IMA_MEASURED;
Quoted 8 lines

security/integrity/ima/ima_api.c line 245

Quoted 4 lines
security/integrity/ima/ima_api.c new file +4 -0
+		mutex_unlock(&iint->mutex);
+		kref_put(&iint->refcount, iint_free);
+	}
+}

I'm also wondering if there's a way to wrap up the mutex operations since this seems to be done the exact same way every time. Dunno, maybe it is too much locking obfuscation for just a few lines saved.

Biggest problem here is the i_version checks. i_version is only updated for directories unless you're on ext4 and use an undocumented mount option..

Quoted 11 lines

security/integrity/ima/ima_main.c line 103

Quoted 3 lines
security/integrity/ima/ima_main.c new file +3 -0
+
+	/* Invalidate PCR, if a measured file is already open for read */
+	if ((mdata.mask & (MAY_WRITE | MAY_READ)) == MAY_WRITE) {

It would warm my heart to see something like this:

int mdata_is_write_only(struct ima_measure_data *mdata)

{
	if (mdata.mask & MAY_READ)
		return 0;
	return mdata.mask & MAY_WRITE;

Umm, no. The above is a perfectly fine idiom for testing flags in C. The helper would just obsfucated it.

Quoted 4 lines

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

I also haven't seen a good explanation for it yet.

Quoted 5 lines

Please take a really, really hard look at these patches, all 3000 lines of it, and try to rework them. Find common bits of code that are duplicated or copy-n-pasted around. Find functions that have gotten too long and break them up. I bet you can knock a couple hundred lines of code off this sucker, easy.

*nod* And change all the places that pass a pointer to a structure as a void pointer instead of a few normal paramters. That part really drives me nuts.

#25 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 8 lines · Christoph Hellwig
Quoted 4 lines · Dave Hansen

If you're looking for another way to split your patch out, you could just stick use inode->integrity for the first patch, then introduce your radix-tree in a subsequent patch. It would be functionally fine, and more clearly separate out the ideas.

This was done earlier and is a really bad thing for review. Just as the breakout happening in this round.

If Christoph and I tell you conflicting things, it goes without saying to listen to Christoph. :)

-- Dave

#26 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 7 lines · Dave Hansen
Quoted 6 lines · Christoph Hellwig
Quoted 4 lines · Dave Hansen

If you're looking for another way to split your patch out, you could just stick use inode->integrity for the first patch, then introduce your radix-tree in a subsequent patch. It would be functionally fine, and more clearly separate out the ideas.

Quoted 5 lines
Quoted 2 lines

This was done earlier and is a really bad thing for review. Just as the breakout happening in this round.

If Christoph and I tell you conflicting things, it goes without saying to listen to Christoph. :)

Or listen to what makes more sense. It's not the most important comment of the review anyway..

author
#27 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

In reply to Christoph Hellwig
Quoted 6 lines · Christoph Hellwig
Quoted 4 lines · Dave Hansen

If you're looking for another way to split your patch out, you could just stick use inode->integrity for the first patch, then introduce your radix-tree in a subsequent patch. It would be functionally fine, and more clearly separate out the ideas.

Quoted 7 lines

This was done earlier and is a really bad thing for review. Just as the breakout happening in this round.

Folks, breaking out logical changes is good, but splitting new code for the sake of it is just a bloody stupid idea. It makes reviewing things much harder and makes the submitter to stupid work. If a single new driver really is large splitting the patch doesn't help.

I'm not disagreeing, just letting you know that in this case, the first IMA patch builds cleanly and works, with each of the subsequent patches adding new functionality.

Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
security/integrity/ima/ima_main.c new file +14 -0
+	if ((file->f_mode & FMODE_WRITE) &&
+	    (atomic_read(&inode->i_writecount) == 1)) {
+		rcu_read_lock();
+		iint = ima_iint_lookup(inode);
+		if (!iint) {
+			rcu_read_unlock();
+			return;
+		}
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+
+		mutex_lock(&iint->mutex);
+		if (iint->version != inode->i_version)
+			iint->flags &= ~IMA_MEASURED;
Quoted 8 lines

security/integrity/ima/ima_api.c line 245

Quoted 4 lines
security/integrity/ima/ima_api.c new file +4 -0
+		mutex_unlock(&iint->mutex);
+		kref_put(&iint->refcount, iint_free);
+	}
+}

I'm also wondering if there's a way to wrap up the mutex operations since this seems to be done the exact same way every time. Dunno, maybe it is too much locking obfuscation for just a few lines saved.

Quoted 3 lines

Biggest problem here is the i_version checks. i_version is only updated for directories unless you're on ext4 and use an undocumented mount option..

hm, i_version seems to be working properly for regular files on ext3.

Quoted 7 lines
Quoted 5 lines

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

I also haven't seen a good explanation for it yet.

Previous posting:
"The OS protects against an executable file, already open for write, from being executed; and an executable file, open for execute, from being modified. In the same vein, we want to know that the file measured is the same file read, that it hasn't been modified. So, if a file already open for read is then opened for write, we log it with a "Time of Measure, Time of Use" error (ToMToU) and invalidate the PCR.....

This is important when measuring configuration files, shell scripts (not measured in brpm_check_integrity which are protected by the OS), and files imported/included by scripts."

Another posting:
"From an integrity perspective, a file measurement might be invalidated unnecessarily, but it is safe. For any file when opened for write, while having an existing reader, will cause the file measurement to be invalidated."

I'm just not seeing a problem. Perhaps because only regular files are being measured, and of them, only those defined by the policy, which most likely would not include pseudo filesystems (i.e. sysfs, procfs, tmpfs, securityfs).

Quoted 9 lines
Quoted 5 lines

Please take a really, really hard look at these patches, all 3000 lines of it, and try to rework them. Find common bits of code that are duplicated or copy-n-pasted around. Find functions that have gotten too long and break them up. I bet you can knock a couple hundred lines of code off this sucker, easy.

*nod* And change all the places that pass a pointer to a structure as a void pointer instead of a few normal paramters. That part really drives me nuts.

The parameters to the integrity API calls (collect, measure, store, etc) need to be generic (void *), as different templates measure different types of data, requiring different parameters.

As Dave Hansen, pointed out, the casting of void pointers is unnecessary. This will be cleaned up in the next patch set.

Mimi

#28 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 22 lines · Mimi Zohar
Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Dave Hansen

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

I also haven't seen a good explanation for it yet.

Previous posting:
"The OS protects against an executable file, already open for write, from being executed; and an executable file, open for execute, from being modified. In the same vein, we want to know that the file measured is the same file read, that it hasn't been modified. So, if a file already open for read is then opened for write, we log it with a "Time of Measure, Time of Use" error (ToMToU) and invalidate the PCR.....

This is important when measuring configuration files, shell scripts (not measured in brpm_check_integrity which are protected by the OS), and files imported/included by scripts."

Quoted 5 lines

Another posting:
"From an integrity perspective, a file measurement might be invalidated unnecessarily, but it is safe. For any file when opened for write, while having an existing reader, will cause the file measurement to be invalidated."

Those are all great explanations. But, some of that needs to get in the patch somehow. This is a subtle thing and someone looking at this a year for now is going to have difficulty understanding why it was done.

Quoted 4 lines

I'm just not seeing a problem. Perhaps because only regular files are being measured, and of them, only those defined by the policy, which most likely would not include pseudo filesystems (i.e. sysfs, procfs, tmpfs, securityfs).

There is no practical problem if you have false-positives on this check and do extra invalidations. But, I think both Christoph and I are nervous that this check is racy and there may be false-negatives and thus may miss some invalidations (which would be harmful).

The check is racy which is cause for concern by itself. But, with careful consideration, it may not be a dangerous or harmful race. Could you please consider it carefully and share some of your thoughts in a comment in the next version?

You need to check very, very carefully that there are no possible ways for i_writecount to be elevated without a corresponding elevation of d_count. I'm especially concerned as I look at some of the mmap() code. It appears that there are some temporary i_writecount elevations as VM_DENYWRITE is figured out. That needs some careful auditing to ensure it doesn't violate what is being assumed in the integrity code.

-- Dave

author
#29 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 48 lines · Dave Hansen
Quoted 22 lines · Mimi Zohar
Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Dave Hansen

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

I also haven't seen a good explanation for it yet.

Previous posting:
"The OS protects against an executable file, already open for write, from being executed; and an executable file, open for execute, from being modified. In the same vein, we want to know that the file measured is the same file read, that it hasn't been modified. So, if a file already open for read is then opened for write, we log it with a "Time of Measure, Time of Use" error (ToMToU) and invalidate the PCR.....

This is important when measuring configuration files, shell scripts (not measured in brpm_check_integrity which are protected by the OS), and files imported/included by scripts."

Quoted 5 lines

Another posting:
"From an integrity perspective, a file measurement might be invalidated unnecessarily, but it is safe. For any file when opened for write, while having an existing reader, will cause the file measurement to be invalidated."

Those are all great explanations. But, some of that needs to get in the patch somehow. This is a subtle thing and someone looking at this a year for now is going to have difficulty understanding why it was done.

Quoted 4 lines

I'm just not seeing a problem. Perhaps because only regular files are being measured, and of them, only those defined by the policy, which most likely would not include pseudo filesystems (i.e. sysfs, procfs, tmpfs, securityfs).

There is no practical problem if you have false-positives on this check and do extra invalidations. But, I think both Christoph and I are nervous that this check is racy and there may be false-negatives and thus may miss some invalidations (which would be harmful).

The check is racy which is cause for concern by itself. But, with careful consideration, it may not be a dangerous or harmful race. Could you please consider it carefully and share some of your thoughts in a comment in the next version?

Quoted 6 lines

You need to check very, very carefully that there are no possible ways for i_writecount to be elevated without a corresponding elevation of d_count. I'm especially concerned as I look at some of the mmap() code. It appears that there are some temporary i_writecount elevations as VM_DENYWRITE is figured out. That needs some careful auditing to ensure it doesn't violate what is being assumed in the integrity code.

The original IMA maintained its own readcount, but we thought the code would be simpler if we used existing kernel reference counts. Based on both Christoph's and your concerns, we probably should go back to maintaining our own readcount for user level files.

Mimi

author
#30 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

In reply to Dave Hansen
Quoted 25 lines · Dave Hansen
Quoted 24 lines · Mimi Zohar

index 0000000..6c6fcd9

security/integrity/ima/Kconfig new file +20 -0
@@ -0,0 +1,34 @@
+#
+# IBM Integrity Measurement Architecture
+#
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on INTEGRITY
+	depends on ACPI
+	select SECURITYFS
+	select CRYPTO
+	select CRYPTO_HMAC
+	select CRYPTO_MD5
+	select CRYPTO_SHA1
+	select TCG_TPM
+	select TCG_TIS
+	help
+	  The Trusted Computing Group(TCG) runtime Integrity
+	  Measurement Architecture(IMA) maintains a list of hash
+	  values of executables and other sensitive system files
+	  loaded into the run-time of this system.  If your system
+	  has a TPM chip, then IMA also maintains an aggregate
Quoted 8 lines

security/integrity/ima/Kconfig line 24

Quoted 4 lines
security/integrity/ima/Kconfig new file +4 -0
+	  integrity value over this list inside the TPM hardware.
+	  These measurements and the aggregate (signed inside the
+	  TPM) can be retrieved and presented to remote parties to
+	  establish system properties. If unsure, say N.

This still doesn't tell me how it helps me. "If an attacker managed to change the contents of an important system file being measured, we can tell." Right?

Yes, that is a clearer description. Thanks.

Quoted 4 lines
Quoted 4 lines
security/integrity/ima/Kconfig new file +4 -0
+config IMA_MEASURE_PCR_IDX
+	int "PCR for Aggregate (8 <= Index <= 14)"
+	depends on IMA
+	range 8 14
Quoted 8 lines

security/integrity/ima/Kconfig line 34

Quoted 5 lines
security/integrity/ima/Kconfig new file +5 -0
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.

Why would you want to change this? Can it be done at runtime instead of compile time? I don't know what a PCR is.

The only reason to change it would be if in the future, TCG decides on a standard PCR for IMA, other than 10, or if they pick 10 for something else. We really don't need a runtime variable for this, but kconfig makes it easy to change once if necessary in the future.

Quoted 10 lines

security/integrity/ima/Kconfig

Quoted 8 lines
security/integrity/ima/Kconfig new file +8 -0
+#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)

Please don't. Can you use pr_debug() and friends?

will clean this up.

Quoted 6 lines

security/integrity/ima/Kconfig

Quoted 2 lines
security/integrity/ima/Kconfig new file +2 -0
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE		20

When another algorithm (with a longer digest) is added, will we detect that without this just plain breaking?

As the kernel command line option "ima_hash=" verifies the crypto algorithm specified, changes to the code would be required to support a new algorithm anyway.

Quoted 16 lines

security/integrity/ima/Kconfig

Quoted 13 lines
security/integrity/ima/Kconfig new file +13 -0
+struct ima_h_table {
+	atomic_long_t len;	/* number of stored measurements in the list */
+	atomic_long_t violations;
+	unsigned int max_htable_size;
+	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+	atomic_t queue_len[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long IMA_HASH_KEY(u8 *digest)
+{
+	return (hash_long(*digest, IMA_HASH_BITS));
+}

'return' isn't a function. :)

Quoted 1 line

Also, please lower-case this sucker so we know it isn't a macro.

Ok

Quoted 15 lines

security/integrity/ima/Kconfig

Quoted 11 lines
security/integrity/ima/Kconfig new file +11 -0
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+		       char *op, char *cause)
+{
+	int result, namelen;
+	struct ima_inode_measure_entry measure_entry;
+	struct ima_store_template_data template = {
+		.name = "ima",
+		.len = sizeof(measure_entry),
+		.data = (char *)&measure_entry,
+		.violation = 1,
+	};

If '.data' is a char*, perhaps it should be a void*. If it already is a void*, you don't need a cast.

Quoted 6 lines

security/integrity/ima/Kconfig

Quoted 3 lines
security/integrity/ima/Kconfig new file +3 -0
+int ima_must_measure(void *data)
+{
+	struct ima_measure_data *mdata = (struct ima_measure_data *)data;

No need to cast a void*. You have several of these. Please find all of them and fix them up.

Thank you. will be in the next set patches.

Quoted 9 lines
Quoted 9 lines
security/integrity/ima/Kconfig new file +9 -0
+	struct ima_iint_cache *iint;
+	int must_measure = -EACCES;
+
+	if (!S_ISREG(mdata->inode->i_mode))
+		return -EPERM;
+	if ((mdata->mask & MAY_WRITE) || (mdata->mask & MAY_APPEND))
+		return -EPERM;
+
+	rcu_read_lock();
Quoted 8 lines

security/integrity/ima/Kconfig

Quoted 4 lines
security/integrity/ima/Kconfig new file +4 -0
+	iint = ima_iint_lookup(mdata->inode);
+	if (iint)
+		kref_get(&iint->refcount);
+	rcu_read_unlock();

All of ima_iint_lookup()'s callers do the exact same thing. Please just make it ima_iint_find_get(), and do the RCU and kref_get() internally and once.

cleaner, thanks.

Quoted 16 lines
Quoted 16 lines
security/integrity/ima/Kconfig new file +16 -0
+	if (!iint) {
+		int rc;
+
+		/* Most insertions are done at inode_alloc,
+		 * except those allocated before late_initcall.
+		 * Can't initialize at security_initcall,
+		 * got to wait at least until proc_init.
+		 */
+		rc = ima_iint_insert(mdata->inode);
+		if (rc < 0)
+			return rc;
+
+		rcu_read_lock();
+		iint = ima_iint_lookup(mdata->inode);
+		if (!iint) {
+			rcu_read_unlock();
Quoted 8 lines

security/integrity/ima/Kconfig

Quoted 5 lines
security/integrity/ima/Kconfig new file +5 -0
+			return -ENOMEM;
+		}
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+	}

How about a retry goto instead of just copying the code again? Better yet, can you just stick all of this in a helper function?

After the ima_iint_find_get() recommendation above, this becomes a lot smaller.

Quoted 10 lines

security/integrity/ima/Kconfig

Quoted 6 lines
security/integrity/ima/Kconfig new file +6 -0
+int ima_iint_insert(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+	int rc = 0;
+
+	iint = kzalloc(sizeof(*iint), GFP_KERNEL);

Does this basically get done for every inode, or only special ones? I just wonder if having a dedicated slab with a constructor to do redundant things like mutex_init() would be helpful.

every inode, except those allocated before init_latecall.

Quoted 17 lines
Quoted 17 lines
security/integrity/ima/Kconfig new file +17 -0
+static void ima_add_boot_aggregate(void)
+{
+	struct ima_inode_measure_entry measure_entry;
+	struct ima_store_template_data template = {
+		.name = "ima",
+		.len = sizeof(measure_entry),
+		.data = (char *)&measure_entry,
+	};
+	int namelen, result;
+
+	memset(&measure_entry, 0, sizeof measure_entry);
+	namelen = strlen(boot_aggregate_name);
+	if (namelen > IMA_EVENT_NAME_LEN_MAX)
+		namelen = IMA_EVENT_NAME_LEN_MAX;
+	memcpy(measure_entry.file_name, boot_aggregate_name, namelen);
+
+	if (ima_used_chip) {
Quoted 8 lines

security/integrity/ima/Kconfig

Quoted 5 lines
security/integrity/ima/Kconfig new file +5 -0
+		int i;
+		u8 pcr_i[IMA_DIGEST_SIZE];
+		struct hash_desc desc;
+		struct crypto_hash *tfm;
+		struct scatterlist sg;

All of this stack stuff with very important, large sounding names makes me nervous. Can you reassure me?

The crypto code here will be moved to ima_crypto.c and will be refactored, cleaning up the code. Both measure_entry and template could be allocated/freed each time, but does that make sense?

Quoted 5 lines
Quoted 5 lines
security/integrity/ima/Kconfig new file +5 -0
+		tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+		if (!tfm || IS_ERR(tfm)) {
+			ima_error("error initializing digest.\n");
+			return;
+		}
Quoted 8 lines

security/integrity/ima/Kconfig

Quoted 6 lines
security/integrity/ima/Kconfig new file +6 -0
+		desc.tfm = tfm;
+		desc.flags = 0;
+		crypto_hash_init(&desc);
+
+		/* cumulative sha1 over tpm registers 0-7 */
+		for (i = 0; i < 8; i++) {

Surely there's a NR_TPM_REGISTERS or similar somewhere.

Will look.

Quoted 21 lines
Quoted 21 lines
security/integrity/ima/Kconfig new file +21 -0
+static void ima_file_free(struct file *file)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	struct ima_iint_cache *iint;
+
+	if (S_ISDIR(inode->i_mode))
+		return;
+	if ((file->f_mode & FMODE_WRITE) &&
+	    (atomic_read(&inode->i_writecount) == 1)) {
+		rcu_read_lock();
+		iint = ima_iint_lookup(inode);
+		if (!iint) {
+			rcu_read_unlock();
+			return;
+		}
+		kref_get(&iint->refcount);
+		rcu_read_unlock();
+
+		mutex_lock(&iint->mutex);
+		if (iint->version != inode->i_version)
+			iint->flags &= ~IMA_MEASURED;
Quoted 8 lines

security/integrity/ima/Kconfig

Quoted 4 lines
security/integrity/ima/Kconfig new file +4 -0
+		mutex_unlock(&iint->mutex);
+		kref_put(&iint->refcount, iint_free);
+	}
+}

I'm also wondering if there's a way to wrap up the mutex operations since this seems to be done the exact same way every time. Dunno, maybe it is too much locking obfuscation for just a few lines saved.

Unlike the ima_iint_lookup(), the code within the mutex locking differs between calls.

Quoted 15 lines

security/integrity/ima/Kconfig

Quoted 11 lines
security/integrity/ima/Kconfig new file +11 -0
+static int ima_path_check_integrity(struct path *path, int mask)
+{
+	struct ima_measure_data mdata;
+
+	memset(&mdata, 0, sizeof mdata);
+	mdata.inode = path->dentry->d_inode;
+	mdata.mask = mask;
+	mdata.function = PATH_CHECK;
+
+	/* Invalidate PCR, if a measured file is already open for read */
+	if ((mdata.mask & (MAY_WRITE | MAY_READ)) == MAY_WRITE) {

It would warm my heart to see something like this:

int mdata_is_write_only(struct ima_measure_data *mdata)

Quoted 8 lines
{
	if (mdata.mask & MAY_READ)
		return 0;
	return mdata.mask & MAY_WRITE;
}

I don't know about you, but I find that immeasurably nicer. Is it even right?

In addition to MAY_READ/MAY_WRITE, there might be other flags.

Mimi

#31 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 14 lines · Mimi Zohar
Quoted 4 lines · Dave Hansen
Quoted 4 lines
security/integrity/ima/Kconfig new file +4 -0
+config IMA_MEASURE_PCR_IDX
+	int "PCR for Aggregate (8 <= Index <= 14)"
+	depends on IMA
+	range 8 14
Quoted 8 lines

security/integrity/ima/Kconfig line 34

Quoted 5 lines
security/integrity/ima/Kconfig new file +5 -0
+	default 10
+	help
+	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+	  that IMA uses to maintain the integrity aggregate of the
+	  measurement list.  If unsure, use the default 10.

Why would you want to change this? Can it be done at runtime instead of compile time? I don't know what a PCR is.

Quoted 4 lines

The only reason to change it would be if in the future, TCG decides on a standard PCR for IMA, other than 10, or if they pick 10 for something else. We really don't need a runtime variable for this, but kconfig makes it easy to change once if necessary in the future.

OK. Could you take out the prompt for now? You can use Kconfig for values that don't give user prompts. I just don't think it is something that people need to see.

in mm/Kconfig, for instance:

config NR_QUICK

        int
        depends on QUICKLIST
        default "2" if SUPERH || AVR32
        default "1"
Quoted 11 lines
Quoted 10 lines

security/integrity/ima/ima_iint.c line 35

Quoted 6 lines
security/integrity/ima/ima_iint.c new file +6 -0
+int ima_iint_insert(struct inode *inode)
+{
+	struct ima_iint_cache *iint;
+	int rc = 0;
+
+	iint = kzalloc(sizeof(*iint), GFP_KERNEL);

Does this basically get done for every inode, or only special ones? I just wonder if having a dedicated slab with a constructor to do redundant things like mutex_init() would be helpful.

Quoted 1 line

every inode, except those allocated before init_latecall.

I'd be willing to bet that you'll see a measurable performance improvement if you decide to use a slab here. All of the inodes for the different fs's use slabs and these are at least as common as any single fs's inode. Also, using the con/destructors will save some work at each object creation.

Quoted 26 lines
Quoted 17 lines
Quoted 17 lines
security/integrity/ima/ima_init.c new file +17 -0
+static void ima_add_boot_aggregate(void)
+{
+	struct ima_inode_measure_entry measure_entry;
+	struct ima_store_template_data template = {
+		.name = "ima",
+		.len = sizeof(measure_entry),
+		.data = (char *)&measure_entry,
+	};
+	int namelen, result;
+
+	memset(&measure_entry, 0, sizeof measure_entry);
+	namelen = strlen(boot_aggregate_name);
+	if (namelen > IMA_EVENT_NAME_LEN_MAX)
+		namelen = IMA_EVENT_NAME_LEN_MAX;
+	memcpy(measure_entry.file_name, boot_aggregate_name, namelen);
+
+	if (ima_used_chip) {
Quoted 8 lines

security/integrity/ima/ima_init.c line 46

Quoted 5 lines
security/integrity/ima/ima_init.c new file +5 -0
+		int i;
+		u8 pcr_i[IMA_DIGEST_SIZE];
+		struct hash_desc desc;
+		struct crypto_hash *tfm;
+		struct scatterlist sg;

All of this stack stuff with very important, large sounding names makes me nervous. Can you reassure me?

Quoted 3 lines

The crypto code here will be moved to ima_crypto.c and will be refactored, cleaning up the code. Both measure_entry and template could be allocated/freed each time, but does that make sense?

That's reassuring, thanks. :)

-- Dave

maintainer
#32 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

In reply to Dave Hansen
Quoted 12 lines · Dave Hansen
Quoted 12 lines
security/integrity/ima/ima_main.c new file +11 -0
+		int rc;
+
+		mdata.mask = MAY_READ;
+		rc = ima_must_measure(&mdata);
+		if (!rc || rc == -EEXIST) {
+			if (atomic_read(&(path->dentry->d_count)) - 1 >
+			    atomic_read(&(mdata.inode->i_writecount)))
+				ima_add_violation(mdata.inode,
+						  path->dentry->d_name.name,
+						  "invalid_pcr", "ToMToU");
+		}
Quoted 8 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+		return 0;
+	}

I have memories of talking about this bit. I was confused and you explained it to me, but it still isn't explained in the code. :( Again, I'm not convinced that this works. Can the code convince me, or should I go digging in my inbox?

This bit is crap, plain and simple. d_count doesn't work as a proxy for "how many times had we opened this file". At all.

a) stat(2) and just about anything else that looks funny at the pathname will bump d_count.

b) there may be several links to given file; all will share inode and have different dentries.

In other words, result of that comparison happens to be junk.

#33 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

In reply to Mimi Zohar

security/integrity/ima/Kconfig line 7

Quoted 4 lines · Mimi Zohar
security/integrity/ima/Kconfig new file +4 -0
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on INTEGRITY
+	depends on ACPI

ACPI? why?

Signature · 2 lines
-Len Brown
Intel Open Source Technology Center
author
#34 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

Quoted 9 lines · Len Brown

security/integrity/ima/Kconfig line 7

Quoted 4 lines · Mimi Zohar
security/integrity/ima/Kconfig new file +4 -0
+config IMA
+	bool "Integrity Measurement Architecture(IMA)"
+	depends on INTEGRITY
+	depends on ACPI

ACPI? why?

It's needed for accessing the TPM.

Mimi

#35 lore raw

Re: [PATCH 3/6] integrity: IMA as an integrity service provider

In reply to Mimi Zohar
Quoted 9 lines · Mimi Zohar

IMA provides hardware (TPM) based measurement and attestation for both files and other types of template measurements. As the Trusted Computing (TPM) model requires, IMA measures all files before they are accessed in any way (on the bprm_check_integrity, path_check_integrity, and file_mmap hooks), and commits the measurements to the TPM. In addition, IMA maintains a list of these hash values, which can be used to validate the aggregate PCR value. The TPM can sign these measurements, and thus the system can prove to itself and to a third party these measurements in a way that cannot be circumvented by malicious or compromised software.

I think this needs a bit of a plain-text explanation, sans acronyms. Perhaps a real-world example with files like /etc/passwd or /bin/sh would help me understand it better.

I'm still trying to wrap my head around the whole "invalidate something when there are both readers and writers around" situation. How does that fit into the description above? Does that tie into the guarantee that "IMA measures all files before they are accessed in any way"?

-- Dave

author
#36 lore raw

PATCH 4/6 integrity: IMA display

In reply to Mimi Zohar

Make the measurement lists available through securityfs.

---

security/integrity/ima/Makefile +1 -1
index d9d4405..959ae66 100644
@@ -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
security/integrity/ima/ima.h +5 -0
index b727a5e..795b552 100644
@@ -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 {
security/integrity/ima/ima_api.c +1 -0
index 83ab012..f2b53c7 100644
@@ -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
 };
 
 /**
security/integrity/ima/ima_fs.c new file +339 -0

339 lines not on this page · raw message

security/integrity/ima/ima_init.c +7 -1
index 374b368..de78f93 100644
@@ -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();
 }
security/integrity/ima/ima_main.c +1 -0
index 2dcc3b5..8672f86 100644
@@ -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 */
Signature · 1 line
1.5.6.5
author
#37 lore raw

PATCH 5/6 integrity: IMA policy

In reply to Mimi Zohar

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

---

Documentation/ABI/testing/ima_policy new file +61 -0
new file mode 100644
index 0000000..6434f0d
@@ -0,0 +1,61 @@
+What:		security/ima/policy
+Date:		May 2008
+Contact:	Mimi Zohar <[email protected]>
+Description:
+		The Trusted Computing Group(TCG) runtime Integrity
+		Measurement Architecture(IMA) maintains a list of hash
+		values of executables and other sensitive system files
+		loaded into the run-time of this system.  At runtime,
+		the policy can be constrained based on LSM specific data.
+		Policies are loaded into the securityfs file ima/policy
+		by opening the file, writing the rules one at a time and
+		then closing the file.  The new policy takes effect after
+		the file ima/policy is closed.
+
+		rule format: action [condition ...]
+
+		action: measure | dont_measure
+		condition:= base | lsm
+			base:	[[func=] [mask=] [fsmagic=] [uid=]]
+			lsm:	[[subj_user=] [subj_role=] [subj_type=]
+				 [obj_user=] [obj_role=] [obj_type=]]
+
+		base: 	func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+			mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+			fsmagic:= hex value
+			uid:= decimal value
+		lsm:  	are LSM specific
+
+		default policy:
+			# PROC_SUPER_MAGIC
+			dont_measure fsmagic=0x9fa0
+			# SYSFS_MAGIC
+			dont_measure fsmagic=0x62656572
+			# DEBUGFS_MAGIC
+			dont_measure fsmagic=0x64626720
+			# TMPFS_MAGIC
+			dont_measure fsmagic=0x01021994
+			# SECURITYFS_MAGIC
+			dont_measure fsmagic=0x73636673
+
+			measure func=BPRM_CHECK
+			measure func=FILE_MMAP mask=MAY_EXEC
+			measure func=INODE_PERM mask=MAY_READ uid=0
+
+		The default policy measures all executables in bprm_check,
+		all files mmapped executable in file_mmap, and all files
+		open for read by root in inode_permission.
+
+		Examples of LSM specific definitions:
+
+		SELinux:
+			# SELINUX_MAGIC
+			dont_measure fsmagic=0xF97CFF8C
+
+			dont_measure obj_type=var_log_t
+			dont_measure obj_type=auditd_log_t
+			measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+			measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+		Smack:
+			measure subj_user=_ func=INODE_PERM mask=MAY_READ
security/integrity/ima/Kconfig +7 -0
index 6c6fcd9..3d5ccdb 100644
@@ -32,3 +32,10 @@ config IMA_MEASURE_PCR_IDX
 	  IMA_MEASURE_PCR_IDX determines the TPM PCR register index
 	  that IMA uses to maintain the integrity aggregate of the
 	  measurement list.  If unsure, use the default 10.
+
+config IMA_LSM_RULES
+	bool "Enable LSM measurement policy rules"
+	depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK)
+	default y
+	help
+	  Disabling this option will not enforce LSM based policy rules.
security/integrity/ima/ima.h +22 -0
index 795b552..5bab990 100644
@@ -171,4 +171,26 @@ int ima_add_rule(int, char *subj_user, char *subj_role, char *subj_type,
 		 char *func, char *mask, char *fsmagic, char *uid);
 void ima_init_policy(void);
 void ima_update_policy(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+					    void **lsmrule)
+{
+	return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+					     void *lsmrule,
+					     struct audit_context *actx)
+{
+	return -EINVAL;
+}
+#endif
 #endif
security/integrity/ima/ima_fs.c +136 -1

164 lines not on this page · raw message

security/integrity/ima/ima_policy.c +207 -0

246 lines not on this page · raw message

Signature · 1 line
1.5.6.5
author
#38 lore raw

PATCH 6/6 integrity: replace task uid with cred uid

In reply to Mimi Zohar

This patch addresses the credential merge changes in the security-testing-2.6/next tree.

---

security/integrity/ima/ima_main.c +1 -1
index 8672f86..4b74197 100644
@@ -135,7 +135,7 @@ static int ima_path_check_integrity(struct path *path, int mask)
 			struct vfsmount *mnt = mntget(path->mnt);
 			struct file *file;
 
-			file = dentry_open(de, mnt, O_RDONLY);
+			file = dentry_open(de, mnt, O_RDONLY, current->cred);
 			if (IS_ERR(file)) {
 				ima_info("%s dentry_open failed\n",
 					 de->d_name.name);
security/integrity/ima/ima_policy.c +1 -1
index c887379..f803d1a 100644
@@ -92,7 +92,7 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
 	if ((rule->flags & IMA_FSMAGIC)
 	    && rule->fsmagic != inode->i_sb->s_magic)
 		return false;
-	if ((rule->flags & IMA_UID) && rule->uid != tsk->uid)
+	if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
 		return false;
 	for (i = 0; i < MAX_LSM_RULES; i++) {
 		int rc;
security/integrity/integrity_audit.c +1 -1
index 29518a6..142ba8b 100644
@@ -48,7 +48,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
 
 	ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
 	audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u",
-			 current->pid, current->uid,
+			 current->pid, current->cred->uid,
 			 audit_get_loginuid(current));
 	audit_log_task_context(ab);
 	switch (audit_msgno) {
Signature · 1 line
1.5.6.5