]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
Apply signature checking to modules on module load
authorMaxim Uvarov <maxim.uvarov@oracle.com>
Tue, 16 Aug 2011 18:19:55 +0000 (11:19 -0700)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Tue, 11 Oct 2011 18:31:54 +0000 (11:31 -0700)
Apply signature checking to modules on module load, checking the signature
against the ring of public keys compiled into the kernel (if enabled by
CONFIG_MODULE_SIG).  Turning on signature checking will also force the module's
ELF metadata to be verified first.

These patches have been in use by RHEL and Fedora kernels for years, and so
have been thoroughly tested.  The signed modules survive both the debuginfo
separation performed by rpmbuild and the strip performed when modules are being
reduced as much as possible before being included in an initial ramdisk
composition.  Signed modules have been tested to work with LE and BE, 32- and
64-bit arch kernels, including i386, x86_64, ppc64, ia64, s390 and s390x.

There are several reasons why these patches are useful, amongst which are:

 (1) to protect against accidentally-corrupted modules causing damage;

 (2) to protect against maliciously modified modules causing damage;

 (3) to allow a sysadmin (or more likely an IT department) to enforce a policy
     that only known and approved modules shall be loaded onto machines which
     they're expected to support;

 (4) to allow other support providers to do likewise, or at least to _detect_
     the fact that unsupported modules are loaded;

 (5) to allow the detection of modules replaced by a second-order distro or a
     preloaded Linux purveyor.

Basically, these patches have two main appeals to me: (a) preventing malicious
modules from being loaded, and (b) reducing support workload by pointing out
modules on a crashing box that aren't what they're expected to be.

Now, this is not a complete solution by any means: the core kernel is not
protected, and nor are /dev/mem or /dev/kmem, but it denies (or at least
controls) one relatively simple attack vector.

This facility is optional: the builder of a kernel is by no means under any
requirement to actually enable it, let alone force the set of loadable modules
to be restricted to just those that the builder provides (there are degrees of
restriction available).

Use of the module signing facility is documentated in:

Documentation/module-signing.txt

which I've included here for reference:

==============================
KERNEL MODULE SIGNING FACILITY
==============================

The module signing facilitiy applies cryptographic signature checking to
modules on module load, checking the signature against a ring of public keys
compiled into the kernel.  GPG is used to do the cryptographic work and
determines the format of the signature and key data.  The facility uses GPG's
MPI library to handle the huge numbers involved.

This facility is enabled through CONFIG_MODULE_SIG.  Turning on signature
checking will also force the module's ELF metadata to be verified before the
signature is checked.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Aristeu Rozanski <arozansk@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Signed-off-by: Maxim Uvarov <maxim.uvarov@oracle.com>
17 files changed:
Documentation/module-signing.txt [new file with mode: 0644]
Makefile
include/linux/elfnote.h
include/linux/modsign.h [new file with mode: 0644]
include/linux/module-verify-elf.h [new file with mode: 0644]
include/linux/module-verify.h [new file with mode: 0644]
include/linux/module.h
init/Kconfig
kernel/Makefile
kernel/module-verify-elf.c [new file with mode: 0644]
kernel/module-verify-sig.c [new file with mode: 0644]
kernel/module-verify.c [new file with mode: 0644]
kernel/module.c
scripts/Makefile.modpost
scripts/mod/Makefile
scripts/mod/mod-extract.c [new file with mode: 0644]
scripts/mod/modsign-note.sh [new file with mode: 0644]

diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
new file mode 100644 (file)
index 0000000..ae7c4f9
--- /dev/null
@@ -0,0 +1,147 @@
+                       ==============================
+                       KERNEL MODULE SIGNING FACILITY
+                       ==============================
+
+The module signing facility applies cryptographic signature checking to modules
+on module load, checking the signature against a ring of public keys compiled
+into the kernel.  GPG is used to do the cryptographic work and determines the
+format of the signature and key data.  The facility uses GPG's MPI library to
+handle the huge numbers involved.
+
+This facility is enabled through CONFIG_MODULE_SIG.  Turning on signature
+checking will also force the module's ELF metadata to be verified before the
+signature is checked.
+
+
+=====================
+SUPPLYING PUBLIC KEYS
+=====================
+
+A set of public keys must be supplied at main kernel compile time.  This is
+done by taking a GPG public key file, running it through the kernel's bin2c
+program and writing the result over crypto/signature/key.h.  To automate this
+process, something like this could be done:
+
+       cat >genkey <<EOF
+       %pubring kernel.pub
+       %secring kernel.sec
+       Key-Type: DSA
+       Key-Length: 512
+       Name-Real: A. N. Other
+       Name-Comment: Kernel Module GPG key
+       %commit
+       EOF
+       make scripts/bin2c
+       gpg --homedir . --batch --gen-key genkey
+       gpg --homedir . --export --keyring kernel.pub keyname |
+        scripts/bin2c ksign_def_public_key __initdata >crypto/signature/key.h
+
+The above generates fresh keys using /dev/random.  If there's insufficient data
+in /dev/random, more can be provided more by running:
+
+       rngd -r /dev/urandom
+
+in the background.
+
+Note:
+
+ (1) That "keyname" is the name of the key in the keyring.  This differentiates
+     it from any other keys that may be added to the keyring.
+
+ (2) That no GPG password is used in the above scriptlet.
+
+
+==============
+MODULE SIGNING
+==============
+
+Modules will then be signed automatically.  The kernel make command line can
+include the following options:
+
+ (*) MODSECKEY=<secret-key-ring-path>
+
+     This indicates the whereabouts of the GPG keyring that is the source of
+     the secret key to be used.  The default is "./kernel.sec".
+
+ (*) MODPUBKEY=<public-key-ring-path>
+
+     This indicates the whereabouts of the GPG keyring that is the source of
+     the public key to be used.  The default is "./kernel.pub".
+
+ (*) MODKEYNAME=<key-name>
+
+     The name of the key pair to be used from the aforementioned keyrings.
+     This defaults to being unset, thus leaving the choice of default key to
+     gpg.
+
+ (*) KEYFLAGS="gpg-options"
+
+     Override the complete gpg command line, including the preceding three
+     options.  The default options supplied to gpg are:
+
+       --no-default-keyring
+       --secret-keyring $(MODSECKEY)
+       --keyring $(MODPUBKEY)
+       --no-default-keyring
+       --homedir .
+       --no-options
+       --no-auto-check-trustdb
+       --no-permission-warning
+
+      with:
+
+       --default-key $(MODKEYNAME)
+
+      being added if requested.
+
+The resulting module.ko file will be the signed module.
+
+
+========================
+STRIPPING SIGNED MODULES
+========================
+
+Signed modules may be safely stripped as the signature only covers those parts
+of the module the kernel actually uses and any ELF metadata required to deal
+with them.  Any necessary ELF metadata that is affected by stripping is
+canonicalised by the sig generator and the sig checker to hide strip effects.
+
+This permits the debuginfo to be detached from the module and placed in another
+spot so that gdb can find it when referring to that module without the need for
+multiple signed versions of the module.  Such is done by rpmbuild when
+producing RPMs.
+
+It also permits the module to be stripped as far as possible for when modules
+are being reduced prior to being included in an initial ramdisk composition.
+
+
+======================
+LOADING SIGNED MODULES
+======================
+
+Modules are loaded with insmod, exactly as for unsigned modules.  The signature
+is inserted into the module object file as an ELF section called ".module_sig".
+The signature checker will spot it and apply signature checking.
+
+
+=========================================
+NON-VALID SIGNATURES AND UNSIGNED MODULES
+=========================================
+
+If CONFIG_MODULE_SIG_FORCE is enabled or "enforcemodulesig=1" is supplied on
+the kernel command line, the kernel will _only_ load validly signed modules
+for which it has a public key.  Otherwise, it will also load modules that are
+unsigned.  Any module for which the kernel has a key, but which proves to have
+a signature mismatch will not be permitted to load (returning EKEYREJECTED).
+
+This table indicates the behaviours of the various situations:
+
+       MODULE STATE                    PERMISSIVE MODE ENFORCING MODE
+       =============================== =============== ===============
+       Unsigned                        Ok              EKEYREJECTED
+       Signed, no public key           ENOKEY          ENOKEY
+       Validly signed, public key      Ok              Ok
+       Invalidly signed, public key    EKEYREJECTED    EKEYREJECTED
+       Validly signed, expired key     EKEYEXPIRED     EKEYEXPIRED
+       Corrupt signature               ELIBBAD         ELIBBAD
+       Corrupt ELF                     ELIBBAD         ELIBBAD
index ec5fd701ded68c1be058b2598f7040ca22eb8457..7b00eac0ff1a891d16e02200d161f04b1a622c4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1383,8 +1383,9 @@ $(clean-dirs):
        $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
 
 clean: rm-dirs := $(MODVERDIR)
-clean: rm-files := $(KBUILD_EXTMOD)/Module.symvers
-
+clean: rm-files := $(KBUILD_EXTMOD)/Module.symvers \
+                   $(KBUILD_EXTMOD)/Module.markers \
+                   $(KBUILD_EXTMOD)/modules.order
 help:
        @echo  '  Building external modules.'
        @echo  '  Syntax: make -C path/to/kernel/src M=$$PWD target'
@@ -1404,7 +1405,7 @@ clean: $(clean-dirs)
        $(call cmd,rmdirs)
        $(call cmd,rmfiles)
        @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
-               \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+               \( -name '*.[oas]' -o -name '*.ko' -o -name '*.ko.*' -o -name '.*.cmd' \
                -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
                -o -name '*.symtypes' -o -name 'modules.order' \
                -o -name modules.builtin -o -name '.tmp_*.o.*' \
index 278e3ef0533699f2e9a3845bdc12715ce6be30cb..4d5c5d56093f7bde8ee1547e69fd3089150dfbda 100644 (file)
@@ -93,6 +93,9 @@
 
 #define ELFNOTE32(name, type, desc) ELFNOTE(32, name, type, desc)
 #define ELFNOTE64(name, type, desc) ELFNOTE(64, name, type, desc)
+
+#define ELFNOTE_NAME(name)     #name
+#define ELFNOTE_SECTION(name)  ".note."#name
 #endif /* __ASSEMBLER__ */
 
 #endif /* _LINUX_ELFNOTE_H */
diff --git a/include/linux/modsign.h b/include/linux/modsign.h
new file mode 100644 (file)
index 0000000..c5ac87a
--- /dev/null
@@ -0,0 +1,27 @@
+/* Module signing definitions
+ *
+ * Copyright (C) 2009 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 Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_MODSIGN_H
+#define _LINUX_MODSIGN_H
+
+#ifdef CONFIG_MODULE_SIG
+
+#include <linux/elfnote.h>
+
+/*
+ * The parameters of the ELF note used to carry the signature
+ */
+#define MODSIGN_NOTE_NAME      module.sig
+#define MODSIGN_NOTE_TYPE      100
+
+#endif
+
+#endif /* _LINUX_MODSIGN_H */
diff --git a/include/linux/module-verify-elf.h b/include/linux/module-verify-elf.h
new file mode 100644 (file)
index 0000000..0d7e947
--- /dev/null
@@ -0,0 +1,29 @@
+#ifdef CONFIG_64BIT
+
+#define Elf_Sym Elf64_Sym
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Phdr Elf64_Phdr
+#define Elf_Dyn Elf64_Dyn
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_SYM   ELF64_R_SYM
+#define ELF_R_TYPE  ELF64_R_TYPE
+
+#define MODULES_ARE_ELF64
+
+#else
+
+#define Elf_Sym Elf32_Sym
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Phdr Elf32_Phdr
+#define Elf_Dyn Elf32_Dyn
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_SYM   ELF32_R_SYM
+#define ELF_R_TYPE  ELF32_R_TYPE
+
+#define MODULES_ARE_ELF32
+
+#endif
diff --git a/include/linux/module-verify.h b/include/linux/module-verify.h
new file mode 100644 (file)
index 0000000..95995b2
--- /dev/null
@@ -0,0 +1,64 @@
+/* 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 <linux/crypto.h>
+#include <asm/module.h>
+
+#ifdef CONFIG_MODULE_VERIFY
+struct module_verify_data {
+       struct hash_desc        hash;           /* 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, int *_gpgsig_ok);
+
+/*
+ * 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
+
+/*
+ * module-verify-sig.c
+ */
+#ifdef CONFIG_MODULE_SIG
+extern int module_verify_signature(struct module_verify_data *mvdata,
+                                  int *_gpgsig_ok);
+#else
+#define module_verify_signature(m, g) (0)
+#endif
+
+#else
+#define module_verify(h, s, g) (0)
+#endif
index d9ca2d5dc6d0d974c5d57eb89479365d0a9f8796..89187a430dc86ba8a6d2c570f32da6756c047b4e 100644 (file)
@@ -344,6 +344,9 @@ struct module
 
        unsigned int taints;    /* same bits as kernel:tainted */
 
+       /* Is this module GPG signed */
+       int gpgsig_ok;
+
 #ifdef CONFIG_GENERIC_BUG
        /* Support for BUG */
        unsigned num_bugs;
index 412c21b00d513f81269d7860b1a9af1909e0225f..1bc8cccb6656efcd7c891a99bf4d42808e9a6c9b 100644 (file)
@@ -1364,6 +1364,38 @@ 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_SIG
+       bool "Module signature verification (EXPERIMENTAL)"
+       depends on MODULES && EXPERIMENTAL
+       select CRYPTO
+       select CRYPTO_SHA1
+       select CRYPTO_SIGNATURE
+       select CRYPTO_MPILIB
+       select MODULE_VERIFY_ELF
+       help
+         Check modules for valid signatures upon load.  For more information
+         see:
+
+         Documentation/module-signing.txt
+
+config MODULE_SIG_FORCE
+       bool "Required modules to be validly signed (EXPERIMENTAL)"
+       depends on MODULE_SIG
+       help
+         Reject unsigned modules or signed modules for which we don't have a
+         key.
+
+config MODULE_VERIFY
+       bool
+       depends on MODULES
+       default y if MODULE_VERIFY_ELF || MODULE_SIG
+
 endif # MODULES
 
 config INIT_ALL_POSSIBLE
index 2d64cfcc8b42bd187bed3b3bf3124be74b392219..21476f20774323cb25ced20c34e3b4ceb531557d 100644 (file)
@@ -51,6 +51,9 @@ 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_MODULE_SIG) += module-verify-sig.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_PM) += power/
 obj-$(CONFIG_FREEZER) += power/
diff --git a/kernel/module-verify-elf.c b/kernel/module-verify-elf.c
new file mode 100644 (file)
index 0000000..f18015b
--- /dev/null
@@ -0,0 +1,346 @@
+/* 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 <linux/ctype.h>
+#include <linux/module-verify.h>
+#include <linux/elf.h>
+#include <linux/module-verify-elf.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 struct elf_note *note;
+       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;
+       const void *start, *p, *stop;
+       const char *q, *qs;
+       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)
+
+#define notecheck(X)                                                   \
+do { if (unlikely(!(X))) { line = __LINE__; goto notecheck_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 and note list as best we can */
+       for (section = mvdata->sections + 1; section < secstop; section++) {
+               section2 = mvdata->sections + section->sh_info;
+               start = mvdata->buffer + section->sh_offset;
+               stop = start + section->sh_size;
+
+               switch (section->sh_type) {
+               case SHT_REL:
+                       rels = start;
+                       relstop = stop;
+
+                       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 = start;
+                       relastop = stop;
+
+                       for (rela = relas; rela < relastop; rela++) {
+                               relacheck(rela->r_offset < section2->sh_size);
+                               relacheck(ELF_R_SYM(rela->r_info) <
+                                         mvdata->nsyms);
+                       }
+
+                       break;
+
+               case SHT_NOTE:
+                       p = start;
+                       while (p < stop) {
+                               note = p;
+                               notecheck(stop - p >= sizeof(*note));
+                               p += sizeof(*note);
+                               tmp = note->n_namesz;
+                               if (tmp > 0) {
+                                       notecheck(stop - p >= tmp);
+                                       qs = p + tmp - 1;
+                                       notecheck(*qs == '\0');
+                                       for (q = p; q < qs; q++)
+                                               notecheck(*q != '\0');
+                                       tmp = roundup(tmp, 4);
+                                       notecheck(stop - p >= tmp);
+                                       p += tmp;
+                               }
+                               tmp = note->n_descsz;
+                               if (tmp > 0) {
+                                       notecheck(stop - p >= tmp);
+                                       tmp = roundup(tmp, 4);
+                                       notecheck(stop - p >= tmp);
+                                       p += tmp;
+                               }
+                       }
+                       seccheck(p == stop);
+                       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;
+
+notecheck_error:
+       printk("Verify ELF error [sec %ld note %ld] (assertion %d)\n",
+              (long)(section - mvdata->sections),
+              (long)(p - start), line);
+       goto format_error;
+
+format_error:
+       return -ELIBBAD;
+}
diff --git a/kernel/module-verify-sig.c b/kernel/module-verify-sig.c
new file mode 100644 (file)
index 0000000..b0511e0
--- /dev/null
@@ -0,0 +1,482 @@
+/* module-verify-sig.c: module signature checker
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from GregKH's RSA module signer
+ *
+ * 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 <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/crypto/ksign.h>
+#include <linux/modsign.h>
+#include <linux/module-verify.h>
+#include <linux/module-verify-elf.h>
+
+#undef MODSIGN_DEBUG
+
+#ifdef MODSIGN_DEBUG
+#define _debug(FMT, ...) printk(FMT, ##__VA_ARGS__)
+#else
+#define _debug(FMT, ...) do {} while (0)
+#endif
+
+#ifdef MODSIGN_DEBUG
+#define count_and_csum(C, __p, __n)                    \
+do {                                                   \
+       int __loop;                                     \
+       for (__loop = 0; __loop < __n; __loop++) {      \
+               (C)->csum += __p[__loop];               \
+               (C)->xcsum += __p[__loop];              \
+       }                                               \
+       (C)->signed_size += __n;                        \
+} while (0)
+#else
+#define count_and_csum(C, __p, __n)            \
+do {                                           \
+       (C)->signed_size += __n;                \
+} while (0)
+#endif
+
+#define crypto_digest_update_data(C, PTR, N)                   \
+do {                                                           \
+       struct scatterlist sg;                                  \
+       uint8_t *__p = (uint8_t *)(PTR);                        \
+       size_t __n = (N);                                       \
+       count_and_csum((C), __p, __n);                          \
+       sg_init_one(&sg, __p, __n);                             \
+       crypto_hash_update(&(C)->hash, &sg, __n);               \
+} while (0)
+
+#define crypto_digest_update_val(C, VAL)                               \
+do {                                                           \
+       struct scatterlist sg;                                  \
+       uint8_t *__p = (uint8_t *)&(VAL);                       \
+       size_t __n = sizeof(VAL);                               \
+       count_and_csum((C), __p, __n);                          \
+       sg_init_one(&sg, __p, __n);                             \
+       crypto_hash_update(&(C)->hash, &sg, __n);               \
+} while (0)
+
+static int module_verify_canonicalise(struct module_verify_data *mvdata);
+
+static int extract_elf_rela(struct module_verify_data *mvdata,
+                           int secix,
+                           const Elf_Rela *relatab, size_t nrels,
+                           const char *sh_name);
+
+static int extract_elf_rel(struct module_verify_data *mvdata,
+                          int secix,
+                          const Elf_Rel *reltab, size_t nrels,
+                          const char *sh_name);
+
+#ifdef CONFIG_MODULE_SIG_FORCE
+static int signedonly = 1;
+#else
+static int signedonly;
+#endif
+
+static int __init sign_setup(char *str)
+{
+       signedonly = 1;
+       return 0;
+}
+__setup("enforcemodulesig", sign_setup);
+
+static const char modsign_note_name[] = ELFNOTE_NAME(MODSIGN_NOTE_NAME);
+static const char modsign_note_section[] = ELFNOTE_SECTION(MODSIGN_NOTE_NAME);
+
+/*
+ * verify a module's signature
+ */
+int module_verify_signature(struct module_verify_data *mvdata,
+                           int *_gpgsig_ok)
+{
+       const struct elf_note *note;
+       const Elf_Shdr *sechdrs = mvdata->sections;
+       const char *secstrings = mvdata->secstrings;
+       const char *sig;
+       unsigned note_size, sig_size, note_namesz;
+       int i, ret;
+
+       for (i = 1; i < mvdata->nsects; i++) {
+               switch (sechdrs[i].sh_type) {
+               case SHT_NOTE:
+                       if (strcmp(mvdata->secstrings + sechdrs[i].sh_name,
+                                  modsign_note_section) == 0)
+                               mvdata->sig_index = i;
+                       break;
+               }
+       }
+
+       if (mvdata->sig_index <= 0)
+               goto no_signature;
+
+       note = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
+       note_size = sechdrs[mvdata->sig_index].sh_size;
+
+       /* there should be one note of the appropriate type */
+       if (note_size < sizeof(*note) + 2 * 4)
+               goto format_error_no_free;
+       note_namesz = note->n_namesz;
+       sig_size = note->n_descsz;
+       if (note_namesz != sizeof(modsign_note_name))
+               goto format_error_no_free;
+       if (note->n_type != MODSIGN_NOTE_TYPE)
+               goto format_error_no_free;
+       if (memcmp(note + 1, modsign_note_name, note_namesz) != 0)
+               goto format_error_no_free;
+       sig = (void *)(note + 1) + roundup(note_namesz, 4);
+
+       _debug("sig in section %d (size %d)\n",
+              mvdata->sig_index, sig_size);
+       _debug("%02x%02x%02x%02x%02x%02x%02x%02x\n",
+              sig[0], sig[1], sig[2], sig[3],
+              sig[4], sig[5], sig[6], sig[7]);
+
+       /* produce a canonicalisation map for the sections */
+       ret = module_verify_canonicalise(mvdata);
+       if (ret < 0)
+               return ret;
+
+       /* grab an SHA1 transformation context
+        * - !!! if this tries to load the sha1.ko module, we will deadlock!!!
+        */
+       mvdata->hash.tfm = crypto_alloc_hash("sha1", 0, 0);
+       if (!mvdata->hash.tfm) {
+               printk("Couldn't load module - SHA1 transform unavailable\n");
+               return -EPERM;
+       }
+
+       crypto_hash_init(&mvdata->hash);
+
+#ifdef MODSIGN_DEBUG
+       mvdata->xcsum = 0;
+#endif
+
+       /* load data from each relevant section into the digest */
+       for (i = 1; i < mvdata->nsects; i++) {
+               unsigned long sh_type = sechdrs[i].sh_type;
+               unsigned long sh_info = sechdrs[i].sh_info;
+               unsigned long sh_size = sechdrs[i].sh_size;
+               unsigned long sh_flags = sechdrs[i].sh_flags;
+               const char *sh_name = secstrings + sechdrs[i].sh_name;
+               const void *data = mvdata->buffer + sechdrs[i].sh_offset;
+
+               if (i == mvdata->sig_index)
+                       continue;
+
+#ifdef MODSIGN_DEBUG
+               mvdata->csum = 0;
+#endif
+
+               /* it would be nice to include relocation sections, but the act
+                * of adding a signature to the module seems changes their
+                * contents, because the symtab gets changed when sections are
+                * added or removed */
+               if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+                       if (mvdata->canonlist[sh_info]) {
+                               uint32_t xsh_info = mvdata->canonmap[sh_info];
+
+                               crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
+                               crypto_digest_update_val(mvdata, xsh_info);
+
+                               if (sh_type == SHT_RELA)
+                                       ret = extract_elf_rela(
+                                               mvdata, i,
+                                               data,
+                                               sh_size / sizeof(Elf_Rela),
+                                               sh_name);
+                               else
+                                       ret = extract_elf_rel(
+                                               mvdata, i,
+                                               data,
+                                               sh_size / sizeof(Elf_Rel),
+                                               sh_name);
+
+                               if (ret < 0)
+                                       goto format_error;
+                       }
+
+                       continue;
+               }
+
+               /* include allocatable loadable sections */
+               if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+                       goto include_section;
+
+               continue;
+
+       include_section:
+               crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
+               crypto_digest_update_data(mvdata, data, sh_size);
+
+               _debug("%08zx %02x digested the %s section, size %ld\n",
+                      mvdata->signed_size, mvdata->csum, sh_name, sh_size);
+
+               mvdata->canonlist[i] = 1;
+       }
+
+       _debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
+              mvdata->signed_size, mvdata->xcsum);
+
+       /* do the actual signature verification */
+       ret = ksign_verify_signature(sig, sig_size, mvdata->hash.tfm);
+
+       _debug("verify-sig : %d\n", ret);
+
+       switch (ret) {
+       case 0:                 /* good signature */
+               *_gpgsig_ok = 1;
+               break;
+       case -EKEYREJECTED:     /* signature mismatch or number format error */
+               printk(KERN_ERR "Module signature verification failed\n");
+               break;
+       case -ENOKEY:           /* signed, but we don't have the public key */
+               printk(KERN_ERR "Module signed with unknown public key\n");
+               break;
+       default:                /* other error (probably ENOMEM) */
+               break;
+       }
+
+       return ret;
+
+format_error:
+       crypto_free_hash(mvdata->hash.tfm);
+format_error_no_free:
+       printk(KERN_ERR "Module format error encountered\n");
+       return -ELIBBAD;
+
+       /* deal with the case of an unsigned module */
+no_signature:
+       if (!signedonly)
+               return 0;
+       printk(KERN_ERR "An attempt to load unsigned module was rejected\n");
+       return -EKEYREJECTED;
+}
+
+/*
+ * canonicalise the section table index numbers
+ */
+static int module_verify_canonicalise(struct module_verify_data *mvdata)
+{
+       int canon, loop, changed, tmp;
+
+       /* produce a list of index numbers of sections that contribute
+        * to the kernel's module image
+        */
+       mvdata->canonlist =
+               kmalloc(sizeof(int) * mvdata->nsects * 2, GFP_KERNEL);
+       if (!mvdata->canonlist)
+               return -ENOMEM;
+
+       mvdata->canonmap = mvdata->canonlist + mvdata->nsects;
+       canon = 0;
+
+       for (loop = 1; loop < mvdata->nsects; loop++) {
+               const Elf_Shdr *section = mvdata->sections + loop;
+
+               if (loop != mvdata->sig_index) {
+                       /* we only need to canonicalise allocatable sections */
+                       if (section->sh_flags & SHF_ALLOC)
+                               mvdata->canonlist[canon++] = loop;
+               }
+       }
+
+       /* canonicalise the index numbers of the contributing section */
+       do {
+               changed = 0;
+
+               for (loop = 0; loop < canon - 1; loop++) {
+                       const char *x, *y;
+
+                       x = mvdata->secstrings +
+                               mvdata->sections[mvdata->canonlist[loop + 0]].sh_name;
+                       y = mvdata->secstrings +
+                               mvdata->sections[mvdata->canonlist[loop + 1]].sh_name;
+
+                       if (strcmp(x, y) > 0) {
+                               tmp = mvdata->canonlist[loop + 0];
+                               mvdata->canonlist[loop + 0] =
+                                       mvdata->canonlist[loop + 1];
+                               mvdata->canonlist[loop + 1] = tmp;
+                               changed = 1;
+                       }
+               }
+
+       } while (changed);
+
+       for (loop = 0; loop < canon; loop++)
+               mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1;
+
+       return 0;
+}
+
+/*
+ * extract an ELF RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static int extract_elf_rela(struct module_verify_data *mvdata,
+                           int secix,
+                           const Elf_Rela *relatab, size_t nrels,
+                           const char *sh_name)
+{
+       struct {
+#if defined(MODULES_ARE_ELF32)
+               uint32_t        r_offset;
+               uint32_t        r_addend;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#elif defined(MODULES_ARE_ELF64)
+               uint64_t        r_offset;
+               uint64_t        r_addend;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#else
+#error unsupported module type
+#endif
+       } __attribute__((packed)) relocation;
+
+       const Elf_Rela *reloc;
+       const Elf_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               int st_shndx;
+
+               reloc = &relatab[loop];
+
+               /* decode the relocation */
+               relocation.r_offset = reloc->r_offset;
+               relocation.r_addend = reloc->r_addend;
+               relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = symbol->st_shndx;
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+                       relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+               crypto_digest_update_val(mvdata, relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = mvdata->strings + symbol->st_name;
+                       crypto_digest_update_data(mvdata,
+                                                 name, strlen(name) + 1);
+               }
+       }
+
+       _debug("%08zx %02x digested the %s section, nrels %zu\n",
+              mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+       return 0;
+}
+
+/*
+ * extract an ELF REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static int extract_elf_rel(struct module_verify_data *mvdata,
+                          int secix,
+                          const Elf_Rel *reltab, size_t nrels,
+                          const char *sh_name)
+{
+       struct {
+#if defined(MODULES_ARE_ELF32)
+               uint32_t        r_offset;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#elif defined(MODULES_ARE_ELF64)
+               uint64_t        r_offset;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#else
+#error unsupported module type
+#endif
+       } __attribute__((packed)) relocation;
+
+       const Elf_Rel *reloc;
+       const Elf_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               int st_shndx;
+
+               reloc = &reltab[loop];
+
+               /* decode the relocation */
+               relocation.r_offset = reloc->r_offset;
+               relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = symbol->st_shndx;
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+                       relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+               crypto_digest_update_val(mvdata, relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = mvdata->strings + symbol->st_name;
+                       crypto_digest_update_data(mvdata,
+                                                 name, strlen(name) + 1);
+               }
+       }
+
+       _debug("%08zx %02x digested the %s section, nrels %zu\n",
+              mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+       return 0;
+}
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644 (file)
index 0000000..80ff866
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 <linux/module-verify.h>
+
+/*
+ * verify a module's integrity
+ * - check the ELF is viable
+ * - check the module's signature
+ */
+int module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok)
+{
+       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;
+       }
+
+       ret = module_verify_signature(&mvdata, _gpgsig_ok);
+
+error:
+       kfree(mvdata.secsizes);
+       kfree(mvdata.canonlist);
+       return ret;
+}
index 795bdc7f5c3f270301abcb56e2f787550f36c6b9..06cfec84ec366500928857e1b3ad0a2091e6d385 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/jump_label.h>
 #include <linux/pfn.h>
 #include <linux/bsearch.h>
+#include <linux/module-verify.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/module.h>
@@ -2281,7 +2282,8 @@ static inline void kmemleak_load_module(const struct module *mod,
 /* Sets info->hdr and info->len. */
 static int copy_and_check(struct load_info *info,
                          const void __user *umod, unsigned long len,
-                         const char __user *uargs)
+                         const char __user *uargs,
+                         int *gpgsig_ok)
 {
        int err;
        Elf_Ehdr *hdr;
@@ -2314,6 +2316,12 @@ static int copy_and_check(struct load_info *info,
                goto free_hdr;
        }
 
+       /* Verify the module's contents */
+       *gpgsig_ok = 0;
+       err = module_verify(hdr, len, gpgsig_ok);
+       if (err < 0)
+               goto free_hdr;
+
        info->hdr = hdr;
        info->len = len;
        return 0;
@@ -2741,12 +2749,13 @@ static struct module *load_module(void __user *umod,
        struct load_info info = { NULL, };
        struct module *mod;
        long err;
+       int gpgsig_ok;
 
        DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
               umod, len, uargs);
 
        /* Copy in the blobs from userspace, check they are vaguely sane. */
-       err = copy_and_check(&info, umod, len, uargs);
+       err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok);
        if (err)
                return ERR_PTR(err);
 
@@ -2757,6 +2766,8 @@ static struct module *load_module(void __user *umod,
                goto free_copy;
        }
 
+       mod->gpgsig_ok = gpgsig_ok;
+
        /* Now module is in final location, initialize linked lists, etc. */
        err = module_unload_init(mod);
        if (err)
@@ -3401,8 +3412,13 @@ void print_modules(void)
        printk(KERN_DEFAULT "Modules linked in:");
        /* Most callers should already have preempt disabled, but make sure */
        preempt_disable();
-       list_for_each_entry_rcu(mod, &modules, list)
+       list_for_each_entry_rcu(mod, &modules, list) {
                printk(" %s%s", mod->name, module_flags(mod, buf));
+#ifdef CONFIG_MODULE_SIG
+               if (!mod->gpgsig_ok)
+                       printk("(U)");
+#endif
+       }
        preempt_enable();
        if (last_unloaded_module[0])
                printk(" [last unloaded: %s]", last_unloaded_module);
index 56dfafc73c1a4559ee63c01b5a4d35dda0f2df73..b7d328813eb61fe19335eb9cf055470fbd2a4579 100644 (file)
@@ -14,7 +14,8 @@
 # 3)  create one <module>.mod.c file pr. module
 # 4)  create one Module.symvers file with CRC for all exported symbols
 # 5) compile all <module>.mod.c files
-# 6) final link of the module to a <module.ko> file
+# 6) final link of the module to a <module.ko> (or <module.unsigned>) file
+# 7) signs the modules to a <module.ko> file
 
 # Step 3 is used to place certain information in the module's ELF
 # section, including information such as:
@@ -32,6 +33,8 @@
 # Step 4 is solely used to allow module versioning in external modules,
 # where the CRC of each module is retrieved from the Module.symvers file.
 
+# Step 7 is dependent on CONFIG_MODULE_SIG being enabled.
+
 # KBUILD_MODPOST_WARN can be set to avoid error out in case of undefined
 # symbols in the final module linking stage
 # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
@@ -116,6 +119,7 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
 targets += $(modules:.ko=.mod.o)
 
 # Step 6), final link of the modules
+ifneq ($(CONFIG_MODULE_SIG),y)
 quiet_cmd_ld_ko_o = LD [M]  $@
       cmd_ld_ko_o = $(LD) -r $(LDFLAGS)                                 \
                              $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
@@ -125,7 +129,40 @@ $(modules): %.ko :%.o %.mod.o FORCE
        $(call if_changed,ld_ko_o)
 
 targets += $(modules)
+else
+quiet_cmd_ld_ko_unsigned_o = LD [M]  $@
+      cmd_ld_ko_unsigned_o = \
+               $(LD) -r $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ \
+                       $(filter-out FORCE,$^) \
+               $(if $(AFTER_LINK),; $(AFTER_LINK))
+
+$(modules:.ko=.ko.unsigned): %.ko.unsigned :%.o %.mod.o FORCE
+       $(call if_changed,ld_ko_unsigned_o)
+
+targets += $(modules)
+
+# Step 7), sign the modules
+MODSECKEY = ./kernel.sec
+MODPUBKEY = ./kernel.pub
+KEYFLAGS = --no-default-keyring --secret-keyring $(MODSECKEY) --keyring $(MODPUBKEY) --no-default-keyring --homedir . --no-options --no-auto-check-trustdb --no-permission-warning
+ifdef MODKEYNAME
+KEYFLAGS += --default-key $(MODKEYNAME)
+endif
+
+quiet_cmd_sign_ko_ko_unsigned = SIGN [M] $@
+      cmd_sign_ko_ko_unsigned = \
+               scripts/mod/mod-extract $< $@.digest && \
+               rm -f $@.digest.sig && \
+               gpg --batch --no-greeting $(KEYFLAGS) -b $@.digest && \
+               sh scripts/mod/modsign-note.sh $@.digest.sig | \
+                 $(CC) -x assembler-with-cpp $(c_flags) $(CFLAGS_MODULE) -c -o $@.note.o - && \
+               $(LD) -r -o $@ $< $@.note.o
+
+$(modules): %.ko :%.ko.unsigned FORCE
+       $(call if_changed,sign_ko_ko_unsigned)
 
+targets += $(modules)
+endif
 
 # Add FORCE to the prequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
index ff954f8168c1b58f9cb00d3909dcf25f76125f03..4654e3bfa98eddc48be5b7de62e46546f534c979 100644 (file)
@@ -1,4 +1,4 @@
-hostprogs-y    := modpost mk_elfconfig
+hostprogs-y    := modpost mk_elfconfig mod-extract
 always         := $(hostprogs-y) empty.o
 
 modpost-objs   := modpost.o file2alias.o sumversion.o
diff --git a/scripts/mod/mod-extract.c b/scripts/mod/mod-extract.c
new file mode 100644 (file)
index 0000000..8b36a11
--- /dev/null
@@ -0,0 +1,892 @@
+/* mod-extract.c: module extractor for signing
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <asm/byteorder.h>
+
+static void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
+static void extract_elf32(void *buffer, size_t size, Elf32_Ehdr *hdr);
+
+struct byteorder {
+       uint16_t (*get16)(const uint16_t *);
+       uint32_t (*get32)(const uint32_t *);
+       uint64_t (*get64)(const uint64_t *);
+       void (*set16)(uint16_t *, uint16_t);
+       void (*set32)(uint32_t *, uint32_t);
+       void (*set64)(uint64_t *, uint64_t);
+};
+
+uint16_t get16_le(const uint16_t *p) { return __le16_to_cpu(*p); }
+uint32_t get32_le(const uint32_t *p) { return __le32_to_cpu(*p); }
+uint64_t get64_le(const uint64_t *p) { return __le64_to_cpu(*p); }
+uint16_t get16_be(const uint16_t *p) { return __be16_to_cpu(*p); }
+uint32_t get32_be(const uint32_t *p) { return __be32_to_cpu(*p); }
+uint64_t get64_be(const uint64_t *p) { return __be64_to_cpu(*p); }
+
+void set16_le(uint16_t *p, uint16_t n) { *p = __cpu_to_le16(n); }
+void set32_le(uint32_t *p, uint32_t n) { *p = __cpu_to_le32(n); }
+void set64_le(uint64_t *p, uint64_t n) { *p = __cpu_to_le64(n); }
+void set16_be(uint16_t *p, uint16_t n) { *p = __cpu_to_be16(n); }
+void set32_be(uint32_t *p, uint32_t n) { *p = __cpu_to_be32(n); }
+void set64_be(uint64_t *p, uint64_t n) { *p = __cpu_to_be64(n); }
+
+const struct byteorder byteorder_le = {
+       get16_le, get32_le, get64_le,
+       set16_le, set32_le, set64_le
+};
+const struct byteorder byteorder_be = {
+       get16_be, get32_be, get64_be,
+       set16_be, set32_be, set64_be
+};
+const struct byteorder *order;
+
+uint16_t get16(const uint16_t *p) { return order->get16(p); }
+uint32_t get32(const uint32_t *p) { return order->get32(p); }
+uint64_t get64(const uint64_t *p) { return order->get64(p); }
+void set16(uint16_t *p, uint16_t n) { order->set16(p, n); }
+void set32(uint32_t *p, uint32_t n) { order->set32(p, n); }
+void set64(uint64_t *p, uint64_t n) { order->set64(p, n); }
+
+FILE *outfd;
+uint8_t csum, xcsum;
+
+static void write_out(const void *data, size_t size)
+{
+       const uint8_t *p = data;
+       size_t loop;
+
+       for (loop = 0; loop < size; loop++) {
+               csum += p[loop];
+               xcsum += p[loop];
+       }
+
+       if (fwrite(data, 1, size, outfd) != size) {
+               perror("write");
+               exit(1);
+       }
+}
+
+#define write_out_val(VAL) write_out(&(VAL), sizeof(VAL))
+
+static int is_verbose;
+
+static __attribute__((format(printf, 1, 2)))
+void verbose(const char *fmt, ...)
+{
+       va_list va;
+
+       if (is_verbose) {
+               va_start(va, fmt);
+               vprintf(fmt, va);
+               va_end(va);
+       }
+}
+
+static __attribute__((noreturn))
+void usage(void)
+{
+       fprintf(stderr, "Usage: mod-extract [-v] <modulefile> <extractfile>\n");
+       exit(2);
+}
+
+/*
+ *
+ */
+int main(int argc, char **argv)
+{
+       struct stat st;
+       Elf32_Ehdr *hdr32;
+       Elf64_Ehdr *hdr64;
+       size_t len;
+       void *buffer;
+       int fd, be, b64;
+
+       while (argc > 1 && strcmp("-v", argv[1]) == 0) {
+               argv++;
+               argc--;
+               is_verbose++;
+       }
+
+       if (argc != 3)
+               usage();
+
+       /* map the module into memory */
+       fd = open(argv[1], O_RDONLY);
+       if (fd < 0) {
+               perror("open input");
+               exit(1);
+       }
+
+       if (fstat(fd, &st) < 0) {
+               perror("fstat");
+               exit(1);
+       }
+
+       len = st.st_size;
+
+       buffer = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+       if (buffer == MAP_FAILED) {
+               perror("mmap");
+               exit(1);
+       }
+
+       if (close(fd) < 0) {
+               perror("close input");
+               exit(1);
+       }
+
+       /* check it's an ELF object */
+       hdr32 = buffer;
+       hdr64 = buffer;
+
+       if (hdr32->e_ident[EI_MAG0] != ELFMAG0 ||
+           hdr32->e_ident[EI_MAG1] != ELFMAG1 ||
+           hdr32->e_ident[EI_MAG2] != ELFMAG2 ||
+           hdr32->e_ident[EI_MAG3] != ELFMAG3
+           ) {
+               fprintf(stderr, "Module does not appear to be ELF\n");
+               exit(3);
+       }
+
+       /* determine endianness and word size */
+       b64 = (hdr32->e_ident[EI_CLASS] == ELFCLASS64);
+       be = (hdr32->e_ident[EI_DATA] == ELFDATA2MSB);
+       order = be ? &byteorder_be : &byteorder_le;
+
+       verbose("Module is %s-bit %s-endian\n",
+               b64 ? "64" : "32",
+               be ? "big" : "little");
+
+       /* open the output file */
+       outfd = fopen(argv[2], "w");
+       if (!outfd) {
+               perror("open output");
+               exit(1);
+       }
+
+       /* perform the extraction */
+       if (b64)
+               extract_elf64(buffer, len, hdr64);
+       else
+               extract_elf32(buffer, len, hdr32);
+
+       /* done */
+       if (fclose(outfd) == EOF) {
+               perror("close output");
+               exit(1);
+       }
+
+       return 0;
+}
+
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static void extract_elf64_rela(const void *buffer, int secix, int targetix,
+                              const Elf64_Rela *relatab, size_t nrels,
+                              const Elf64_Sym *symbols, size_t nsyms,
+                              const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+                              const char *strings, size_t nstrings,
+                              const char *sh_name)
+{
+       struct {
+               uint64_t        r_offset;
+               uint64_t        r_addend;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+
+       } __attribute__((packed)) relocation;
+
+       const Elf64_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               Elf64_Section st_shndx;
+               Elf64_Xword r_info;
+
+               /* decode the relocation */
+               r_info = get64(&relatab[loop].r_info);
+               relocation.r_offset = relatab[loop].r_offset;
+               relocation.r_addend = relatab[loop].r_addend;
+               set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+               if (ELF64_R_SYM(r_info) >= nsyms) {
+                       fprintf(stderr, "Invalid symbol ID %lx in relocation %zu\n",
+                               ELF64_R_SYM(r_info), loop);
+                       exit(1);
+               }
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &symbols[ELF64_R_SYM(r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = get16(&symbol->st_shndx);
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+                       set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+               write_out_val(relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = strings + get32(&symbol->st_name);
+                       write_out(name, strlen(name) + 1);
+               }
+       }
+
+       verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static void extract_elf64_rel(const void *buffer, int secix, int targetix,
+                             const Elf64_Rel *relatab, size_t nrels,
+                             const Elf64_Sym *symbols, size_t nsyms,
+                             const Elf64_Shdr *sections, size_t nsects, int *canonmap,
+                             const char *strings, size_t nstrings,
+                             const char *sh_name)
+{
+       struct {
+               uint64_t        r_offset;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+
+       } __attribute__((packed)) relocation;
+
+       const Elf64_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               Elf64_Section st_shndx;
+               Elf64_Xword r_info;
+
+               /* decode the relocation */
+               r_info = get64(&relatab[loop].r_info);
+               relocation.r_offset = relatab[loop].r_offset;
+               set32(&relocation.r_type, ELF64_R_TYPE(r_info));
+
+               if (ELF64_R_SYM(r_info) >= nsyms) {
+                       fprintf(stderr, "Invalid symbol ID %lx in relocation %zi\n",
+                               ELF64_R_SYM(r_info), loop);
+                       exit(1);
+               }
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &symbols[ELF64_R_SYM(r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = get16(&symbol->st_shndx);
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+                       set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+               write_out_val(relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = strings + get32(&symbol->st_name);
+                       write_out(name, strlen(name) + 1);
+               }
+       }
+
+       verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract the data from a 64-bit module
+ */
+static void extract_elf64(void *buffer, size_t len, Elf64_Ehdr *hdr)
+{
+       const Elf64_Sym *symbols;
+       Elf64_Shdr *sections;
+       const char *secstrings, *strings;
+       size_t nsyms, nstrings;
+       int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+       sections = buffer + get64(&hdr->e_shoff);
+       secstrings = buffer + get64(&sections[get16(&hdr->e_shstrndx)].sh_offset);
+       shnum = get16(&hdr->e_shnum);
+
+       /* find the symbol table and the string table and produce a list of
+        * index numbers of sections that contribute to the kernel's module
+        * image
+        */
+       canonlist = calloc(sizeof(int), shnum * 2);
+       if (!canonlist) {
+               perror("calloc");
+               exit(1);
+       }
+       canonmap = canonlist + shnum;
+       canon = 0;
+
+       symbols = NULL;
+       strings = NULL;
+       nstrings = 0;
+       nsyms = 0;
+
+       for (loop = 1; loop < shnum; loop++) {
+               const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+               Elf64_Word  sh_type     = get32(&sections[loop].sh_type);
+               Elf64_Xword sh_size     = get64(&sections[loop].sh_size);
+               Elf64_Xword sh_flags    = get64(&sections[loop].sh_flags);
+               Elf64_Off   sh_offset   = get64(&sections[loop].sh_offset);
+               void *data = buffer + sh_offset;
+
+               /* quick sanity check */
+               if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+                       fprintf(stderr, "Section goes beyond EOF\n");
+                       exit(3);
+               }
+
+               /* we only need to canonicalise allocatable sections */
+               if (sh_flags & SHF_ALLOC)
+                       canonlist[canon++] = loop;
+
+               /* keep track of certain special sections */
+               switch (sh_type) {
+               case SHT_SYMTAB:
+                       if (strcmp(sh_name, ".symtab") == 0) {
+                               symbols = data;
+                               nsyms = sh_size / sizeof(Elf64_Sym);
+                       }
+                       break;
+
+               case SHT_STRTAB:
+                       if (strcmp(sh_name, ".strtab") == 0) {
+                               strings = data;
+                               nstrings = sh_size;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (!symbols) {
+               fprintf(stderr, "Couldn't locate symbol table\n");
+               exit(3);
+       }
+
+       if (!strings) {
+               fprintf(stderr, "Couldn't locate strings table\n");
+               exit(3);
+       }
+
+       /* canonicalise the index numbers of the contributing section */
+       do {
+               changed = 0;
+
+               for (loop = 0; loop < canon - 1; loop++) {
+                       const char *x = secstrings + get32(&sections[canonlist[loop + 0]].sh_name);
+                       const char *y = secstrings + get32(&sections[canonlist[loop + 1]].sh_name);
+                       if (strcmp(x, y) > 0) {
+                               tmp = canonlist[loop + 0];
+                               canonlist[loop + 0] = canonlist[loop + 1];
+                               canonlist[loop + 1] = tmp;
+                               changed = 1;
+                       }
+               }
+
+       } while (changed);
+
+       for (loop = 0; loop < canon; loop++)
+               canonmap[canonlist[loop]] = loop + 1;
+
+       if (is_verbose > 1) {
+               printf("\nSection canonicalisation map:\n");
+               for (loop = 1; loop < shnum; loop++) {
+                       const char *x = secstrings + get32(&sections[loop].sh_name);
+                       printf("%4d %s\n", canonmap[loop], x);
+               }
+
+               printf("\nAllocated section list in canonical order:\n");
+               for (loop = 0; loop < canon; loop++) {
+                       const char *x = secstrings + get32(&sections[canonlist[loop]].sh_name);
+                       printf("%4d %s\n", canonlist[loop], x);
+               }
+       }
+
+       memset(canonlist, 0, sizeof(int) * shnum);
+
+       /* iterate through the section table looking for sections we want to
+        * contribute to the signature */
+       verbose("\n");
+       verbose("FILE POS CS SECT NAME\n");
+       verbose("======== == ==== ==============================\n");
+
+       for (loop = 1; loop < shnum; loop++) {
+               const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+               Elf64_Word  sh_type     = get32(&sections[loop].sh_type);
+               Elf64_Xword sh_size     = get64(&sections[loop].sh_size);
+               Elf64_Xword sh_flags    = get64(&sections[loop].sh_flags);
+               Elf64_Word  sh_info     = get32(&sections[loop].sh_info);
+               Elf64_Off   sh_offset   = get64(&sections[loop].sh_offset);
+               void *data = buffer + sh_offset;
+
+               csum = 0;
+
+               /* include canonicalised relocation sections */
+               if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+                       if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+                               fprintf(stderr,
+                                       "Invalid ELF - REL/RELA sh_info does"
+                                       " not refer to a valid section\n");
+                               exit(3);
+                       }
+
+                       if (canonlist[sh_info]) {
+                               Elf32_Word xsh_info;
+
+                               verbose("%08lx ", ftell(outfd));
+
+                               set32(&xsh_info, canonmap[sh_info]);
+
+                               /* write out selected portions of the section
+                                * header */
+                               write_out(sh_name, strlen(sh_name));
+                               write_out_val(sections[loop].sh_type);
+                               write_out_val(sections[loop].sh_flags);
+                               write_out_val(sections[loop].sh_size);
+                               write_out_val(sections[loop].sh_addralign);
+                               write_out_val(xsh_info);
+
+                               if (sh_type == SHT_RELA)
+                                       extract_elf64_rela(buffer, loop, sh_info,
+                                                          data, sh_size / sizeof(Elf64_Rela),
+                                                          symbols, nsyms,
+                                                          sections, shnum, canonmap,
+                                                          strings, nstrings,
+                                                          sh_name);
+                               else
+                                       extract_elf64_rel(buffer, loop, sh_info,
+                                                         data, sh_size / sizeof(Elf64_Rel),
+                                                         symbols, nsyms,
+                                                         sections, shnum, canonmap,
+                                                         strings, nstrings,
+                                                         sh_name);
+                       }
+
+                       continue;
+               }
+
+               /* include allocatable loadable sections */
+               if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+                       goto include_section;
+
+               /* not this section */
+               continue;
+
+       include_section:
+               verbose("%08lx ", ftell(outfd));
+
+               /* write out selected portions of the section header */
+               write_out(sh_name, strlen(sh_name));
+               write_out_val(sections[loop].sh_type);
+               write_out_val(sections[loop].sh_flags);
+               write_out_val(sections[loop].sh_size);
+               write_out_val(sections[loop].sh_addralign);
+
+               /* write out the section data */
+               write_out(data, sh_size);
+
+               verbose("%02x %4d %s\n", csum, loop, sh_name);
+
+               /* note the section has been written */
+               canonlist[loop] = 1;
+       }
+
+       verbose("%08lx         (%lu bytes csum 0x%02x)\n",
+               ftell(outfd), ftell(outfd), xcsum);
+}
+
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static void extract_elf32_rela(const void *buffer, int secix, int targetix,
+                              const Elf32_Rela *relatab, size_t nrels,
+                              const Elf32_Sym *symbols, size_t nsyms,
+                              const Elf32_Shdr *sections, size_t nsects,
+                              int *canonmap,
+                              const char *strings, size_t nstrings,
+                              const char *sh_name)
+{
+       struct {
+               uint32_t        r_offset;
+               uint32_t        r_addend;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+
+       } __attribute__((packed)) relocation;
+
+       const Elf32_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               Elf32_Section st_shndx;
+               Elf32_Word r_info;
+
+               /* decode the relocation */
+               r_info = get32(&relatab[loop].r_info);
+               relocation.r_offset = relatab[loop].r_offset;
+               relocation.r_addend = relatab[loop].r_addend;
+               relocation.r_type = ELF32_R_TYPE(r_info);
+
+               if (ELF32_R_SYM(r_info) >= nsyms) {
+                       fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+                               ELF32_R_SYM(r_info), loop);
+                       exit(1);
+               }
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &symbols[ELF32_R_SYM(r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = get16(&symbol->st_shndx);
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+                       set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+               write_out_val(relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = strings + get32(&symbol->st_name);
+                       write_out(name, strlen(name) + 1);
+               }
+       }
+
+       verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract a REL table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static void extract_elf32_rel(const void *buffer, int secix, int targetix,
+                             const Elf32_Rel *relatab, size_t nrels,
+                             const Elf32_Sym *symbols, size_t nsyms,
+                             const Elf32_Shdr *sections, size_t nsects,
+                             int *canonmap,
+                             const char *strings, size_t nstrings,
+                             const char *sh_name)
+{
+       struct {
+               uint32_t        r_offset;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+
+       } __attribute__((packed)) relocation;
+
+       const Elf32_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               Elf32_Section st_shndx;
+               Elf32_Word r_info;
+
+               /* decode the relocation */
+               r_info = get32(&relatab[loop].r_info);
+               relocation.r_offset = relatab[loop].r_offset;
+               relocation.r_type = ELF32_R_TYPE(r_info);
+
+               if (ELF32_R_SYM(r_info) >= nsyms) {
+                       fprintf(stderr, "Invalid symbol ID %x in relocation %zu\n",
+                               ELF32_R_SYM(r_info), loop);
+                       exit(1);
+               }
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &symbols[ELF32_R_SYM(r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = get16(&symbol->st_shndx);
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < nsects)
+                       set16(&relocation.st_shndx, canonmap[st_shndx]);
+
+               write_out_val(relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = strings + get32(&symbol->st_name);
+                       write_out(name, strlen(name) + 1);
+               }
+       }
+
+       verbose("%02x %4d %s [canon]\n", csum, secix, sh_name);
+}
+
+/*
+ * extract the data from a 32-bit module
+ */
+static void extract_elf32(void *buffer, size_t len, Elf32_Ehdr *hdr)
+{
+       const Elf32_Sym *symbols;
+       Elf32_Shdr *sections;
+       const char *secstrings, *strings;
+       size_t nsyms, nstrings;
+       int loop, shnum, *canonlist, *canonmap, canon, changed, tmp;
+
+       sections = buffer + get32(&hdr->e_shoff);
+       secstrings = buffer + get32(&sections[get16(&hdr->e_shstrndx)].sh_offset);
+       shnum = get16(&hdr->e_shnum);
+
+       /* find the symbol table and the string table and produce a list of
+        * index numbers of sections that contribute to the kernel's module
+        * image
+        */
+       canonlist = calloc(sizeof(int), shnum * 2);
+       if (!canonlist) {
+               perror("calloc");
+               exit(1);
+       }
+       canonmap = canonlist + shnum;
+       canon = 0;
+
+       symbols = NULL;
+       strings = NULL;
+       nstrings = 0;
+       nsyms = 0;
+
+       for (loop = 1; loop < shnum; loop++) {
+               const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+               Elf32_Word  sh_type     = get32(&sections[loop].sh_type);
+               Elf32_Xword sh_size     = get32(&sections[loop].sh_size);
+               Elf32_Xword sh_flags    = get32(&sections[loop].sh_flags);
+               Elf32_Off   sh_offset   = get32(&sections[loop].sh_offset);
+               void *data = buffer + sh_offset;
+
+               /* quick sanity check */
+               if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+                       fprintf(stderr, "Section goes beyond EOF\n");
+                       exit(3);
+               }
+
+               /* we only need to canonicalise allocatable sections */
+               if (sh_flags & SHF_ALLOC)
+                       canonlist[canon++] = loop;
+
+               /* keep track of certain special sections */
+               switch (sh_type) {
+               case SHT_SYMTAB:
+                       if (strcmp(sh_name, ".symtab") == 0) {
+                               symbols = data;
+                               nsyms = sh_size / sizeof(Elf32_Sym);
+                       }
+                       break;
+
+               case SHT_STRTAB:
+                       if (strcmp(sh_name, ".strtab") == 0) {
+                               strings = data;
+                               nstrings = sh_size;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if (!symbols) {
+               fprintf(stderr, "Couldn't locate symbol table\n");
+               exit(3);
+       }
+
+       if (!strings) {
+               fprintf(stderr, "Couldn't locate strings table\n");
+               exit(3);
+       }
+
+       /* canonicalise the index numbers of the contributing section */
+       do {
+               changed = 0;
+
+               for (loop = 0; loop < canon - 1; loop++) {
+                       const char *x = secstrings + get32(&sections[canonlist[loop + 0]].sh_name);
+                       const char *y = secstrings + get32(&sections[canonlist[loop + 1]].sh_name);
+                       if (strcmp(x, y) > 0) {
+                               tmp = canonlist[loop + 0];
+                               canonlist[loop + 0] = canonlist[loop + 1];
+                               canonlist[loop + 1] = tmp;
+                               changed = 1;
+                       }
+               }
+
+       } while (changed);
+
+       for (loop = 0; loop < canon; loop++)
+               canonmap[canonlist[loop]] = loop + 1;
+
+       if (is_verbose > 1) {
+               printf("\nSection canonicalisation map:\n");
+               for (loop = 1; loop < shnum; loop++) {
+                       const char *x = secstrings + get32(&sections[loop].sh_name);
+                       printf("%4d %s\n", canonmap[loop], x);
+               }
+
+               printf("\nAllocated section list in canonical order:\n");
+               for (loop = 0; loop < canon; loop++) {
+                       const char *x = secstrings + get32(&sections[canonlist[loop]].sh_name);
+                       printf("%4d %s\n", canonlist[loop], x);
+               }
+       }
+
+       memset(canonlist, 0, sizeof(int) * shnum);
+
+       /* iterate through the section table looking for sections we want to
+        * contribute to the signature */
+       verbose("\n");
+       verbose("FILE POS CS SECT NAME\n");
+       verbose("======== == ==== ==============================\n");
+
+       for (loop = 1; loop < shnum; loop++) {
+               const char *sh_name = secstrings + get32(&sections[loop].sh_name);
+               Elf32_Word  sh_type     = get32(&sections[loop].sh_type);
+               Elf32_Xword sh_size     = get32(&sections[loop].sh_size);
+               Elf32_Xword sh_flags    = get32(&sections[loop].sh_flags);
+               Elf32_Word  sh_info     = get32(&sections[loop].sh_info);
+               Elf32_Off   sh_offset   = get32(&sections[loop].sh_offset);
+               void *data = buffer + sh_offset;
+
+               csum = 0;
+
+               /* quick sanity check */
+               if (sh_type != SHT_NOBITS && len < sh_offset + sh_size) {
+                       fprintf(stderr, "section goes beyond EOF\n");
+                       exit(3);
+               }
+
+               /* include canonicalised relocation sections */
+               if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+                       if (sh_info <= 0 && sh_info >= hdr->e_shnum) {
+                               fprintf(stderr,
+                                       "Invalid ELF - REL/RELA sh_info does"
+                                       " not refer to a valid section\n");
+                               exit(3);
+                       }
+
+                       if (canonlist[sh_info]) {
+                               Elf32_Word xsh_info;
+
+                               verbose("%08lx ", ftell(outfd));
+
+                               set32(&xsh_info, canonmap[sh_info]);
+
+                               /* write out selected portions of the section header */
+                               write_out(sh_name, strlen(sh_name));
+                               write_out_val(sections[loop].sh_type);
+                               write_out_val(sections[loop].sh_flags);
+                               write_out_val(sections[loop].sh_size);
+                               write_out_val(sections[loop].sh_addralign);
+                               write_out_val(xsh_info);
+
+                               if (sh_type == SHT_RELA)
+                                       extract_elf32_rela(buffer, loop, sh_info,
+                                                          data, sh_size / sizeof(Elf32_Rela),
+                                                          symbols, nsyms,
+                                                          sections, shnum, canonmap,
+                                                          strings, nstrings,
+                                                          sh_name);
+                               else
+                                       extract_elf32_rel(buffer, loop, sh_info,
+                                                         data, sh_size / sizeof(Elf32_Rel),
+                                                         symbols, nsyms,
+                                                         sections, shnum, canonmap,
+                                                         strings, nstrings,
+                                                         sh_name);
+                       }
+
+                       continue;
+               }
+
+               /* include allocatable loadable sections */
+               if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+                       goto include_section;
+
+               /* not this section */
+               continue;
+
+       include_section:
+               verbose("%08lx ", ftell(outfd));
+
+               /* write out selected portions of the section header */
+               write_out(sh_name, strlen(sh_name));
+               write_out_val(sections[loop].sh_type);
+               write_out_val(sections[loop].sh_flags);
+               write_out_val(sections[loop].sh_size);
+               write_out_val(sections[loop].sh_addralign);
+
+               /* write out the section data */
+               write_out(data, sh_size);
+
+               verbose("%02x %4d %s\n", csum, loop, sh_name);
+
+               /* note the section has been written */
+               canonlist[loop] = 1;
+       }
+
+       verbose("%08lx         (%lu bytes csum 0x%02x)\n",
+               ftell(outfd), ftell(outfd), xcsum);
+}
diff --git a/scripts/mod/modsign-note.sh b/scripts/mod/modsign-note.sh
new file mode 100644 (file)
index 0000000..bca67c0
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Generate a module signature note source file
+#
+# mod-sign.sh <sig-file> ><note-src-file>
+#
+
+SIG=$1
+
+cat <<EOF
+#include <linux/modsign.h>
+
+ELFNOTE(MODSIGN_NOTE_NAME, MODSIGN_NOTE_TYPE, .incbin "$SIG")
+EOF
+
+exit 0