linux-crypto.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: torvalds@osdl.org, akpm@osdl.org, herbert.xu@redhat.com
Cc: linux-kernel@vger.kernel.org, davej@redhat.com,
	arjan@infradead.org, linux-crypto@vger.kernel.org,
	dhowells@redhat.com
Subject: [PATCH 4/6] MODSIGN: Module ELF verifier
Date: Wed, 14 Feb 2007 19:09:59 +0000	[thread overview]
Message-ID: <20070214190959.6438.64122.stgit@warthog.cambridge.redhat.com> (raw)
In-Reply-To: <20070214190938.6438.15091.stgit@warthog.cambridge.redhat.com>

Do preliminary verification of the ELF structure of a module.  This is used to
make sure that the ELF structure can then be used to check the module signature
and access the module data without breaking the module loader.

If the module's ELF metadata is determined to be bad, then ELIBBAD will be
returned and a message will be logged to the kernel log.

Signed-Off-By: David Howells <dhowells@redhat.com>
---

 init/Kconfig               |   11 ++
 kernel/Makefile            |    2 
 kernel/module-verify-elf.c |  304 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/module-verify.c     |   41 ++++++
 kernel/module-verify.h     |   53 ++++++++
 kernel/module.c            |    7 +
 6 files changed, 416 insertions(+), 2 deletions(-)

diff --git a/init/Kconfig b/init/Kconfig
index ad33c97..0013f45 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -553,6 +553,17 @@ config MODULE_SRCVERSION_ALL
 	  the version).  With this option, such a "srcversion" field
 	  will be created for all modules.  If unsure, say N.
 
+config MODULE_VERIFY_ELF
+	bool "Module ELF structure verification"
+	depends on MODULES
+	help
+	  Check ELF structure of modules upon load
+
+config MODULE_VERIFY
+	bool
+	depends on MODULES
+	default y if MODULE_VERIFY_ELF
+
 config KMOD
 	bool "Automatic kernel module loading"
 	depends on MODULES
diff --git a/kernel/Makefile b/kernel/Makefile
index 14f4d45..5ed0824 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -30,6 +30,8 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
 obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULE_VERIFY) += module-verify.o
+obj-$(CONFIG_MODULE_VERIFY_ELF) += module-verify-elf.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_PM) += power/
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
diff --git a/kernel/module-verify-elf.c b/kernel/module-verify-elf.c
new file mode 100644
index 0000000..6c4f1b1
--- /dev/null
+++ b/kernel/module-verify-elf.c
@@ -0,0 +1,304 @@
+/* module-verify-elf.c: module ELF verifier
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include "module-verify.h"
+
+#if 0
+#define _debug(FMT, ...) printk(FMT, ##__VA_ARGS__)
+#else
+#define _debug(FMT, ...) do {} while (0)
+#endif
+
+/*
+ * verify the ELF structure of a module
+ */
+int module_verify_elf(struct module_verify_data *mvdata)
+{
+	const Elf_Ehdr *hdr = mvdata->hdr;
+	const Elf_Shdr *section, *section2, *secstop;
+	const Elf_Rela *relas, *rela, *relastop;
+	const Elf_Rel *rels, *rel, *relstop;
+	const Elf_Sym *symbol, *symstop;
+	size_t size, sssize, *secsize, tmp, tmp2;
+	long last;
+	int line;
+
+	size = mvdata->size;
+	mvdata->nsects = hdr->e_shnum;
+
+#define elfcheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto elfcheck_error; } } while(0)
+
+#define seccheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto seccheck_error; } } while(0)
+
+#define symcheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto symcheck_error; } } while(0)
+
+#define relcheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto relcheck_error; } } while(0)
+
+#define relacheck(X)							\
+do { if (unlikely(!(X))) { line = __LINE__; goto relacheck_error; } } while(0)
+
+	/* validate the ELF header */
+	elfcheck(hdr->e_ehsize < size);
+	elfcheck(hdr->e_entry == 0);
+	elfcheck(hdr->e_phoff == 0);
+	elfcheck(hdr->e_phnum == 0);
+
+	elfcheck(hdr->e_shnum < SHN_LORESERVE);
+	elfcheck(hdr->e_shoff < size);
+	elfcheck(hdr->e_shoff >= hdr->e_ehsize);
+	elfcheck((hdr->e_shoff & (sizeof(long) - 1)) == 0);
+	elfcheck(hdr->e_shstrndx > 0);
+	elfcheck(hdr->e_shstrndx < hdr->e_shnum);
+	elfcheck(hdr->e_shentsize == sizeof(Elf_Shdr));
+
+	tmp = (size_t) hdr->e_shentsize * (size_t) hdr->e_shnum;
+	elfcheck(tmp <= size - hdr->e_shoff);
+
+	/* allocate a table to hold in-file section sizes */
+	mvdata->secsizes = kcalloc(hdr->e_shnum, sizeof(size_t), GFP_KERNEL);
+	if (!mvdata->secsizes)
+		return -ENOMEM;
+
+	/* validate the ELF section headers */
+	mvdata->sections = mvdata->buffer + hdr->e_shoff;
+	secstop = mvdata->sections + mvdata->nsects;
+
+	sssize = mvdata->sections[hdr->e_shstrndx].sh_size;
+	elfcheck(sssize > 0);
+
+	section = mvdata->sections;
+	seccheck(section->sh_type == SHT_NULL);
+	seccheck(section->sh_size == 0);
+	seccheck(section->sh_offset == 0);
+
+	secsize = mvdata->secsizes + 1;
+	for (section++; section < secstop; secsize++, section++) {
+		seccheck(section->sh_name < sssize);
+		seccheck(section->sh_link < hdr->e_shnum);
+
+		if (section->sh_entsize > 0)
+			seccheck(section->sh_size % section->sh_entsize == 0);
+
+		seccheck(section->sh_offset >= hdr->e_ehsize);
+		seccheck(section->sh_offset < size);
+
+		/* determine the section's in-file size */
+		tmp = size - section->sh_offset;
+		if (section->sh_offset < hdr->e_shoff)
+			tmp = hdr->e_shoff - section->sh_offset;
+
+		for (section2 = mvdata->sections + 1;
+		     section2 < secstop;
+		     section2++) {
+			if (section->sh_offset < section2->sh_offset) {
+				tmp2 = section2->sh_offset -
+					section->sh_offset;
+				if (tmp2 < tmp)
+					tmp = tmp2;
+			}
+		}
+		*secsize = tmp;
+
+		_debug("Section %ld: %zx bytes at %lx\n",
+		       section - mvdata->sections,
+		       *secsize,
+		       (unsigned long) section->sh_offset);
+
+		/* perform section type specific checks */
+		switch (section->sh_type) {
+		case SHT_NOBITS:
+			break;
+
+		case SHT_REL:
+			seccheck(section->sh_entsize == sizeof(Elf_Rel));
+			goto more_rel_checks;
+
+		case SHT_RELA:
+			seccheck(section->sh_entsize == sizeof(Elf_Rela));
+		more_rel_checks:
+			seccheck(section->sh_info > 0);
+			seccheck(section->sh_info < hdr->e_shnum);
+			goto more_sec_checks;
+
+		case SHT_SYMTAB:
+			seccheck(section->sh_entsize == sizeof(Elf_Sym));
+			goto more_sec_checks;
+
+		default:
+		more_sec_checks:
+			/* most types of section must be contained entirely
+			 * within the file */
+			seccheck(section->sh_size <= *secsize);
+			break;
+		}
+	}
+
+	/* validate the ELF section names */
+	section = &mvdata->sections[hdr->e_shstrndx];
+
+	seccheck(section->sh_offset != hdr->e_shoff);
+
+	mvdata->secstrings = mvdata->buffer + section->sh_offset;
+
+	last = -1;
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		const char *secname;
+		tmp = sssize - section->sh_name;
+		secname = mvdata->secstrings + section->sh_name;
+		seccheck(secname[0] != 0);
+		if (section->sh_name > last)
+			last = section->sh_name;
+	}
+
+	if (last > -1) {
+		tmp = sssize - last;
+		elfcheck(memchr(mvdata->secstrings + last, 0, tmp) != NULL);
+	}
+
+	/* look for various sections in the module */
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		switch (section->sh_type) {
+		case SHT_SYMTAB:
+			if (strcmp(mvdata->secstrings + section->sh_name,
+				   ".symtab") == 0
+			    ) {
+				seccheck(mvdata->symbols == NULL);
+				mvdata->symbols =
+					mvdata->buffer + section->sh_offset;
+				mvdata->nsyms =
+					section->sh_size / sizeof(Elf_Sym);
+				seccheck(section->sh_size > 0);
+			}
+			break;
+
+		case SHT_STRTAB:
+			if (strcmp(mvdata->secstrings + section->sh_name,
+				   ".strtab") == 0
+			    ) {
+				seccheck(mvdata->strings == NULL);
+				mvdata->strings =
+					mvdata->buffer + section->sh_offset;
+				sssize = mvdata->nstrings = section->sh_size;
+				seccheck(section->sh_size > 0);
+			}
+			break;
+		}
+	}
+
+	if (!mvdata->symbols) {
+		printk("Couldn't locate module symbol table\n");
+		goto format_error;
+	}
+
+	if (!mvdata->strings) {
+		printk("Couldn't locate module strings table\n");
+		goto format_error;
+	}
+
+	/* validate the symbol table */
+	symstop = mvdata->symbols + mvdata->nsyms;
+
+	symbol = mvdata->symbols;
+	symcheck(ELF_ST_TYPE(symbol[0].st_info) == STT_NOTYPE);
+	symcheck(symbol[0].st_shndx == SHN_UNDEF);
+	symcheck(symbol[0].st_value == 0);
+	symcheck(symbol[0].st_size == 0);
+
+	last = -1;
+	for (symbol++; symbol < symstop; symbol++) {
+		symcheck(symbol->st_name < sssize);
+		if (symbol->st_name > last)
+			last = symbol->st_name;
+		symcheck(symbol->st_shndx < mvdata->nsects ||
+			 symbol->st_shndx >= SHN_LORESERVE);
+	}
+
+	if (last > -1) {
+		tmp = sssize - last;
+		elfcheck(memchr(mvdata->strings + last, 0, tmp) != NULL);
+	}
+
+	/* validate each relocation table as best we can */
+	for (section = mvdata->sections + 1; section < secstop; section++) {
+		section2 = mvdata->sections + section->sh_info;
+
+		switch (section->sh_type) {
+		case SHT_REL:
+			rels = mvdata->buffer + section->sh_offset;
+			relstop = mvdata->buffer +
+				section->sh_offset + section->sh_size;
+
+			for (rel = rels; rel < relstop; rel++) {
+				relcheck(rel->r_offset < section2->sh_size);
+				relcheck(ELF_R_SYM(rel->r_info) <
+					 mvdata->nsyms);
+			}
+
+			break;
+
+		case SHT_RELA:
+			relas = mvdata->buffer + section->sh_offset;
+			relastop = mvdata->buffer +
+				section->sh_offset + section->sh_size;
+
+			for (rela = relas; rela < relastop; rela++) {
+				relacheck(rela->r_offset < section2->sh_size);
+				relacheck(ELF_R_SYM(rela->r_info) <
+					  mvdata->nsyms);
+			}
+
+			break;
+
+		default:
+			break;
+		}
+	}
+
+
+	_debug("ELF okay\n");
+	return 0;
+
+elfcheck_error:
+	printk("Verify ELF error (assertion %d)\n", line);
+	goto format_error;
+
+seccheck_error:
+	printk("Verify ELF error [sec %ld] (assertion %d)\n",
+	       (long)(section - mvdata->sections), line);
+	goto format_error;
+
+symcheck_error:
+	printk("Verify ELF error [sym %ld] (assertion %d)\n",
+	       (long)(symbol - mvdata->symbols), line);
+	goto format_error;
+
+relcheck_error:
+	printk("Verify ELF error [sec %ld rel %ld] (assertion %d)\n",
+	       (long)(section - mvdata->sections),
+	       (long)(rel - rels), line);
+	goto format_error;
+
+relacheck_error:
+	printk("Verify ELF error [sec %ld rela %ld] (assertion %d)\n",
+	       (long)(section - mvdata->sections),
+	       (long)(rela - relas), line);
+	goto format_error;
+
+format_error:
+	return -ELIBBAD;
+}
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644
index 0000000..875279f
--- /dev/null
+++ b/kernel/module-verify.c
@@ -0,0 +1,41 @@
+/* module-verify.c: module verifier
+ *
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "module-verify.h"
+
+/*
+ * verify a module's integrity
+ * - check the ELF is viable
+ */
+int module_verify(const Elf_Ehdr *hdr, size_t size)
+{
+	struct module_verify_data mvdata;
+	int ret;
+
+	memset(&mvdata, 0, sizeof(mvdata));
+	mvdata.buffer	= hdr;
+	mvdata.hdr	= hdr;
+	mvdata.size	= size;
+
+	ret = module_verify_elf(&mvdata);
+	if (ret < 0) {
+		if (ret == -ELIBBAD)
+			printk("Module failed ELF checks\n");
+		goto error;
+	}
+
+error:
+	kfree(mvdata.secsizes);
+	kfree(mvdata.canonlist);
+	return ret;
+}
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
new file mode 100644
index 0000000..63f5e08
--- /dev/null
+++ b/kernel/module-verify.h
@@ -0,0 +1,53 @@
+/* module-verify.h: module verification definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <asm/module.h>
+
+#ifdef CONFIG_MODULE_VERIFY
+struct module_verify_data {
+	struct crypto_tfm	*digest;	/* module signature digest */
+	const void		*buffer;	/* module buffer */
+	const Elf_Ehdr		*hdr;		/* ELF header */
+	const Elf_Shdr		*sections;	/* ELF section table */
+	const Elf_Sym		*symbols;	/* ELF symbol table */
+	const char		*secstrings;	/* ELF section string table */
+	const char		*strings;	/* ELF string table */
+	size_t			*secsizes;	/* section size list */
+	size_t			size;		/* module object size */
+	size_t			nsects;		/* number of sections */
+	size_t			nsyms;		/* number of symbols */
+	size_t			nstrings;	/* size of strings section */
+	size_t			signed_size;	/* count of bytes contributed to digest */
+	int			*canonlist;	/* list of canonicalised sections */
+	int			*canonmap;	/* section canonicalisation map */
+	int			sig_index;	/* module signature section index */
+	uint8_t			xcsum;		/* checksum of bytes contributed to digest */
+	uint8_t			csum;		/* checksum of bytes representing a section */
+};
+
+/*
+ * module-verify.c
+ */
+extern int module_verify(const Elf_Ehdr *hdr, size_t size);
+
+/*
+ * module-verify-elf.c
+ */
+#ifdef CONFIG_MODULE_VERIFY_ELF
+extern int module_verify_elf(struct module_verify_data *mvdata);
+#else
+#define module_verify_elf(m) (0)
+#endif
+
+#else
+#define module_verify(h, s) (0)
+#endif
diff --git a/kernel/module.c b/kernel/module.c
index 8a94e05..86abdce 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -44,6 +44,7 @@
 #include <asm/semaphore.h>
 #include <asm/cacheflush.h>
 #include <linux/license.h>
+#include "module-verify.h"
 
 #if 0
 #define DEBUGP printk
@@ -1605,8 +1606,10 @@ static struct module *load_module(void __user *umod,
 		goto free_hdr;
 	}
 
-	if (len < hdr->e_shoff + hdr->e_shnum * sizeof(Elf_Shdr))
-		goto truncated;
+	/* Verify the module's contents */
+	err = module_verify(hdr, len);
+	if (err < 0)
+		goto free_hdr;
 
 	/* Convenience variables */
 	sechdrs = (void *)hdr + hdr->e_shoff;

  parent reply	other threads:[~2007-02-14 19:09 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-02-14 19:09 [PATCH 0/6] MODSIGN: Kernel module signing David Howells
2007-02-14 19:09 ` [PATCH 2/6] MODSIGN: In-kernel crypto extensions David Howells
2007-02-14 19:09 ` [PATCH 3/6] MODSIGN: Add indications of module ELF types David Howells
2007-02-14 19:09 ` David Howells [this message]
2007-02-14 19:10 ` [PATCH 5/6] MODSIGN: Module signature checker and key manager David Howells
2007-02-14 19:10 ` [PATCH 6/6] MODSIGN: Apply signature checking to modules on module load David Howells
2007-02-14 19:26 ` [PATCH 0/6] MODSIGN: Kernel module signing Linus Torvalds
2007-02-14 19:40 ` David Howells
2007-02-14 21:32   ` Michael Halcrow
2007-02-14 21:59   ` David Howells
2007-02-14 22:21     ` Michael Halcrow
2007-02-15 21:31   ` Indan Zupancic
2007-02-15  3:41 ` Andrew Morton
2007-02-15  4:13   ` Dave Jones
2007-02-15  5:35     ` Andreas Gruenbacher
2007-02-15  5:45       ` Dave Jones
2007-02-15  6:14         ` Andreas Gruenbacher
2007-02-15  6:22           ` Dave Jones
2007-02-15 20:34           ` Valdis.Kletnieks
2007-02-15 22:12             ` Andreas Gruenbacher
2007-02-16  0:15               ` Olaf Kirch
2007-02-15 22:10           ` Pavel Machek
2007-02-15 20:55     ` Valdis.Kletnieks
2007-02-15 21:32       ` Adrian Bunk
2007-02-15 22:12         ` Valdis.Kletnieks
2007-02-15 14:35 ` Roman Zippel
2007-02-15 17:32 ` David Howells
2007-02-15 18:33   ` Roman Zippel
2007-02-15 20:01     ` David Lang
2007-02-15 21:01       ` Roman Zippel
2007-02-15 21:03 ` Adrian Bunk
2007-02-15 22:13 ` Pavel Machek
2007-02-16 20:21   ` Dave Jones
2007-02-16 20:27     ` Arjan van de Ven

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070214190959.6438.64122.stgit@warthog.cambridge.redhat.com \
    --to=dhowells@redhat.com \
    --cc=akpm@osdl.org \
    --cc=arjan@infradead.org \
    --cc=davej@redhat.com \
    --cc=herbert.xu@redhat.com \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@osdl.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).