Subject: module: simple signature support.
From: Rusty Russell <[email-masked]>

A signature contains a magic marker: it signs everything up to the
magic marker (ie. just append them):
	SUM=`md5sum drivers/block/loop.ko | cut -d\  -f1`; echo "@Module signature:$SUM" >> drivers/block/loop.ko

We can have false positives, but at worst that make us report EINVAL
(bad signature) instead of ENOENT (no signature).
---
 kernel/module.c |   97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/kernel/module.c b/kernel/module.c
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2374,6 +2374,98 @@ free_hdr:
 	return err;
 }
 
+/* CONFIG_MODULE_SIGN implies we don't trust modules: verify signature
+ * before we interpret (almost) anything. */
+#define MOD_SIGNATURE "@Module signature:"
+
+#include <linux/ctype.h>
+#include <crypto/hash.h>
+#include <crypto/md5.h>
+
+static int from_hex(char c)
+{
+	if (isdigit(c))
+		return c - '0';
+	if (isupper(c))
+		return c - 'A' + 10;
+	return c - 'a' + 10;
+}
+
+/* A signature signs everything before it. */
+static int try_signature(void *data, void *sig, unsigned long max_sig)
+{
+	unsigned long data_len = sig - data;
+
+	sig += strlen(MOD_SIGNATURE);
+	max_sig -= strlen(MOD_SIGNATURE);
+
+	/* Dummy: accept md5 as signature. */
+	{
+		struct crypto_api_blows {
+			struct shash_desc md5;
+			char morestuff[100];
+		} m;
+		u8 digest[MD5_DIGEST_SIZE], expected[MD5_DIGEST_SIZE];
+		char *s = sig;
+		int i;
+
+		/* Not a signature? */
+		if (max_sig < MD5_DIGEST_SIZE * 2) {
+			printk("Too close to end (%lu)\n", max_sig);
+			return -ENOENT;
+		}
+
+		for (i = 0; i < MD5_DIGEST_SIZE * 2; i += 2) {
+			/* Not a signature? */
+			if (!isxdigit(s[i]) || !isxdigit(s[i+1])) {
+				printk("Not hex digit (%c)\n", s[i]);
+				return -ENOENT;
+			}
+			digest[i/2] = (from_hex(s[i])<<4) | from_hex(s[i+1]);
+		}
+
+		m.md5.tfm = crypto_alloc_shash("md5", 0, 0);
+		if (IS_ERR(m.md5.tfm))
+			return PTR_ERR(m.md5.tfm);
+		m.md5.flags = 0;
+
+		crypto_shash_digest(&m.md5, data, data_len, expected);
+		crypto_free_shash(m.md5.tfm);
+		
+		if (memcmp(digest, expected, sizeof(digest)) != 0) {
+			printk("Mismatch: given %02x%02x%02x...,"
+			       " expect %02x%02x%02x...\n",
+			       digest[0], digest[1], digest[2],
+			       expected[0], expected[1], expected[2]);
+			return -EINVAL;
+		}
+		printk("Found valid signature!\n");
+		return 0;
+	}
+}
+
+/* -ENOENT if no signature found, -EINVAL if invalid, 0 if good. */
+static int find_and_check_signatures(struct load_info *info)
+{
+	void *p = info->hdr, *end = p + info->len;
+	const size_t sigsize = strlen(MOD_SIGNATURE);
+	int err = -ENOENT;
+
+	/* Poor man's memmem. len > sigsize */
+	while ((p = memchr(p, MOD_SIGNATURE[0], end - p))) {
+		if (p + sigsize > end)
+			break;
+
+		if (memcmp(p, MOD_SIGNATURE, sigsize) == 0) {
+			err = try_signature(info->hdr, p, end - p);
+			if (!err)
+				break;
+		}
+		p++;
+	}
+	return err;
+}
+
 static void free_copy(struct load_info *info)
 {
 	vfree(info->hdr);
@@ -2819,6 +2911,11 @@ static struct module *load_module(void _
 	if (err)
 		return ERR_PTR(err);
 
+	/* Before we trust it, carefully check signatures. */
+	err = find_and_check_signatures(&info);
+	if (err)
+		goto free_copy;
+
 	/* Figure out module layout, and allocate all the memory. */
 	mod = layout_and_allocate(&info);
 	if (IS_ERR(mod)) {