2007-02-14 19:09:38

by David Howells

[permalink] [raw]
Subject: [PATCH 0/6] MODSIGN: Kernel module signing



These patches provide a GPG-based kernel module signing facility. Their use is
not fully automated within the confines of the kernel build process because it
needs provision of keys from outside of the kernel before the kernel can be
compiled.

The patches are:

(1) A cut-down MPI library derived from GPG with error handling added.

(2) Permit hash algorithms to hash kernel data defined by a virtual address
and a length rather than trying to use scattergather lists (which require
struct page pointers to be determined).

(3) Add extra information to the per-arch ELF headers to more fully describe
the format of the ELF metadata.

(4) Add module verification capabilities, including ELF metadata verification.

(5) Add a generic DSA signature checker. Given a SHA1 hash of the data to be
verified and a binary blob holding a GPG signature stream, this verifies
the signature using a built-in ring of public keys.

(6) Add a module signature checker that applies signature checking to modules
on module load, checking the signature against the ring of public keys
compiled into the kernel.


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).


Note to Andrew and Linus: Herbert Xu and the crypto guys need to check the
crypto bits before this should be accepted. Possibly these patches should go
via the crypto tree.

David


2007-02-14 19:09:49

by David Howells

[permalink] [raw]
Subject: [PATCH 2/6] MODSIGN: In-kernel crypto extensions

Two extensions are added:

(1) Support for SHA1 digestion of in-kernel buffers directly without the use
of scatter-gather lists.

(2) Allocation of crypto algorithm instances without resort to fallback module
loading.

SHA1 is used by module signature checking, and so must not itself require
loading as a module when the module signature checking is enabled.

Signed-Off-By: David Howells <[email protected]>
---

crypto/api.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
crypto/digest.c | 9 +++++++++
include/linux/crypto.h | 11 +++++++++++
3 files changed, 65 insertions(+), 1 deletions(-)

diff --git a/crypto/api.c b/crypto/api.c
index 55af8bb..3138d7c 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -341,6 +341,45 @@ out:
EXPORT_SYMBOL_GPL(__crypto_alloc_tfm);

/*
+ * crypto_alloc_tfm2 - Find or load crypto module
+ * @name: Name of algorithm
+ * @flags: Flags to control algorithm instance
+ * @nomodload: True to suppress resort to module loading
+ *
+ * Attempt to find or load a crypto algorithm module and create an
+ * instance of it.
+ */
+struct crypto_tfm *crypto_alloc_tfm2(const char *name, u32 flags,
+ int nomodload)
+{
+ struct crypto_tfm *tfm = NULL;
+ int err;
+
+ do {
+ struct crypto_alg *alg;
+
+ if (!nomodload)
+ alg = crypto_alg_mod_lookup(name, 0, CRYPTO_ALG_ASYNC);
+ else
+ alg = crypto_alg_lookup(name, 0, CRYPTO_ALG_ASYNC);
+
+ err = PTR_ERR(alg);
+ if (IS_ERR(alg))
+ continue;
+
+ tfm = __crypto_alloc_tfm(alg, flags, 0);
+ err = 0;
+ if (IS_ERR(tfm)) {
+ crypto_mod_put(alg);
+ err = PTR_ERR(tfm);
+ tfm = NULL;
+ }
+ } while (err == -EAGAIN && !signal_pending(current));
+
+ return tfm;
+}
+
+/*
* crypto_alloc_base - Locate algorithm and allocate transform
* @alg_name: Name of algorithm
* @type: Type of algorithm
@@ -392,7 +431,12 @@ err:
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(crypto_alloc_base);
-
+
+struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
+{
+ return crypto_alloc_tfm2(name, flags, 0);
+}
+
/*
* crypto_free_tfm - Free crypto transform
* @tfm: Transform to free
diff --git a/crypto/digest.c b/crypto/digest.c
index 1bf7414..d03a4e1 100644
--- a/crypto/digest.c
+++ b/crypto/digest.c
@@ -91,6 +91,14 @@ static int update(struct hash_desc *desc,
return update2(desc, sg, nbytes);
}

+static void update_kernel(struct hash_desc *desc,
+ const void *data, size_t count)
+{
+ struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
+ tfm->__crt_alg->cra_digest.dia_update(tfm, data, count);
+ crypto_yield(desc->flags);
+}
+
static int final(struct hash_desc *desc, u8 *out)
{
struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
@@ -146,6 +154,7 @@ int crypto_init_digest_ops(struct crypto_tfm *tfm)

ops->init = init;
ops->update = update;
+ ops->update_kernel = update_kernel;
ops->final = final;
ops->digest = digest;
ops->setkey = dalg->dia_setkey ? setkey : nosetkey;
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 779aa78..d960ec1 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -273,6 +273,8 @@ struct hash_tfm {
int (*init)(struct hash_desc *desc);
int (*update)(struct hash_desc *desc,
struct scatterlist *sg, unsigned int nsg);
+ void (*update_kernel)(struct hash_desc *desc,
+ const void *data, size_t count);
int (*final)(struct hash_desc *desc, u8 *out);
int (*digest)(struct hash_desc *desc, struct scatterlist *sg,
unsigned int nsg, u8 *out);
@@ -341,6 +343,8 @@ struct crypto_attr_alg {
*/

struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags);
+struct crypto_tfm *crypto_alloc_tfm2(const char *alg_name, u32 tfm_flags,
+ int nomodload);
struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask);
void crypto_free_tfm(struct crypto_tfm *tfm);

@@ -739,6 +743,13 @@ static inline int crypto_hash_update(struct hash_desc *desc,
return crypto_hash_crt(desc->tfm)->update(desc, sg, nbytes);
}

+static inline void crypto_hash_update_kernel(struct hash_desc *desc,
+ const void *data,
+ size_t count)
+{
+ return crypto_hash_crt(desc->tfm)->update_kernel(desc, data, count);
+}
+
static inline int crypto_hash_final(struct hash_desc *desc, u8 *out)
{
return crypto_hash_crt(desc->tfm)->final(desc, out);

2007-02-14 19:09:54

by David Howells

[permalink] [raw]
Subject: [PATCH 3/6] MODSIGN: Add indications of module ELF types

Add per-arch indications of module ELF types and relocation table entry types.

Signed-Off-By: David Howells <[email protected]>
---

include/asm-alpha/module.h | 3 +++
include/asm-arm/module.h | 5 +++++
include/asm-cris/module.h | 5 +++++
include/asm-h8300/module.h | 5 +++++
include/asm-i386/module.h | 5 +++++
include/asm-ia64/module.h | 5 +++++
include/asm-m32r/module.h | 5 +++++
include/asm-m68k/module.h | 5 +++++
include/asm-mips/module.h | 12 ++++++++++--
include/asm-parisc/module.h | 8 ++++++++
include/asm-powerpc/module.h | 10 ++++++++++
include/asm-s390/module.h | 3 +++
include/asm-sh/module.h | 5 +++++
include/asm-sparc/module.h | 5 +++++
include/asm-sparc64/module.h | 5 +++++
include/asm-um/module-i386.h | 4 ++++
include/asm-v850/module.h | 5 +++++
include/asm-x86_64/module.h | 5 +++++
18 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/include/asm-alpha/module.h b/include/asm-alpha/module.h
index 7b63743..3d5a3ea 100644
--- a/include/asm-alpha/module.h
+++ b/include/asm-alpha/module.h
@@ -6,6 +6,7 @@ struct mod_arch_specific
unsigned int gotsecindex;
};

+#define MODULES_ARE_ELF64
#define Elf_Sym Elf64_Sym
#define Elf_Shdr Elf64_Shdr
#define Elf_Ehdr Elf64_Ehdr
@@ -13,6 +14,8 @@ struct mod_arch_specific
#define Elf_Dyn Elf64_Dyn
#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define ARCH_SHF_SMALL SHF_ALPHA_GPREL

diff --git a/include/asm-arm/module.h b/include/asm-arm/module.h
index 24b168d..f1558f3 100644
--- a/include/asm-arm/module.h
+++ b/include/asm-arm/module.h
@@ -6,9 +6,14 @@ struct mod_arch_specific
int foo;
};

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

/*
* Include the ARM architecture version.
diff --git a/include/asm-cris/module.h b/include/asm-cris/module.h
index 7ee7231..03f7b2e 100644
--- a/include/asm-cris/module.h
+++ b/include/asm-cris/module.h
@@ -3,7 +3,12 @@
/* cris is simple */
struct mod_arch_specific { };

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif /* _ASM_CRIS_MODULE_H */
diff --git a/include/asm-h8300/module.h b/include/asm-h8300/module.h
index de23231..b1c08e2 100644
--- a/include/asm-h8300/module.h
+++ b/include/asm-h8300/module.h
@@ -4,9 +4,14 @@
* This file contains the H8/300 architecture specific module code.
*/
struct mod_arch_specific { };
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#define MODULE_SYMBOL_PREFIX "_"

diff --git a/include/asm-i386/module.h b/include/asm-i386/module.h
index 02f8f54..42ab093 100644
--- a/include/asm-i386/module.h
+++ b/include/asm-i386/module.h
@@ -6,9 +6,14 @@ struct mod_arch_specific
{
};

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#ifdef CONFIG_M386
#define MODULE_PROC_FAMILY "386 "
diff --git a/include/asm-ia64/module.h b/include/asm-ia64/module.h
index d2da61e..191355a 100644
--- a/include/asm-ia64/module.h
+++ b/include/asm-ia64/module.h
@@ -23,9 +23,14 @@ struct mod_arch_specific {
unsigned int next_got_entry; /* index of next available got entry */
};

+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define MODULE_PROC_FAMILY "ia64"
#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY \
diff --git a/include/asm-m32r/module.h b/include/asm-m32r/module.h
index eb73ee0..7146455 100644
--- a/include/asm-m32r/module.h
+++ b/include/asm-m32r/module.h
@@ -3,8 +3,13 @@

struct mod_arch_specific { };

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#endif /* _ASM_M32R_MODULE_H */
diff --git a/include/asm-m68k/module.h b/include/asm-m68k/module.h
index c6d75af..ee98908 100644
--- a/include/asm-m68k/module.h
+++ b/include/asm-m68k/module.h
@@ -1,7 +1,12 @@
#ifndef _ASM_M68K_MODULE_H
#define _ASM_M68K_MODULE_H
struct mod_arch_specific { };
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif /* _ASM_M68K_MODULE_H */
diff --git a/include/asm-mips/module.h b/include/asm-mips/module.h
index 399d03f..694f979 100644
--- a/include/asm-mips/module.h
+++ b/include/asm-mips/module.h
@@ -33,11 +33,15 @@ typedef struct {
} Elf64_Mips_Rela;

#ifdef CONFIG_32BIT
-
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Addr Elf32_Addr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#define Elf_Mips_Rel Elf32_Rel
#define Elf_Mips_Rela Elf32_Rela
@@ -48,11 +52,15 @@ typedef struct {
#endif

#ifdef CONFIG_64BIT
-
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Addr Elf64_Addr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#define Elf_Mips_Rel Elf64_Mips_Rel
#define Elf_Mips_Rela Elf64_Mips_Rela
diff --git a/include/asm-parisc/module.h b/include/asm-parisc/module.h
index 00f0688..ebd9a5e 100644
--- a/include/asm-parisc/module.h
+++ b/include/asm-parisc/module.h
@@ -4,17 +4,25 @@
* This file contains the parisc architecture specific module code.
*/
#ifdef __LP64__
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Addr Elf64_Addr
+#define Elf_Rel Elf64_Rel
#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)
#else
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Addr Elf32_Addr
+#define Elf_Rel Elf32_Rel
#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif

struct unwind_table;
diff --git a/include/asm-powerpc/module.h b/include/asm-powerpc/module.h
index e5f14b1..f9baae1 100644
--- a/include/asm-powerpc/module.h
+++ b/include/asm-powerpc/module.h
@@ -52,16 +52,26 @@ struct mod_arch_specific {
*/

#ifdef __powerpc64__
+# define MODULES_ARE_ELF64
# define Elf_Shdr Elf64_Shdr
# define Elf_Sym Elf64_Sym
# define Elf_Ehdr Elf64_Ehdr
+# define Elf_Rel Elf64_Rel
+# define Elf_Rela Elf64_Rela
+# define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+# define ELF_R_SYM(X) ELF64_R_SYM(X)
# ifdef MODULE
asm(".section .stubs,\"ax\",@nobits; .align 3; .previous");
# endif
#else
+# define MODULES_ARE_ELF32
# define Elf_Shdr Elf32_Shdr
# define Elf_Sym Elf32_Sym
# define Elf_Ehdr Elf32_Ehdr
+# define Elf_Rel Elf32_Rel
+# define Elf_Rela Elf32_Rela
+# define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+# define ELF_R_SYM(X) ELF32_R_SYM(X)
# ifdef MODULE
asm(".section .plt,\"ax\",@nobits; .align 3; .previous");
asm(".section .init.plt,\"ax\",@nobits; .align 3; .previous");
diff --git a/include/asm-s390/module.h b/include/asm-s390/module.h
index 1cc1c5a..b64dab0 100644
--- a/include/asm-s390/module.h
+++ b/include/asm-s390/module.h
@@ -29,14 +29,17 @@ struct mod_arch_specific
};

#ifdef __s390x__
+#define MODULES_ARE_ELF64
#define ElfW(x) Elf64_ ## x
#define ELFW(x) ELF64_ ## x
#else
+#define MODULES_ARE_ELF32
#define ElfW(x) Elf32_ ## x
#define ELFW(x) ELF32_ ## x
#endif

#define Elf_Addr ElfW(Addr)
+#define Elf_Rel ElfW(Rel)
#define Elf_Rela ElfW(Rela)
#define Elf_Shdr ElfW(Shdr)
#define Elf_Sym ElfW(Sym)
diff --git a/include/asm-sh/module.h b/include/asm-sh/module.h
index 118d5a2..c3cf495 100644
--- a/include/asm-sh/module.h
+++ b/include/asm-sh/module.h
@@ -9,9 +9,14 @@ struct mod_arch_specific {
/* Nothing to see here .. */
};

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#ifdef CONFIG_CPU_LITTLE_ENDIAN
# ifdef CONFIG_CPU_SH2
diff --git a/include/asm-sparc/module.h b/include/asm-sparc/module.h
index cbd9e67..e2921e2 100644
--- a/include/asm-sparc/module.h
+++ b/include/asm-sparc/module.h
@@ -1,7 +1,12 @@
#ifndef _ASM_SPARC_MODULE_H
#define _ASM_SPARC_MODULE_H
struct mod_arch_specific { };
+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)
#endif /* _ASM_SPARC_MODULE_H */
diff --git a/include/asm-sparc64/module.h b/include/asm-sparc64/module.h
index 3d77ba4..2e7ca17 100644
--- a/include/asm-sparc64/module.h
+++ b/include/asm-sparc64/module.h
@@ -1,7 +1,12 @@
#ifndef _ASM_SPARC64_MODULE_H
#define _ASM_SPARC64_MODULE_H
struct mod_arch_specific { };
+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)
#endif /* _ASM_SPARC64_MODULE_H */
diff --git a/include/asm-um/module-i386.h b/include/asm-um/module-i386.h
index 5ead4a0..b441057 100644
--- a/include/asm-um/module-i386.h
+++ b/include/asm-um/module-i386.h
@@ -9,5 +9,9 @@ struct mod_arch_specific
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

#endif
diff --git a/include/asm-v850/module.h b/include/asm-v850/module.h
index 2c2f494..48752f3 100644
--- a/include/asm-v850/module.h
+++ b/include/asm-v850/module.h
@@ -31,9 +31,14 @@ struct mod_arch_specific
unsigned int core_plt_section, init_plt_section;
};

+#define MODULES_ARE_ELF32
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Rel Elf32_Rel
+#define Elf_Rela Elf32_Rela
+#define ELF_R_TYPE(X) ELF32_R_TYPE(X)
+#define ELF_R_SYM(X) ELF32_R_SYM(X)

/* Make empty sections for module_frob_arch_sections to expand. */
#ifdef MODULE
diff --git a/include/asm-x86_64/module.h b/include/asm-x86_64/module.h
index 67f8f69..3a7373a 100644
--- a/include/asm-x86_64/module.h
+++ b/include/asm-x86_64/module.h
@@ -3,8 +3,13 @@

struct mod_arch_specific {};

+#define MODULES_ARE_ELF64
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Rel Elf64_Rel
+#define Elf_Rela Elf64_Rela
+#define ELF_R_TYPE(X) ELF64_R_TYPE(X)
+#define ELF_R_SYM(X) ELF64_R_SYM(X)

#endif

2007-02-14 19:10:09

by David Howells

[permalink] [raw]
Subject: [PATCH 6/6] MODSIGN: Apply signature checking to modules on module load

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.


=====================
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.

(3) It may be desirable to shred and delete the private key file after signing
the modules.


==============
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


Signed-Off-By: David Howells <[email protected]>
---

.gitignore | 9
Documentation/module-signing.txt | 147 ++++++
Makefile | 7
include/linux/module.h | 3
init/Kconfig | 22 +
kernel/Makefile | 1
kernel/module-verify-sig.c | 455 +++++++++++++++++++
kernel/module-verify.c | 5
kernel/module-verify.h | 17 +
kernel/module.c | 12 -
scripts/Makefile.modpost | 36 +-
scripts/mod/.gitignore | 1
scripts/mod/Makefile | 2
scripts/mod/mod-extract.c | 890 ++++++++++++++++++++++++++++++++++++++
14 files changed, 1596 insertions(+), 11 deletions(-)

diff --git a/.gitignore b/.gitignore
index 060a71d..144cd50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,9 @@
*.o
*.a
*.s
+*.ko.unsigned
+*.ko.out
+*.ko.out.sig
*.ko
*.so
*.mod.c
@@ -45,3 +48,9 @@ series

# cscope files
cscope.*
+
+#
+# GPG leavings from module signing
+#
+trustdb.gpg
+random_seed
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
new file mode 100644
index 0000000..ae7c4f9
--- /dev/null
+++ b/Documentation/module-signing.txt
@@ -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
diff --git a/Makefile b/Makefile
index 7e4968f..efbb768 100644
--- a/Makefile
+++ b/Makefile
@@ -1041,7 +1041,8 @@ clean: archclean $(clean-dirs)
@find . $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
- -o -name '*.symtypes' \) \
+ -o -name '*.symtypes' -o -name '*.ko.unsigned' \
+ -o -name "*.ko.out" -o -name "*.ko.out.sig" \) \
-type f -print | xargs rm -f

# mrproper - Delete all generated files, including .config
@@ -1229,7 +1230,9 @@ clean: $(clean-dirs)
$(call cmd,rmdirs)
@find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
- -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
+ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
+ -o -name '*.ko.unsigned' -o -name "*.ko.out" \
+ -o -name "*.ko.out.sig" \) \
-type f -print | xargs rm -f

help:
diff --git a/include/linux/module.h b/include/linux/module.h
index 419d3ef..aaae69a 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -327,6 +327,9 @@ struct module
unsigned num_bugs;
#endif

+ /* Is this module GPG signed */
+ int gpgsig_ok;
+
#ifdef CONFIG_MODULE_UNLOAD
/* Reference counts */
struct module_ref ref[NR_CPUS];
diff --git a/init/Kconfig b/init/Kconfig
index 0013f45..246b9cc 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -559,10 +559,30 @@ config MODULE_VERIFY_ELF
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 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
+ default y if MODULE_VERIFY_ELF || MODULE_SIG

config KMOD
bool "Automatic kernel module loading"
diff --git a/kernel/Makefile b/kernel/Makefile
index 5ed0824..715da89 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -32,6 +32,7 @@ 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_BSD_PROCESS_ACCT) += acct.o
diff --git a/kernel/module-verify-sig.c b/kernel/module-verify-sig.c
new file mode 100644
index 0000000..23e812b
--- /dev/null
+++ b/kernel/module-verify-sig.c
@@ -0,0 +1,455 @@
+/* module-verify-sig.c: module signature checker
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ * - 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/crypto.h>
+#include <linux/crypto/ksign.h>
+#include "module-verify.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 { \
+ size_t __n = (N); \
+ uint8_t *__p = (uint8_t *)(PTR); \
+ count_and_csum((C), __p, __n); \
+ crypto_hash_update_kernel(&(C)->hash, __p, __n); \
+} while(0)
+
+#define crypto_digest_update_val(C,VAL) \
+do { \
+ size_t __n = sizeof(VAL); \
+ uint8_t *__p = (uint8_t *)&(VAL); \
+ count_and_csum((C), __p, __n); \
+ crypto_hash_update_kernel(&(C)->hash, __p, __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);
+
+/*
+ * verify a module's signature
+ */
+int module_verify_signature(struct module_verify_data *mvdata,
+ int *_gpgsig_ok)
+{
+ const Elf_Shdr *sechdrs = mvdata->sections;
+ const char *secstrings = mvdata->secstrings;
+ const char *sig;
+ unsigned sig_size;
+ int i, ret;
+
+ for (i = 1; i < mvdata->nsects; i++) {
+ switch (sechdrs[i].sh_type) {
+ case SHT_PROGBITS:
+ if (strcmp(mvdata->secstrings + sechdrs[i].sh_name,
+ ".module_sig") == 0) {
+ mvdata->sig_index = i;
+ }
+ break;
+ }
+ }
+
+ if (mvdata->sig_index <= 0)
+ goto no_signature;
+
+ sig = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
+ sig_size = sechdrs[mvdata->sig_index].sh_size;
+
+ _debug("sig in section %d (size %d)\n",
+ mvdata->sig_index, sig_size);
+
+ /* 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_hash_cast(crypto_alloc_tfm2("sha1", 0, 1));
+ 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);
+ 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
index 875279f..64c5813 100644
--- a/kernel/module-verify.c
+++ b/kernel/module-verify.c
@@ -16,8 +16,9 @@
/*
* 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 module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok)
{
struct module_verify_data mvdata;
int ret;
@@ -34,6 +35,8 @@ int module_verify(const Elf_Ehdr *hdr, size_t size)
goto error;
}

+ ret = module_verify_signature(&mvdata, _gpgsig_ok);
+
error:
kfree(mvdata.secsizes);
kfree(mvdata.canonlist);
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
index 63f5e08..95995b2 100644
--- a/kernel/module-verify.h
+++ b/kernel/module-verify.h
@@ -10,11 +10,12 @@
*/

#include <linux/types.h>
+#include <linux/crypto.h>
#include <asm/module.h>

#ifdef CONFIG_MODULE_VERIFY
struct module_verify_data {
- struct crypto_tfm *digest; /* module signature digest */
+ 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 */
@@ -37,7 +38,7 @@ struct module_verify_data {
/*
* module-verify.c
*/
-extern int module_verify(const Elf_Ehdr *hdr, size_t size);
+extern int module_verify(const Elf_Ehdr *hdr, size_t size, int *_gpgsig_ok);

/*
* module-verify-elf.c
@@ -48,6 +49,16 @@ extern int module_verify_elf(struct module_verify_data *mvdata);
#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) (0)
+#define module_verify(h, s, g) (0)
#endif
diff --git a/kernel/module.c b/kernel/module.c
index 86abdce..c3a1dca 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1581,6 +1581,7 @@ static struct module *load_module(void __user *umod,
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
struct exception_table_entry *extable;
mm_segment_t old_fs;
+ int gpgsig_ok;

DEBUGP("load_module: umod=%p, len=%lu, uargs=%p\n",
umod, len, uargs);
@@ -1607,7 +1608,8 @@ static struct module *load_module(void __user *umod,
}

/* Verify the module's contents */
- err = module_verify(hdr, len);
+ gpgsig_ok = 0;
+ err = module_verify(hdr, len, &gpgsig_ok);
if (err < 0)
goto free_hdr;

@@ -1646,6 +1648,7 @@ static struct module *load_module(void __user *umod,
goto free_hdr;
}
mod = (void *)sechdrs[modindex].sh_addr;
+ mod->gpgsig_ok = gpgsig_ok;

if (symindex == 0) {
printk(KERN_WARNING "%s: module has no symbols (stripped?)\n",
@@ -2343,8 +2346,13 @@ void print_modules(void)
char buf[8];

printk("Modules linked in:");
- list_for_each_entry(mod, &modules, list)
+ list_for_each_entry(mod, &modules, list) {
printk(" %s%s", mod->name, taint_flags(mod->taints, buf));
+#ifdef CONFIG_MODULE_SIG
+ if (!mod->gpgsig_ok)
+ printk("(U)");
+#endif
+ }
printk("\n");
}

diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 65e0a79..cd78aed 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -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.symers 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.
@@ -96,6 +99,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) $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ \
$(filter-out FORCE,$^)
@@ -104,7 +108,37 @@ $(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) $(LDFLAGS) $(LDFLAGS_MODULE) -o $@ \
+ $(filter-out FORCE,$^)
+
+$(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 $< [email protected] && \
+ rm -f [email protected] && \
+ gpg --batch --no-greeting $(KEYFLAGS) -b [email protected] && \
+ $(OBJCOPY) --add-section [email protected] $< $@ && \
+ $(OBJCOPY) --set-section-flags .module_sig=alloc $@
+
+$(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.
# ---------------------------------------------------------------------------
diff --git a/scripts/mod/.gitignore b/scripts/mod/.gitignore
index e9b7abe..223dfd6 100644
--- a/scripts/mod/.gitignore
+++ b/scripts/mod/.gitignore
@@ -1,4 +1,5 @@
elfconfig.h
mk_elfconfig
modpost
+mod-extract

diff --git a/scripts/mod/Makefile b/scripts/mod/Makefile
index 11d69c3..bba0494 100644
--- a/scripts/mod/Makefile
+++ b/scripts/mod/Makefile
@@ -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
index 0000000..b7b5dd1
--- /dev/null
+++ b/scripts/mod/mod-extract.c
@@ -0,0 +1,890 @@
+/* mod-extract.c: module extractor for signing
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; 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>
+
+void extract_elf64(void *buffer, size_t size, Elf64_Ehdr *hdr);
+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;
+
+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))
+
+int is_verbose;
+
+void verbose(const char *fmt, ...) __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);
+ }
+}
+
+void usage(void) __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
+ */
+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
+ */
+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
+ */
+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
+ */
+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
+ */
+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
+ */
+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);
+}

2007-02-14 19:09:59

by David Howells

[permalink] [raw]
Subject: [PATCH 4/6] MODSIGN: Module ELF verifier

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 <[email protected]>
---

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 ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; 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 ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; 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 ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; 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;

2007-02-14 19:10:04

by David Howells

[permalink] [raw]
Subject: [PATCH 5/6] MODSIGN: Module signature checker and key manager

Add a facility to retain public keys and to verify signatures made with those
public keys, given a signature and crypto_hash of the data that was signed.

Signed-Off-By: David Howells <[email protected]>
---

crypto/Kconfig | 13 +
crypto/Makefile | 1
crypto/signature/Makefile | 10 +
crypto/signature/dsa.c | 96 ++++++
crypto/signature/key.h | 7
crypto/signature/ksign-keyring.c | 116 +++++++
crypto/signature/ksign-parse.c | 603 ++++++++++++++++++++++++++++++++++++
crypto/signature/ksign-publickey.c | 18 +
crypto/signature/ksign.c | 180 +++++++++++
crypto/signature/local.h | 160 ++++++++++
include/linux/crypto/ksign.h | 22 +
11 files changed, 1226 insertions(+), 0 deletions(-)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index aab5b8f..e764509 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -453,6 +453,19 @@ config CRYPTO_MPILIB
help
Multiprecision maths library from GnuPG

+config CRYPTO_SIGNATURE
+ bool "In-kernel signature checker (EXPERIMENTAL)"
+ depends on CRYPTO
+ help
+ Signature checker (used for module sig checking).
+
+config CRYPTO_SIGNATURE_DSA
+ bool "Handle DSA signatures (EXPERIMENTAL)"
+ depends on CRYPTO_SIGNATURE
+ select CRYPTO_MPILIB
+ help
+ DSA Signature checker.
+
source "drivers/crypto/Kconfig"

endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 49fc857..fe33414 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o

obj-$(CONFIG_CRYPTO_MPILIB) += mpi/
+obj-$(CONFIG_CRYPTO_SIGNATURE) += signature/
diff --git a/crypto/signature/Makefile b/crypto/signature/Makefile
new file mode 100644
index 0000000..4d1042e
--- /dev/null
+++ b/crypto/signature/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the signature checker
+#
+
+obj-y := \
+ ksign.o \
+ ksign-parse.o \
+ ksign-keyring.o \
+ ksign-publickey.o \
+ dsa.o
diff --git a/crypto/signature/dsa.c b/crypto/signature/dsa.c
new file mode 100644
index 0000000..469539c
--- /dev/null
+++ b/crypto/signature/dsa.c
@@ -0,0 +1,96 @@
+/* dsa.c - DSA signature algorithm
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/crypto/mpi.h>
+#include <asm/errno.h>
+#include "local.h"
+
+/*
+ * perform DSA algorithm signature verification
+ */
+int DSA_verify(const MPI datahash, const MPI sig[], const MPI pkey[])
+{
+ MPI p, q, g, y, r, s;
+ MPI w = NULL, u1 = NULL, u2 = NULL, v = NULL;
+ MPI base[3];
+ MPI exp[3];
+ int rc;
+
+ if (!datahash ||
+ !sig[0] || !sig[1] ||
+ !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3])
+ return -EINVAL;
+
+ p = pkey[0]; /* prime */
+ q = pkey[1]; /* group order */
+ g = pkey[2]; /* group generator */
+ y = pkey[3]; /* g^x mod p */
+ r = sig[0];
+ s = sig[1];
+
+ if (!(mpi_cmp_ui(r, 0) > 0 && mpi_cmp(r, q) < 0)) {
+ printk("DSA_verify assertion failed [0 < r < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ if (!(mpi_cmp_ui(s, 0) > 0 && mpi_cmp(s, q) < 0)) {
+ printk("DSA_verify assertion failed [0 < s < q]\n");
+ return -EKEYREJECTED;
+ }
+
+ rc = -ENOMEM;
+ w = mpi_alloc(mpi_get_nlimbs(q)); if (!w ) goto cleanup;
+ u1 = mpi_alloc(mpi_get_nlimbs(q)); if (!u1) goto cleanup;
+ u2 = mpi_alloc(mpi_get_nlimbs(q)); if (!u2) goto cleanup;
+ v = mpi_alloc(mpi_get_nlimbs(p)); if (!v ) goto cleanup;
+
+ /* w = s^(-1) mod q */
+ if (mpi_invm(w, s, q) < 0)
+ goto cleanup;
+
+ /* u1 = (datahash * w) mod q */
+ if (mpi_mulm(u1, datahash, w, q) < 0)
+ goto cleanup;
+
+ /* u2 = r * w mod q */
+ if (mpi_mulm(u2, r, w, q) < 0)
+ goto cleanup;
+
+ /* v = g^u1 * y^u2 mod p mod q */
+ base[0] = g; exp[0] = u1;
+ base[1] = y; exp[1] = u2;
+ base[2] = NULL; exp[2] = NULL;
+
+ if (mpi_mulpowm(v, base, exp, p) < 0)
+ goto cleanup;
+
+ if (mpi_fdiv_r(v, v, q) < 0)
+ goto cleanup;
+
+ rc = (mpi_cmp(v, r) == 0) ? 0 : -EKEYREJECTED;
+
+cleanup:
+ mpi_free(w);
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(v);
+ return rc;
+}
diff --git a/crypto/signature/key.h b/crypto/signature/key.h
new file mode 100644
index 0000000..7297968
--- /dev/null
+++ b/crypto/signature/key.h
@@ -0,0 +1,7 @@
+const int ksign_def_public_key_size = 0;
+/* automatically generated by bin2hex */
+static unsigned char ksign_def_public_key[] __initdata =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
diff --git a/crypto/signature/ksign-keyring.c b/crypto/signature/ksign-keyring.c
new file mode 100644
index 0000000..a839261
--- /dev/null
+++ b/crypto/signature/ksign-keyring.c
@@ -0,0 +1,116 @@
+/* ksign-keyring.c: public key cache
+ *
+ * Copyright (C) 2001 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This file is derived from part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/rwsem.h>
+#include "local.h"
+
+static LIST_HEAD(keyring);
+static DECLARE_RWSEM(keyring_sem);
+
+/*
+ * handle a public key element parsed from the keyring blob
+ */
+static int add_keyblock_key(struct ksign_public_key *pk, void *data)
+{
+ printk("- Added public key %X%X\n", pk->keyid[0], pk->keyid[1]);
+
+ if (pk->expiredate && pk->expiredate < xtime.tv_sec)
+ printk(" - public key has expired\n");
+
+ if (pk->timestamp > xtime.tv_sec )
+ printk(" - key was been created %lu seconds in future\n",
+ pk->timestamp - xtime.tv_sec);
+
+ atomic_inc(&pk->count);
+
+ down_write(&keyring_sem);
+ list_add_tail(&pk->link, &keyring);
+ up_write(&keyring_sem);
+
+ return 0;
+}
+
+/*
+ * handle a user ID element parsed from the keyring blob
+ */
+static int add_keyblock_uid(struct ksign_user_id *uid, void *data)
+{
+ printk("- User ID: %s\n", uid->name);
+ return 1;
+}
+
+/*
+ * add the keys from a ASN.1 encoded blob into the keyring
+ */
+int ksign_load_keyring_from_buffer(const void *buffer, size_t size)
+{
+ printk("Loading keyring\n");
+
+ return ksign_parse_packets((const uint8_t *) buffer,
+ size,
+ NULL,
+ add_keyblock_key,
+ add_keyblock_uid,
+ NULL);
+}
+
+/*
+ * find a public key by ID
+ */
+struct ksign_public_key *ksign_get_public_key(const uint32_t *keyid)
+{
+ struct ksign_public_key *pk;
+
+ down_read(&keyring_sem);
+
+ list_for_each_entry(pk, &keyring, link) {
+ if (memcmp(pk->keyid, keyid, sizeof(pk->keyid)) == 0) {
+ atomic_inc(&pk->count);
+ goto found;
+ }
+ }
+
+ pk = NULL;
+
+found:
+ up_read(&keyring_sem);
+ return pk;
+}
+
+/*
+ * clear the public-key keyring
+ */
+void ksign_clear_keyring(void)
+{
+ struct ksign_public_key *pk;
+
+ down_write(&keyring_sem);
+
+ while (!list_empty(&keyring)) {
+ pk = list_entry(keyring.next, struct ksign_public_key, link);
+ list_del(&pk->link);
+
+ ksign_put_public_key(pk);
+ }
+
+ up_write(&keyring_sem);
+}
diff --git a/crypto/signature/ksign-parse.c b/crypto/signature/ksign-parse.c
new file mode 100644
index 0000000..96e2ff5
--- /dev/null
+++ b/crypto/signature/ksign-parse.c
@@ -0,0 +1,603 @@
+/* parse packet data
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/errno.h>
+#include "local.h"
+
+static inline uint32_t buffer_to_u32(const uint8_t *buffer)
+{
+ uint32_t a;
+ a = *buffer << 24;
+ a |= buffer[1] << 16;
+ a |= buffer[2] << 8;
+ a |= buffer[3];
+ return a;
+}
+
+static inline uint16_t read_16(const uint8_t **datap)
+{
+ uint16_t a;
+ a = *(*datap)++ << 8;
+ a |= *(*datap)++;
+ return a;
+}
+
+static inline uint32_t read_32(const uint8_t **datap)
+{
+ uint32_t a;
+ a = *(*datap)++ << 24;
+ a |= *(*datap)++ << 16;
+ a |= *(*datap)++ << 8;
+ a |= *(*datap)++;
+ return a;
+}
+
+void ksign_free_signature(struct ksign_signature *sig)
+{
+ int i;
+
+ if (sig) {
+ for (i = 0; i < DSA_NSIG; i++)
+ mpi_free(sig->data[i]);
+ kfree(sig->hashed_data);
+ kfree(sig->unhashed_data);
+ kfree(sig);
+ }
+}
+
+void ksign_free_public_key(struct ksign_public_key *pk)
+{
+ int i;
+
+ if (pk) {
+ for (i = 0; i < DSA_NPKEY; i++)
+ mpi_free(pk->pkey[i]);
+ kfree(pk);
+ }
+}
+
+void ksign_free_user_id(struct ksign_user_id *uid)
+{
+ kfree(uid);
+}
+
+/*
+ *
+ */
+static void ksign_calc_pk_keyid(struct hash_desc *sha1,
+ struct ksign_public_key *pk)
+{
+ unsigned n;
+ unsigned nb[DSA_NPKEY];
+ unsigned nn[DSA_NPKEY];
+ uint8_t *pp[DSA_NPKEY];
+ uint32_t a32;
+ int i;
+ int npkey = DSA_NPKEY;
+
+ crypto_hash_init(sha1);
+
+ n = pk->version < 4 ? 8 : 6;
+ for (i = 0; i < npkey; i++) {
+ nb[i] = mpi_get_nbits(pk->pkey[i]);
+ pp[i] = mpi_get_buffer( pk->pkey[i], nn + i, NULL);
+ n += 2 + nn[i];
+ }
+
+ SHA1_putc(sha1, 0x99); /* ctb */
+ SHA1_putc(sha1, n >> 8); /* 2 uint8_t length header */
+ SHA1_putc(sha1, n);
+
+ if (pk->version < 4)
+ SHA1_putc(sha1, 3);
+ else
+ SHA1_putc(sha1, 4);
+
+ a32 = pk->timestamp;
+ SHA1_putc(sha1, a32 >> 24 );
+ SHA1_putc(sha1, a32 >> 16 );
+ SHA1_putc(sha1, a32 >> 8 );
+ SHA1_putc(sha1, a32 >> 0 );
+
+ if (pk->version < 4) {
+ uint16_t a16;
+
+ if( pk->expiredate )
+ a16 = (uint16_t)
+ ((pk->expiredate - pk->timestamp) / 86400L);
+ else
+ a16 = 0;
+ SHA1_putc(sha1, a16 >> 8);
+ SHA1_putc(sha1, a16 >> 0);
+ }
+
+ SHA1_putc(sha1, PUBKEY_ALGO_DSA);
+
+ for (i = 0; i < npkey; i++) {
+ SHA1_putc(sha1, nb[i] >> 8);
+ SHA1_putc(sha1, nb[i]);
+ SHA1_write(sha1, pp[i], nn[i]);
+ kfree(pp[i]);
+ }
+}
+
+/*
+ * parse a user ID embedded in a signature
+ */
+static int ksign_parse_user_id(const uint8_t *datap, const uint8_t *endp,
+ ksign_user_id_actor_t uidfnx, void *fnxdata)
+{
+ struct ksign_user_id *uid;
+ int rc = 0;
+ int n;
+
+ if (!uidfnx)
+ return 0;
+
+ n = endp - datap;
+ uid = kmalloc(sizeof(*uid) + n + 1, GFP_KERNEL);
+ if (!uid)
+ return -ENOMEM;
+ uid->len = n;
+
+ memcpy(uid->name, datap, n);
+ uid->name[n] = 0;
+
+ rc = uidfnx(uid, fnxdata);
+ if (rc == 0)
+ return rc; /* uidfnx keeps the record */
+ if (rc == 1)
+ rc = 0;
+
+ ksign_free_user_id(uid);
+ return rc;
+}
+
+/*
+ * extract a public key embedded in a signature
+ */
+static int ksign_parse_key(const uint8_t *datap, const uint8_t *endp,
+ uint8_t *hdr, int hdrlen,
+ ksign_public_key_actor_t pkfnx, void *fnxdata)
+{
+ struct ksign_public_key *pk;
+ struct hash_desc sha1;
+ unsigned long timestamp, expiredate;
+ uint8_t hash[SHA1_DIGEST_SIZE];
+ int i, version;
+ int is_v4 = 0;
+ int rc = 0;
+
+ if (endp - datap < 12) {
+ printk("ksign: public key packet too short\n");
+ return -EBADMSG;
+ }
+
+ version = *datap++;
+ switch (version) {
+ case 4:
+ is_v4 = 1;
+ case 2:
+ case 3:
+ break;
+ default:
+ printk("ksign: public key packet with unknown version %d\n",
+ version);
+ return -EBADMSG;
+ }
+
+ timestamp = read_32(&datap);
+ if (is_v4) {
+ expiredate = 0; /* have to get it from the selfsignature */
+ } else {
+ unsigned short ndays;
+ ndays = read_16(&datap);
+ if (ndays)
+ expiredate = timestamp + ndays * 86400L;
+ else
+ expiredate = 0;
+ }
+
+ if (*datap++ != PUBKEY_ALGO_DSA) {
+ printk("ksign: public key packet with unknown version %d\n",
+ version);
+ return 0;
+ }
+
+ /* extract the stuff from the DSA public key */
+ pk = kzalloc(sizeof(struct ksign_public_key), GFP_KERNEL);
+ if (!pk)
+ return -ENOMEM;
+
+ atomic_set(&pk->count, 1);
+ pk->timestamp = timestamp;
+ pk->expiredate = expiredate;
+ pk->hdrbytes = hdrlen;
+ pk->version = version;
+
+ for (i = 0; i < DSA_NPKEY; i++) {
+ unsigned int remaining = endp - datap;
+ pk->pkey[i] = mpi_read_from_buffer(datap, &remaining);
+ datap += remaining;
+ }
+
+ rc = -ENOMEM;
+
+ sha1.tfm = crypto_hash_cast(crypto_alloc_tfm2("sha1", 0, 1));
+ if (!sha1.tfm)
+ goto cleanup;
+ sha1.flags = 0;
+
+ ksign_calc_pk_keyid(&sha1, pk);
+ crypto_hash_final(&sha1, hash);
+ crypto_free_hash(sha1.tfm);
+
+ pk->keyid[0] = hash[12] << 24 | hash[13] << 16 | hash[14] << 8 | hash[15];
+ pk->keyid[1] = hash[16] << 24 | hash[17] << 16 | hash[18] << 8 | hash[19];
+
+ rc = 0;
+ if (pkfnx)
+ rc = pkfnx(pk, fnxdata);
+
+cleanup:
+ ksign_put_public_key(pk);
+ return rc;
+}
+
+/*
+ * find an element representing the issuer
+ */
+static const uint8_t *ksign_find_sig_issuer(const uint8_t *buffer)
+{
+ size_t buflen;
+ size_t n;
+ int type;
+ int seq = 0;
+
+ if (!buffer)
+ return NULL;
+
+ buflen = read_16(&buffer);
+ while (buflen) {
+ n = *buffer++; buflen--;
+ if (n == 255) {
+ if (buflen < 4)
+ goto too_short;
+ n = read_32(&buffer);
+ buflen -= 4;
+ } else if (n >= 192) {
+ if(buflen < 2)
+ goto too_short;
+ n = ((n - 192) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+
+ if (buflen < n)
+ goto too_short;
+
+ type = *buffer & 0x7f;
+ if (!(++seq > 0)) {
+ ;
+ } else if (type == SIGSUBPKT_ISSUER) {
+ /* found */
+ buffer++;
+ n--;
+ if (n > buflen || n < 8)
+ goto too_short;
+ return buffer;
+ }
+
+ buffer += n;
+ buflen -= n;
+ }
+
+too_short:
+ return NULL; /* end of subpackets; not found */
+}
+
+/*
+ * extract signature data embedded in a signature
+ */
+static int ksign_parse_signature(const uint8_t *datap, const uint8_t *endp,
+ ksign_signature_actor_t sigfnx, void *fnxdata)
+{
+ struct ksign_signature *sig;
+ size_t n;
+ int version, is_v4 = 0;
+ int rc;
+ int i;
+
+ if (endp - datap < 16) {
+ printk("ksign: signature packet too short\n");
+ return -EBADMSG;
+ }
+
+ version = *datap++;
+ switch (version) {
+ case 4:
+ is_v4 = 1;
+ case 3:
+ case 2:
+ break;
+ default:
+ printk("ksign: signature packet with unknown version %d\n",
+ version);
+ return 0;
+ }
+
+ /* store information */
+ sig = kzalloc(sizeof(*sig), GFP_KERNEL);
+ if (!sig)
+ return -ENOMEM;
+
+ sig->version = version;
+
+ if (!is_v4)
+ datap++; /* ignore md5 length */
+
+ sig->sig_class = *datap++;
+ if (!is_v4) {
+ sig->timestamp = read_32(&datap);
+ sig->keyid[0] = read_32(&datap);
+ sig->keyid[1] = read_32(&datap);
+ }
+
+ rc = 0;
+ if (*datap++ != PUBKEY_ALGO_DSA) {
+ printk("ksign: ignoring non-DSA signature\n");
+ goto leave;
+ }
+ if (*datap++ != DIGEST_ALGO_SHA1) {
+ printk("ksign: ignoring non-SHA1 signature\n");
+ goto leave;
+ }
+
+ rc = -EBADMSG;
+ if (is_v4) {
+ /* read subpackets */
+ n = read_16(&datap); /* length of hashed data */
+ if (n > 10000) {
+ printk("ksign: signature packet:"
+ " hashed data too long\n");
+ goto leave;
+ }
+ if (n) {
+ if ((size_t)(endp - datap) < n) {
+ printk("ksign: signature packet:"
+ " available data too short\n");
+ goto leave;
+ }
+ sig->hashed_data = kmalloc(n + 2, GFP_KERNEL);
+ if (!sig->hashed_data) {
+ rc = -ENOMEM;
+ goto leave;
+ }
+ sig->hashed_data[0] = n >> 8;
+ sig->hashed_data[1] = n;
+ memcpy(sig->hashed_data + 2, datap, n);
+ datap += n;
+ }
+
+ n = read_16(&datap); /* length of unhashed data */
+ if (n > 10000) {
+ printk("ksign: signature packet:"
+ " unhashed data too long\n");
+ goto leave;
+ }
+ if (n) {
+ if ((size_t) (endp - datap) < n) {
+ printk("ksign: signature packet:"
+ " available data too short\n");
+ goto leave;
+ }
+ sig->unhashed_data = kmalloc(n + 2, GFP_KERNEL);
+ if (!sig->unhashed_data) {
+ rc = -ENOMEM;
+ goto leave;
+ }
+ sig->unhashed_data[0] = n >> 8;
+ sig->unhashed_data[1] = n;
+ memcpy(sig->unhashed_data + 2, datap, n);
+ datap += n;
+ }
+ }
+
+ if (endp - datap < 5) { /* sanity check */
+ printk("ksign: signature packet too short\n");
+ goto leave;
+ }
+
+ sig->digest_start[0] = *datap++;
+ sig->digest_start[1] = *datap++;
+
+ if (is_v4) {
+ const uint8_t *p;
+
+ p = ksign_find_sig_issuer(sig->hashed_data);
+ if (!p)
+ p = ksign_find_sig_issuer(sig->unhashed_data);
+ if (!p) {
+ printk("ksign: signature packet without issuer\n");
+ } else {
+ sig->keyid[0] = buffer_to_u32(p);
+ sig->keyid[1] = buffer_to_u32(p + 4);
+ }
+ }
+
+ for (i = 0; i < DSA_NSIG; i++) {
+ unsigned remaining = endp - datap;
+ sig->data[i] = mpi_read_from_buffer(datap, &remaining);
+ datap += remaining;
+ }
+
+ rc = 0;
+ if (sigfnx) {
+ rc = sigfnx(sig, fnxdata);
+ if (rc == 0)
+ return rc; /* sigfnx keeps the signature */
+ if (rc == 1)
+ rc = 0;
+ }
+
+leave:
+ ksign_free_signature(sig);
+ return rc;
+}
+
+/*
+ * parse the next packet and call appropriate handler function for known types
+ * - returns:
+ * 0 on EOF
+ * 1 if there might be more packets
+ * -EBADMSG if the packet is in an invalid format
+ * -ve on other error
+ */
+static int ksign_parse_one_packet(const uint8_t **datap,
+ const uint8_t *endp,
+ ksign_signature_actor_t sigfnx,
+ ksign_public_key_actor_t pkfnx,
+ ksign_user_id_actor_t uidfnx,
+ void *data)
+{
+ int rc, c, ctb, pkttype, lenuint8_ts;
+ unsigned long pktlen;
+ uint8_t hdr[8];
+ int hdrlen;
+
+ /* extract the next packet and dispatch it */
+ rc = 0;
+ if (*datap >= endp)
+ goto leave;
+ ctb = *(*datap)++;
+
+ rc = -EBADMSG;
+
+ hdrlen = 0;
+ hdr[hdrlen++] = ctb;
+ if (!(ctb & 0x80)) {
+ printk("ksign: invalid packet (ctb=%02x)\n", ctb);
+ goto leave;
+ }
+
+ pktlen = 0;
+ if (ctb & 0x40) {
+ pkttype = ctb & 0x3f;
+ if (*datap >= endp) {
+ printk("ksign: 1st length byte missing\n");
+ goto leave;
+ }
+ c = *(*datap)++;
+ hdr[hdrlen++] = c;
+
+ if (c < 192) {
+ pktlen = c;
+ } else if (c < 224) {
+ pktlen = (c - 192) * 256;
+ if (*datap >= endp) {
+ printk("ksign: 2nd length uint8_t missing\n");
+ goto leave;
+ }
+ c = *(*datap)++;
+ hdr[hdrlen++] = c;
+ pktlen += c + 192;
+ } else if (c == 255) {
+ if (*datap + 3 >= endp) {
+ printk("ksign: 4 uint8_t length invalid\n");
+ goto leave;
+ }
+ pktlen = (hdr[hdrlen++] = *(*datap)++ << 24);
+ pktlen |= (hdr[hdrlen++] = *(*datap)++ << 16);
+ pktlen |= (hdr[hdrlen++] = *(*datap)++ << 8);
+ pktlen |= (hdr[hdrlen++] = *(*datap)++ << 0);
+ } else {
+ pktlen = 0;/* to indicate partial length */
+ }
+ } else {
+ pkttype = (ctb >> 2) & 0xf;
+ lenuint8_ts = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));
+ if( !lenuint8_ts ) {
+ pktlen = 0; /* don't know the value */
+ } else {
+ if (*datap + lenuint8_ts > endp) {
+ printk("ksign: length uint8_ts missing\n");
+ goto leave;
+ }
+ for( ; lenuint8_ts; lenuint8_ts-- ) {
+ pktlen <<= 8;
+ pktlen |= hdr[hdrlen++] = *(*datap)++;
+ }
+ }
+ }
+
+ if (*datap + pktlen > endp) {
+ printk("ksign: packet length longer than available data\n");
+ goto leave;
+ }
+
+ /* deal with the next packet appropriately */
+ switch (pkttype) {
+ case PKT_PUBLIC_KEY:
+ rc = ksign_parse_key(*datap, *datap + pktlen, hdr, hdrlen,
+ pkfnx, data);
+ break;
+ case PKT_SIGNATURE:
+ rc = ksign_parse_signature(*datap, *datap + pktlen,
+ sigfnx, data);
+ break;
+ case PKT_USER_ID:
+ rc = ksign_parse_user_id(*datap, *datap + pktlen,
+ uidfnx, data);
+ break;
+ default:
+ rc = 0; /* unknown packet */
+ break;
+ }
+
+ *datap += pktlen;
+leave:
+ return rc;
+}
+
+/*
+ * parse the contents of a packet buffer, passing the signature, public key and
+ * user ID to the caller's callback functions
+ */
+int ksign_parse_packets(const uint8_t *buf,
+ size_t size,
+ ksign_signature_actor_t sigfnx,
+ ksign_public_key_actor_t pkfnx,
+ ksign_user_id_actor_t uidfnx,
+ void *data)
+{
+ const uint8_t *datap, *endp;
+ int rc;
+
+ datap = buf;
+ endp = buf + size;
+ do {
+ rc = ksign_parse_one_packet(&datap, endp,
+ sigfnx, pkfnx, uidfnx, data);
+ } while (rc == 0 && datap < endp);
+
+ return rc;
+}
diff --git a/crypto/signature/ksign-publickey.c b/crypto/signature/ksign-publickey.c
new file mode 100644
index 0000000..832a419
--- /dev/null
+++ b/crypto/signature/ksign-publickey.c
@@ -0,0 +1,18 @@
+#include "local.h"
+#include "key.h"
+
+static int __init ksign_init(void)
+{
+ int rc;
+
+ printk("ksign: Installing public key data\n");
+
+ rc = ksign_load_keyring_from_buffer(ksign_def_public_key,
+ ksign_def_public_key_size);
+ if (rc < 0)
+ printk("Unable to load default keyring: error=%d\n", -rc);
+
+ return rc;
+}
+
+module_init(ksign_init)
diff --git a/crypto/signature/ksign.c b/crypto/signature/ksign.c
new file mode 100644
index 0000000..b62eb38
--- /dev/null
+++ b/crypto/signature/ksign.c
@@ -0,0 +1,180 @@
+/* ksign.c: signature checker
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <asm/errno.h>
+#include "local.h"
+
+#if 0
+#define _debug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__)
+#else
+#define _debug(FMT, ...) do { ; } while (0)
+#endif
+
+/*
+ * check the signature which is contained in SIG.
+ */
+static int ksign_signature_check(const struct ksign_signature *sig,
+ struct crypto_hash *sha1_tfm)
+{
+ struct ksign_public_key *pk;
+ struct hash_desc sha1_d;
+ uint8_t sha1[SHA1_DIGEST_SIZE];
+ MPI result = NULL;
+ int rc = 0;
+
+ pk = ksign_get_public_key(sig->keyid);
+ if (!pk) {
+ printk("ksign: module signed with unknown public key\n");
+ printk("- signature keyid: %08x%08x ver=%u\n",
+ sig->keyid[0], sig->keyid[1], sig->version);
+ return -ENOKEY;
+ }
+
+ if (pk->timestamp > sig->timestamp)
+ printk("ksign:"
+ " public key is %lu seconds newer than the signature\n",
+ pk->timestamp - sig->timestamp);
+
+ sha1_d.tfm = sha1_tfm;
+ sha1_d.flags = 0;
+
+ /* complete the digest */
+ if (sig->version >= 4)
+ SHA1_putc(&sha1_d, sig->version);
+ SHA1_putc(&sha1_d, sig->sig_class);
+
+ if (sig->version < 4) {
+ u32 a = sig->timestamp;
+ SHA1_putc(&sha1_d, (a >> 24) & 0xff);
+ SHA1_putc(&sha1_d, (a >> 16) & 0xff);
+ SHA1_putc(&sha1_d, (a >> 8) & 0xff);
+ SHA1_putc(&sha1_d, (a >> 0) & 0xff);
+ }
+ else {
+ uint8_t buf[6];
+ size_t n;
+ SHA1_putc(&sha1_d, PUBKEY_ALGO_DSA);
+ SHA1_putc(&sha1_d, DIGEST_ALGO_SHA1);
+ if (sig->hashed_data) {
+ n = (sig->hashed_data[0] << 8) | sig->hashed_data[1];
+ SHA1_write(&sha1_d, sig->hashed_data, n + 2);
+ n += 6;
+ }
+ else {
+ n = 6;
+ }
+
+ /* add some magic */
+ buf[0] = sig->version;
+ buf[1] = 0xff;
+ buf[2] = n >> 24;
+ buf[3] = n >> 16;
+ buf[4] = n >> 8;
+ buf[5] = n;
+ SHA1_write(&sha1_d, buf, 6);
+ }
+
+ crypto_hash_final(&sha1_d, sha1);
+ crypto_free_hash(sha1_tfm);
+
+ rc = -ENOMEM;
+ result = mpi_alloc((SHA1_DIGEST_SIZE + BYTES_PER_MPI_LIMB - 1) /
+ BYTES_PER_MPI_LIMB);
+ if (!result)
+ goto cleanup;
+
+ rc = mpi_set_buffer(result, sha1, SHA1_DIGEST_SIZE, 0);
+ if (rc < 0)
+ goto cleanup;
+
+ rc = DSA_verify(result, sig->data, pk->pkey);
+
+ cleanup:
+ mpi_free(result);
+ ksign_put_public_key(pk);
+
+ return rc;
+}
+
+/*
+ * examine the signatures that are parsed out of the signature data - we keep
+ * the first one that's appropriate and ignore the rest
+ * - return 0 if signature of interest (sig not freed by caller)
+ * - return 1 if no interest (caller frees)
+ */
+static int ksign_grab_signature(struct ksign_signature *sig, void *fnxdata)
+{
+ struct ksign_signature **_sig = fnxdata;
+
+ if (sig->sig_class != 0x00) {
+ _debug("ksign: standalone signature of class 0x%02x\n",
+ sig->sig_class);
+ return 1;
+ }
+
+ if (*_sig)
+ return 1;
+
+ *_sig = sig;
+ return 0;
+}
+
+/*
+ * verify the signature of some data with one of the kernel's known public keys
+ * - the SHA1 context should be currently open with the signed data digested
+ * into it so that more data can be appended
+ * - the SHA1 context is finalised and freed before returning
+ */
+int ksign_verify_signature(const char *sigdata, unsigned sig_size,
+ struct crypto_hash *sha1)
+{
+ struct ksign_signature *sig = NULL;
+ int retval;
+
+ /* parse the signature data to get the actual signature */
+ retval = ksign_parse_packets(sigdata, sig_size,
+ &ksign_grab_signature, NULL, NULL,
+ &sig);
+ if (retval < 0)
+ goto cleanup;
+
+ if (!sig) {
+ printk(KERN_NOTICE
+ "Couldn't find valid DSA signature in module\n");
+ return -ENOENT;
+ }
+
+ _debug("signature keyid: %08x%08x ver=%u\n",
+ sig->keyid[0], sig->keyid[1], sig->version);
+
+ /* check the data SHA1 transformation against the public key */
+ retval = ksign_signature_check(sig, sha1);
+ switch (retval) {
+ case 0:
+ _debug("ksign: Signature check succeeded\n");
+ break;
+ case -ENOMEM:
+ _debug("ksign: Signature check ENOMEM\n");
+ break;
+ default:
+ _debug("ksign: Signature check failed\n");
+ if (retval != -ENOKEY)
+ retval = -EKEYREJECTED;
+ break;
+ }
+
+ cleanup:
+ if (sig)
+ ksign_free_signature(sig);
+
+ return retval;
+}
diff --git a/crypto/signature/local.h b/crypto/signature/local.h
new file mode 100644
index 0000000..aa18cc4
--- /dev/null
+++ b/crypto/signature/local.h
@@ -0,0 +1,160 @@
+/* local.h: kernel signature checker internal defs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ * - Derived from GnuPG packet.h - packet definitions
+ * - Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/list.h>
+#include <linux/crypto.h>
+#include <linux/crypto/ksign.h>
+#include <linux/crypto/mpi.h>
+#include <asm/atomic.h>
+
+#define SHA1_DIGEST_SIZE 20
+
+#define PUBKEY_USAGE_SIG 1 /* key is good for signatures */
+#define PUBKEY_USAGE_ENC 2 /* key is good for encryption */
+
+#define PUBKEY_ALGO_DSA 17
+#define DSA_NPKEY 4 /* number of MPI's in DSA public key */
+#define DSA_NSIG 2 /* number of MPI's in DSA signature */
+
+#define DIGEST_ALGO_SHA1 2
+
+typedef enum {
+ PKT_NONE = 0,
+ PKT_SIGNATURE = 2, /* secret key encrypted packet */
+ PKT_PUBLIC_KEY = 6, /* public key */
+ PKT_USER_ID = 13, /* user id packet */
+} pkttype_t;
+
+typedef enum {
+ SIGSUBPKT_TEST_CRITICAL = -3,
+ SIGSUBPKT_NONE = 0,
+ SIGSUBPKT_SIG_CREATED = 2, /* signature creation time */
+ SIGSUBPKT_SIG_EXPIRE = 3, /* signature expiration time */
+ SIGSUBPKT_EXPORTABLE = 4, /* exportable */
+ SIGSUBPKT_TRUST = 5, /* trust signature */
+ SIGSUBPKT_REGEXP = 6, /* regular expression */
+ SIGSUBPKT_REVOCABLE = 7, /* revocable */
+ SIGSUBPKT_KEY_EXPIRE = 9, /* key expiration time */
+ SIGSUBPKT_ARR = 10, /* additional recipient request */
+ SIGSUBPKT_PREF_SYM = 11, /* preferred symmetric algorithms */
+ SIGSUBPKT_REV_KEY = 12, /* revocation key */
+ SIGSUBPKT_ISSUER = 16, /* issuer key ID */
+ SIGSUBPKT_NOTATION = 20, /* notation data */
+ SIGSUBPKT_PREF_HASH = 21, /* preferred hash algorithms */
+ SIGSUBPKT_PREF_COMPR = 22, /* preferred compression algorithms */
+ SIGSUBPKT_KS_FLAGS = 23, /* key server preferences */
+ SIGSUBPKT_PREF_KS = 24, /* preferred key server */
+ SIGSUBPKT_PRIMARY_UID = 25, /* primary user id */
+ SIGSUBPKT_POLICY = 26, /* policy URL */
+ SIGSUBPKT_KEY_FLAGS = 27, /* key flags */
+ SIGSUBPKT_SIGNERS_UID = 28, /* signer's user id */
+ SIGSUBPKT_REVOC_REASON = 29, /* reason for revocation */
+ SIGSUBPKT_PRIV_VERIFY_CACHE = 101, /* cache verification result */
+
+ SIGSUBPKT_FLAG_CRITICAL = 128
+} sigsubpkttype_t;
+
+/*
+ * signature record
+ */
+struct ksign_signature {
+ uint32_t keyid[2]; /* 64 bit keyid */
+ time_t timestamp; /* signature made */
+ uint8_t version;
+ uint8_t sig_class; /* sig classification, append for MD calculation*/
+ uint8_t *hashed_data; /* all subpackets with hashed data (v4 only) */
+ uint8_t *unhashed_data; /* ditto for unhashed data */
+ uint8_t digest_start[2]; /* first 2 uint8_ts of the digest */
+ MPI data[DSA_NSIG];
+};
+
+extern void ksign_free_signature(struct ksign_signature *sig);
+
+/*
+ * public key record
+ */
+struct ksign_public_key {
+ struct list_head link;
+ atomic_t count; /* ref count */
+ time_t timestamp; /* key made */
+ time_t expiredate; /* expires at this date or 0 if not at all */
+ uint8_t hdrbytes; /* number of header bytes */
+ uint8_t version;
+ int is_valid; /* key (especially subkey) is valid */
+ unsigned long local_id; /* internal use, valid if > 0 */
+ uint32_t main_keyid[2]; /* keyid of the primary key */
+ uint32_t keyid[2]; /* calculated by keyid_from_pk() */
+ MPI pkey[DSA_NPKEY];
+};
+
+extern void ksign_free_public_key(struct ksign_public_key *pk);
+
+static inline void ksign_put_public_key(struct ksign_public_key *pk)
+{
+ if (atomic_dec_and_test(&pk->count))
+ ksign_free_public_key(pk);
+}
+
+extern int ksign_load_keyring_from_buffer(const void *buffer, size_t size);
+
+extern struct ksign_public_key *ksign_get_public_key(const uint32_t *keyid);
+
+/*
+ * user ID record
+ */
+struct ksign_user_id {
+ int len; /* length of the name */
+ char name[0];
+};
+
+extern void ksign_free_user_id(struct ksign_user_id *uid);
+
+/*
+ *
+ */
+typedef int (*ksign_signature_actor_t)(struct ksign_signature *, void *fnxdata);
+typedef int (*ksign_public_key_actor_t)(struct ksign_public_key *, void *fnxdata);
+typedef int (*ksign_user_id_actor_t)(struct ksign_user_id *, void *fnxdata);
+
+extern int ksign_parse_packets(const uint8_t *buf,
+ size_t size,
+ ksign_signature_actor_t sigfnx,
+ ksign_public_key_actor_t pkfnx,
+ ksign_user_id_actor_t uidfnx,
+ void *data);
+
+extern int DSA_verify(const MPI datahash, const MPI sig[], const MPI pkey[]);
+
+/*
+ * fast access to the digest
+ * - we _know_ the data is locked into kernel memory, so we don't want to have
+ * to kmap() it
+ */
+static inline void SHA1_putc(struct hash_desc *sha1, uint8_t ch)
+{
+ crypto_hash_update_kernel(sha1, &ch, 1);
+}
+
+static inline void SHA1_write(struct hash_desc *sha1, const void *s, size_t n)
+{
+ crypto_hash_update_kernel(sha1, s, n);
+}
diff --git a/include/linux/crypto/ksign.h b/include/linux/crypto/ksign.h
new file mode 100644
index 0000000..27c9e4a
--- /dev/null
+++ b/include/linux/crypto/ksign.h
@@ -0,0 +1,22 @@
+/* ksign.h: in-kernel signature checker
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_CRYPTO_KSIGN_H
+#define _LINUX_CRYPTO_KSIGN_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_CRYPTO_SIGNATURE
+extern int ksign_verify_signature(const char *sig, unsigned sig_size,
+ struct crypto_hash *sha1);
+#endif
+
+#endif /* _LINUX_CRYPTO_KSIGN_H */

2007-02-14 19:26:45

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing



On Wed, 14 Feb 2007, David Howells wrote:
>
> (1) A cut-down MPI library derived from GPG with error handling added.

Do we really need to add this?

Wouldn't it be much nicer to just teach people to use one of the existing
signature things that we need for _other_ cases anyway, and already have
merged?

(Of course, it's possible that none of the current crypto supports any
signature checking at all - I didn't actually look. In which case my
argument is pointless).

Linus

2007-02-14 19:41:14

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Linus Torvalds <[email protected]> wrote:

> > (1) A cut-down MPI library derived from GPG with error handling added.
>
> Do we really need to add this?

I presume you mean the MPI library specifically? If so, then yes. It's
necessary to do DSA signature verification (or RSA for that matter).

> Wouldn't it be much nicer to just teach people to use one of the existing
> signature things that we need for _other_ cases anyway, and already have
> merged?

Existing signature things? I know not of such beasts, nor can I see them
offhand.

> (Of course, it's possible that none of the current crypto supports any
> signature checking at all - I didn't actually look. In which case my
> argument is pointless).

Hashing, yes; encryption, yes; signature checking: no from what I can see.

It's possible that I can share code with eCryptFS, though at first sight that
doesn't seem to overlap with what I want to do.

David

2007-02-14 21:32:05

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 07:40:57PM +0000, David Howells wrote:
> Hashing, yes; encryption, yes; signature checking: no from what I
> can see.
>
> It's possible that I can share code with eCryptFS, though at first
> sight that doesn't seem to overlap with what I want to do.

Right now, eCryptfs just delegates its modular exponentiation
operations to a userspace daemon. If RSA ever finds its way into the
kernel, I might tweak eCryptfs to use that instead for some of the
public key operations.

Mike

2007-02-14 22:05:27

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Michael Halcrow <[email protected]> wrote:

> Right now, eCryptfs just delegates its modular exponentiation
> operations to a userspace daemon. If RSA ever finds its way into the
> kernel, I might tweak eCryptfs to use that instead for some of the
> public key operations.

Am I right in thinking that RSA uses many of the same MPI lib bits as DSA?

David

2007-02-14 22:21:41

by Michael Halcrow

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 09:59:37PM +0000, David Howells wrote:
> Michael Halcrow <[email protected]> wrote:
>
> > Right now, eCryptfs just delegates its modular exponentiation
> > operations to a userspace daemon. If RSA ever finds its way into the
> > kernel, I might tweak eCryptfs to use that instead for some of the
> > public key operations.
>
> Am I right in thinking that RSA uses many of the same MPI lib bits
> as DSA?

I would imagine so. Assuming we aren't having to hassle with key
generation (eCryptfs takes care of that in userspace), then RSA is,
more or less, a^b mod c (mpi_mulpowm() and friends).

Mike

2007-02-15 03:41:30

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, 14 Feb 2007 19:09:38 +0000 David Howells <[email protected]> wrote:

> These patches provide a GPG-based kernel module signing facility. Their use is
> not fully automated within the confines of the kernel build process because it
> needs provision of keys from outside of the kernel before the kernel can be
> compiled.
>
> The patches are:
>
> (1) A cut-down MPI library derived from GPG with error handling added.
>
> (2) Permit hash algorithms to hash kernel data defined by a virtual address
> and a length rather than trying to use scattergather lists (which require
> struct page pointers to be determined).
>
> (3) Add extra information to the per-arch ELF headers to more fully describe
> the format of the ELF metadata.
>
> (4) Add module verification capabilities, including ELF metadata verification.
>
> (5) Add a generic DSA signature checker. Given a SHA1 hash of the data to be
> verified and a binary blob holding a GPG signature stream, this verifies
> the signature using a built-in ring of public keys.
>
> (6) Add a module signature checker that applies signature checking to modules
> on module load, checking the signature against the ring of public keys
> compiled into the kernel.

Grand total:

77 files changed, 9681 insertions(+), 10 deletions(-)

just to be able to sign modules.

Normally I'd collapse writhing in laughter, but..

> These patches have been in use by RHEL and Fedora kernels for years, and so
> have been thoroughly tested.

so I guess there's an argument for merging them so we can send them back to
you guys. But there's also an argument to declare all this gunk a
vendor-only thing. How much pain would that cause?

2007-02-15 04:14:30

by Dave Jones

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 07:41:12PM -0800, Andrew Morton wrote:

> 77 files changed, 9681 insertions(+), 10 deletions(-)
>
> just to be able to sign modules.
>
> Normally I'd collapse writhing in laughter, but..
>
> > These patches have been in use by RHEL and Fedora kernels for years, and so
> > have been thoroughly tested.
>
> so I guess there's an argument for merging them so we can send them back to
> you guys. But there's also an argument to declare all this gunk a
> vendor-only thing. How much pain would that cause?

it needs rediffing pretty much every time the cryptoapi changes.
On a good month that means once per point release, otherwise...

One argument in its favour is aparently Red Hat isn't the only vendor
with something like this. I've not investigated it, but I hear rumours
that suse has something similar. Having everyone using the same code
would be a win for obvious reasons.

Dave

--
http://www.codemonkey.org.uk

2007-02-15 05:35:59

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wednesday 14 February 2007 20:13, Dave Jones wrote:
> I've not investigated it, but I hear rumours that suse has something
> similar.

Actually, no. We don't belive that module signing adds significant value, and
it also doesn't work well with external modules. (The external modules we
really care about are GPL ones; it gives us a way to update drivers without
pushing out entirely new kernels.)

Cheers,
Andreas

2007-02-15 05:46:28

by Dave Jones

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 09:35:40PM -0800, Andreas Gruenbacher wrote:
> On Wednesday 14 February 2007 20:13, Dave Jones wrote:
> > I've not investigated it, but I hear rumours that suse has something
> > similar.
>
> Actually, no. We don't belive that module signing adds significant value,

ok, then I was misinformed.

> and it also doesn't work well with external modules.

well, the situation for external modules is no worse than usual.
They still work, they just aren't signed. Which from a distributor point
of view, is actually a nice thing, as they stick out like a sore thumb
in oops reports with (U) markers :)

> (The external modules we really care about are GPL ones; it gives us a way
> to update drivers without pushing out entirely new kernels.)

external modules still compile, and run just fine. The signed modules code
doesn't prevent loading of them unless the user decides to do so with
a special boot option (which is no different really than say, reducing
the cap-bound sysctl to prevent module loading).

Dave

--
http://www.codemonkey.org.uk

2007-02-15 06:15:03

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wednesday 14 February 2007 21:45, Dave Jones wrote:
> well, the situation for external modules is no worse than usual.
> They still work, they just aren't signed. Which from a distributor point
> of view, is actually a nice thing, as they stick out like a sore thumb
> in oops reports with (U) markers :)

I agree, that's really what should happen. We solve this by marking modules as
supported, partner supported, or unsupported, but in an "insecure" way, so
partners and users could try to fake the support status of a module and/or
remove status flags from Oopses, and cryptography wouldn't save us. We could
try to sign Oopses which I guess you guys are doing. This whole issue hasn't
been a serious problem in the past though, and we generally try to trust
users not to play games on us.

In the end, it all seems to boils down to a difference in philosophy.

Thanks,
Andreas

2007-02-15 06:22:30

by Dave Jones

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 10:14:53PM -0800, Andreas Gruenbacher wrote:
> On Wednesday 14 February 2007 21:45, Dave Jones wrote:
> > well, the situation for external modules is no worse than usual.
> > They still work, they just aren't signed. Which from a distributor point
> > of view, is actually a nice thing, as they stick out like a sore thumb
> > in oops reports with (U) markers :)
>
> I agree, that's really what should happen. We solve this by marking modules as
> supported, partner supported, or unsupported, but in an "insecure" way, so
> partners and users could try to fake the support status of a module and/or
> remove status flags from Oopses, and cryptography wouldn't save us. We could
> try to sign Oopses which I guess you guys are doing. This whole issue hasn't
> been a serious problem in the past though, and we generally try to trust
> users not to play games on us.

For the most part it works out. I've had users file oopses where they've editted
out Tainted: P, and left in nvidia(U) for example :-)

Dave

--
http://www.codemonkey.org.uk

2007-02-15 14:35:50

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hi,

On Wed, 14 Feb 2007, David Howells wrote:

> 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 is really the weak point - it offers no advantage over an equivalent
implementation in user space (e.g. in the module tools). So why has to be
done in the kernel?

bye, Roman

2007-02-15 17:33:05

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Roman Zippel <[email protected]> wrote:

> > 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 is really the weak point - it offers no advantage over an equivalent
> implementation in user space (e.g. in the module tools). So why has to be
> done in the kernel?

Because the init_module() system call is the common point of module submission
to the kernel, not any particular userspace program. There is no requirement
for userspace to load a module by invoking a module tools program or library,
and so userspace can bypass the checks entirely by just calling init_module().
Assume for a moment that you can't trust userspace... (Obviously, if you can't
trust the kernel then you're already stuffed.)

It is possible to protect /dev/mem and /dev/kmem or make them unavailable and
it is possible to protect the kernel's memory whilst it is running (provided
you don't have nommu or broken hardware and you don't let userspace concoct any
DMA request it likes) which mostly closes those other vectors I mentioned.
This isn't something I intended to look at with this patch. Those are separate
holes.

Making the core kernel load image inaccessible or immutable to root is not
something I can provide a generic solution for and security checking the core
kernel load image has to be done at an earlier layer as you can't rely on
something guaranteeing its own integrity because if someone can alter that
something, then they can bypass the self-checking too...

However, modules permits arbitrary modification of the running kernel to be
attempted.

David

2007-02-15 18:34:25

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hi,

On Thu, 15 Feb 2007, David Howells wrote:

> > This is really the weak point - it offers no advantage over an equivalent
> > implementation in user space (e.g. in the module tools). So why has to be
> > done in the kernel?
>
> Because the init_module() system call is the common point of module submission
> to the kernel, not any particular userspace program. There is no requirement
> for userspace to load a module by invoking a module tools program or library,
> and so userspace can bypass the checks entirely by just calling init_module().
> Assume for a moment that you can't trust userspace... (Obviously, if you can't
> trust the kernel then you're already stuffed.)

All the security stuff in the kernel should provide the necessary means to
restrict this system call.

> It is possible to protect /dev/mem and /dev/kmem or make them unavailable and
> it is possible to protect the kernel's memory whilst it is running (provided
> you don't have nommu or broken hardware and you don't let userspace concoct any
> DMA request it likes) which mostly closes those other vectors I mentioned.
> This isn't something I intended to look at with this patch. Those are separate
> holes.

Exactly and as long as there are these holes, these patches are only
kernel bloat. The simple verification can also be done in userspace and
module signing offers no real security.
What real value do these patches provide, that can't be reached via other
means? Who else than distributions would be interested in this? Pretty
much any use you initially mentioned can be done in simpler ways, e.g.
anyone afraid of modules simply disables module loading completely.

bye, Roman

2007-02-15 20:01:22

by David Lang

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thu, 15 Feb 2007, Roman Zippel wrote:

> On Thu, 15 Feb 2007, David Howells wrote:
>
>> It is possible to protect /dev/mem and /dev/kmem or make them unavailable and
>> it is possible to protect the kernel's memory whilst it is running (provided
>> you don't have nommu or broken hardware and you don't let userspace concoct any
>> DMA request it likes) which mostly closes those other vectors I mentioned.
>> This isn't something I intended to look at with this patch. Those are separate
>> holes.
>
> Exactly and as long as there are these holes, these patches are only
> kernel bloat. The simple verification can also be done in userspace and
> module signing offers no real security.
> What real value do these patches provide, that can't be reached via other
> means? Who else than distributions would be interested in this? Pretty
> much any use you initially mentioned can be done in simpler ways, e.g.
> anyone afraid of modules simply disables module loading completely.

this issue, and these holes keep comeing up in discussions, why can't these
holes be closed? I seem to remember seeing patches that would remove /dev/kmem
being sent to the list, but they weren't accepted into the kernel (and I seem to
remember people being against the concept of removeing them, not against
techincal details of the patches. but this was many years ago)

at one point I remember hearing that X required raw /dev/kmem, but for servers
you don't need/want X anyway, so this is a useful option even if X doesn't get
fixed.

David Lang

2007-02-15 20:34:28

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, 14 Feb 2007 22:14:53 PST, Andreas Gruenbacher said:
> I agree, that's really what should happen. We solve this by marking modules as
> supported, partner supported, or unsupported, but in an "insecure" way, so
> partners and users could try to fake the support status of a module and/or
> remove status flags from Oopses, and cryptography wouldn't save us.

Where cryptography *can* save you is that a partner or user can't fake a
'Suse Supported' signature without access to the Suse private key.


Attachments:
(No filename) (226.00 B)

2007-02-15 20:56:13

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, 14 Feb 2007 23:13:45 EST, Dave Jones said:
> One argument in its favour is aparently Red Hat isn't the only vendor
> with something like this. I've not investigated it, but I hear rumours
> that suse has something similar. Having everyone using the same code
> would be a win for obvious reasons.

Another argument in its favor is that it actually allows the kernel to
implement *real* checking of module licenses and trumps all the proposals
to deal with MODULE_LICENSE("GPL\0Haha!"). A vendor (or user) that wants
to be *sure* that only *really really* GPL modules are loaded can simply
refuse to load unsigned modules - and then refuse to sign a module until
after they had themselves visited the source's website, verified that the
source code was available under GPL, and so on.

Remember - the GPL is about the availability of the source. And at modprobe
time, the source isn't available. So you're left with two options:

1) Trust the binary to not lie to you about its license.
2) Ask a trusted 3rd party (usually, the person/distro that built the kernel)
whether they've verified the claim that it's really GPL.


Attachments:
(No filename) (226.00 B)

2007-02-15 21:02:34

by Roman Zippel

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hi,

On Thu, 15 Feb 2007, David Lang wrote:

> this issue, and these holes keep comeing up in discussions, why can't these
> holes be closed? I seem to remember seeing patches that would remove /dev/kmem
> being sent to the list, but they weren't accepted into the kernel (and I seem
> to remember people being against the concept of removeing them, not against
> techincal details of the patches. but this was many years ago)

1. It depends on the ratio of added code and its usefulness. I must assume
the first patch didn't even make it to the kernel due to its size, so I
think it's not unreasonable to explore the alternatives.
2. There are many ways to load an unauthorized module, thus you have to
prevent any modification of the kernel not just in memory but also on
disk.

bye, Roman

2007-02-15 21:03:01

by Adrian Bunk

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Wed, Feb 14, 2007 at 07:09:38PM +0000, David Howells wrote:
>...
> There are several reasons why these patches are useful, amongst which are:
>...
> (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.
>...

Who might have rebuilt the whole kernel using a new signature.

Or a bug reporter might have edited out the parts containing the
information that unsupported code was loaded.

Or the unsupported module itself might have removed all traces of having
been loaded from the kernel.

For printing nvidia(U), you could simply add some marker similar to
MODULE_LICENSE() that gets added to supported modules during "official"
builds and gets checked when loading a module.

That's 10k LOC less and the same level of security...

> David

cu
Adrian

--

"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed

2007-02-15 21:31:52

by Indan Zupancic

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hello,

On Wed, February 14, 2007 20:40, David Howells wrote:
> Linus Torvalds <[email protected]> wrote:
>
>> > (1) A cut-down MPI library derived from GPG with error handling added.
>>
>> Do we really need to add this?
>
> I presume you mean the MPI library specifically? If so, then yes. It's
> necessary to do DSA signature verification (or RSA for that matter).
>
>> Wouldn't it be much nicer to just teach people to use one of the existing
>> signature things that we need for _other_ cases anyway, and already have
>> merged?
>
> Existing signature things? I know not of such beasts, nor can I see them
> offhand.

The question is if using DSA/RSA is the right choice for something like this.
I think that the symmetrically encrypted hash output as signature would provide
the same amount of security. The only additional requirement is that the key
can't be read by userspace. But if they can reach the kernel binary, they can
modify it too. Same for the bootloader, where you'd want the key and initial
checking anyway. Else this whole thing could be done in user space as Roman
Zippel said...

The ELF section stuff seems like unnecessary bloat too. Can't you use/extend
modinfo, or kernel symbols?

With the above changes the code should shrink to only a few hundred new lines
of code, instead of thousands, and signature checking will be much faster too.

Greetings,

Indan

2007-02-15 21:32:35

by Adrian Bunk

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thu, Feb 15, 2007 at 03:55:41PM -0500, [email protected] wrote:
> On Wed, 14 Feb 2007 23:13:45 EST, Dave Jones said:
> > One argument in its favour is aparently Red Hat isn't the only vendor
> > with something like this. I've not investigated it, but I hear rumours
> > that suse has something similar. Having everyone using the same code
> > would be a win for obvious reasons.
>
> Another argument in its favor is that it actually allows the kernel to
> implement *real* checking of module licenses and trumps all the proposals
> to deal with MODULE_LICENSE("GPL\0Haha!"). A vendor (or user) that wants
> to be *sure* that only *really really* GPL modules are loaded can simply
> refuse to load unsigned modules - and then refuse to sign a module until
> after they had themselves visited the source's website, verified that the
> source code was available under GPL, and so on.
>
> Remember - the GPL is about the availability of the source. And at modprobe
> time, the source isn't available. So you're left with two options:
>
> 1) Trust the binary to not lie to you about its license.
> 2) Ask a trusted 3rd party (usually, the person/distro that built the kernel)
> whether they've verified the claim that it's really GPL.

There are different opinions whether the "complete source code" of the
GPLv2 includes in such cases public keys, making it questionable whether
your example will survive at court in all jurisdictions.

E.g. remember that gpl-violations.org has already successfully enforced
the publication of public keys for "firmware only loads signed kernels"
cases by threatening companies to otherwise take legal actions in
Germany.

cu
Adrian

--

"Is there not promise of rain?" Ling Tan asked suddenly out
of the darkness. There had been need of rain for many days.
"Only a promise," Lao Er said.
Pearl S. Buck - Dragon Seed

2007-02-15 22:13:06

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thursday 15 February 2007 12:34, [email protected] wrote:
> On Wed, 14 Feb 2007 22:14:53 PST, Andreas Gruenbacher said:
> > I agree, that's really what should happen. We solve this by marking
> > modules as supported, partner supported, or unsupported, but in an
> > "insecure" way, so partners and users could try to fake the support
> > status of a module and/or remove status flags from Oopses, and
> > cryptography wouldn't save us.
>
> Where cryptography *can* save you is that a partner or user can't fake a
> 'Suse Supported' signature without access to the Suse private key.

No question about that. We actually already get this from rpm signatures. What
would module signatures buy us? The kernel could then reliably determine that
an unsigned module was loaded. But people could still fake their Oopses, or
overwite the flags which indicate that a module's signature didn't match, so
we still wouldn't reliably get at that information.

Andreas

2007-02-15 22:12:43

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thu, 15 Feb 2007 22:32:40 +0100, Adrian Bunk said:
> There are different opinions whether the "complete source code" of the
> GPLv2 includes in such cases public keys, making it questionable whether
> your example will survive at court in all jurisdictions.

It's no less shaky than the whole EXPORT_SYMBOL_GPL-as-enforcement crock. :)

> E.g. remember that gpl-violations.org has already successfully enforced
> the publication of public keys for "firmware only loads signed kernels"
> cases by threatening companies to otherwise take legal actions in
> Germany.

A court order for the publication of *public* keys? :)

I think you meant "private keys" in both paragraphs above. And it's probably
a non-issue the way Red Hat implemented it - they included a document on
"How to generate your own public/private key pair", which invokes commands that
create a bitstring that you can then use to sign the entire applicable part
of the kernel tree. The fact that it's not the *same* bitstring as they used
is (IMHO) legally about as relevant as the fact that they compiled the tree
with one release of GCC, included instructions on how to compile it, and I
don't get a bitwise identical binary if I compile it with a different GCC
release.

Yes, you're still screwed if you only build *part* of the kernel tree and
expect it to work - modules you sign won't load into their kernel, and vice
versa. But that's the same problem as the old 2.4 "You didn't do a make clean
between rebuilds and you bugged out because different parts of the tree were
built with different GCC releases". As distributed, you *can* build a working
kernel from the pieces and instructions provided.



Attachments:
(No filename) (226.00 B)

2007-02-16 00:33:31

by Olaf Kirch

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thursday 15 February 2007 12:34, [email protected] wrote:
> On Wed, 14 Feb 2007 22:14:53 PST, Andreas Gruenbacher said:
> > I agree, that's really what should happen. We solve this by marking
> > modules as supported, partner supported, or unsupported, but in an
> > "insecure" way, so partners and users could try to fake the support
> > status of a module and/or remove status flags from Oopses, and
> > cryptography wouldn't save us.
>
> Where cryptography *can* save you is that a partner or user can't fake a
> 'Suse Supported' signature without access to the Suse private key.

The user has control over the running kernel, and given enough time
and clue, can circumvent any protection mechanism the vendor comes
up with. And that's a good thing IMO, unless you believe in "trusted
computing" and all those Bigbrotherisms some agencies want to put
in your machines.

So the user is running a system in some state that may or may not
resemble what the vendor shipped. When the machine crashes, the
user is free to munge the oops until it looks like a valid one.

Someone mentioned in this context that you can sign the oops - but to
do that you need a private key. And the whole point of this exercise is
that the user does not have access to that key.

So as far as support is concerned, you're back in square one.
You cannot tell a "genuine" oops produced on a supported kernel
from a doctored one produced on Joe Doe's Garage Kernel.

Olaf
--
Olaf Kirch | Anyone who has had to work with X.509 has probably
[email protected] | experienced what can best be described as
------------------+ ISO water torture. -- Peter Gutmann

2007-02-16 15:38:51

by Bodo Eggert

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Roman Zippel <[email protected]> wrote:
> On Thu, 15 Feb 2007, David Howells wrote:

>> > This is really the weak point - it offers no advantage over an equivalent
>> > implementation in user space (e.g. in the module tools). So why has to be
>> > done in the kernel?
>>
>> Because the init_module() system call is the common point of module
>> submission
>> to the kernel, not any particular userspace program. There is no requirement
>> for userspace to load a module by invoking a module tools program or library,
>> and so userspace can bypass the checks entirely by just calling
>> init_module().
>> Assume for a moment that you can't trust userspace... (Obviously, if you
>> can't trust the kernel then you're already stuffed.)
>
> All the security stuff in the kernel should provide the necessary means to
> restrict this system call.

Hardening a system should include deeper layers, too.

>> It is possible to protect /dev/mem and /dev/kmem or make them unavailable and
>> it is possible to protect the kernel's memory whilst it is running (provided
>> you don't have nommu or broken hardware and you don't let userspace concoct
>> any DMA request it likes) which mostly closes those other vectors I
>> mentioned.
>> This isn't something I intended to look at with this patch. Those are
>> separate holes.
>
> Exactly and as long as there are these holes, these patches are only
> kernel bloat.

Off cause there are other ways to expose the kernel, but they can be plugged,
too, one by one. You wouldn't leave your door open just because your window
is open, while leaving the window open because the door is open.

> The simple verification can also be done in userspace and

You may verify in userspace, but you can't use the result.

OTOH, having 1000 lines of code to verify a module _is_ bloat for this task.
Remove a 0 and it should be plenty (not counting the help text).

> module signing offers no real security.

It offers security against a class of attacks.

> What real value do these patches provide, that can't be reached via other
> means?

It provides an extra layer of security: If an exploit allows execution of
insmod(userstring), it can't compromise kernel internals.

> Who else than distributions would be interested in this?

How large is the userbase _not_ using a distribution? And are they unable to
unselect [X] Signes modules support?

> Pretty
> much any use you initially mentioned can be done in simpler ways, e.g.
> anyone afraid of modules simply disables module loading completely.

That would work if there was no need for modules. Unfortunately some people
are doomed with external modules, or not experienced enough to customize
their system. Those could benefit from a sysctl(?) saying "unsigned_modules
= taint|disallow".
--
Whenever you have plenty of ammo, you never miss. Whenever you are low on
ammo, you can't hit the broad side of a barn.

Fri?, Spammer: [email protected]

2007-02-16 20:15:56

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hi!

> 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.

Could we fix the /dev/*mem holes, first? They are already used by
malicious modules (aka rootkits...). Or can selinux already provide
/dev/*mem protection with no way for admin to turn it off?

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-02-16 20:16:12

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

Hi!

> > well, the situation for external modules is no worse than usual.
> > They still work, they just aren't signed. Which from a distributor point
> > of view, is actually a nice thing, as they stick out like a sore thumb
> > in oops reports with (U) markers :)
>
> I agree, that's really what should happen. We solve this by marking modules as
> supported, partner supported, or unsupported, but in an "insecure" way, so
> partners and users could try to fake the support status of a module and/or
> remove status flags from Oopses, and cryptography wouldn't save us. We could
> try to sign Oopses which I guess you guys are doing. This whole issue hasn't

I do not think any ammount of crypto can determine that I loaded
unsupported module, then edited oops. (TPM hw module may be able to do that, not
sure).

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2007-02-16 20:21:59

by Dave Jones

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing

On Thu, Feb 15, 2007 at 10:13:04PM +0000, Pavel Machek wrote:
> Hi!
>
> > 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.
>
> Could we fix the /dev/*mem holes, first? They are already used by
> malicious modules (aka rootkits...). Or can selinux already provide
> /dev/*mem protection with no way for admin to turn it off?

There are some valid uses for peeking through /dev/mem. Things like
dmidecode for example. So you don't want to disable it completely
in a lot of cases, but have fine-grained access to specific parts
of the file. I'm not sure SELinux can do this. Maybe the MLS stuff
helps here (though I'm far from an expert on this, so I could be
talking out of my rear).

The restricted dev/mem patches we've had in Fedora for a while
do the right thing, but they're a bit crufty (in part due to
drivers/char/mem.c being a bit of a mess before we even start
patching it). I've had "clean these up for upstream" on my
todo for a while. I might get around to it one of these days.

Dave

--
http://www.codemonkey.org.uk

2007-02-16 20:28:09

by Arjan van de Ven

[permalink] [raw]
Subject: Re: [PATCH 0/6] MODSIGN: Kernel module signing


>
> The restricted dev/mem patches we've had in Fedora for a while
> do the right thing, but they're a bit crufty (in part due to
> drivers/char/mem.c being a bit of a mess before we even start
> patching it). I've had "clean these up for upstream" on my
> todo for a while. I might get around to it one of these days.


the real thing is that /dev/mem is too many things for too many people.
Fundamentally it has 3 components
1) ram-but-not-kernel data: basically BIOS datastructures
2) kernel visible ram: user/kernel data, this has all the nasty cache
coherency issues. This is also a "debug only" use, and "rootkit only"
sometimes ;)
3) MMIO space: this really should not be used anymore, sysfs provides a
MUCH better interface and it also breaks if you have enforcing IOMMU's
in the system... it can't really work since the kernel doesn't get told
which device is being accessed

unless we split this up (well the third is split already) it's going to
remain a big mess.

--
if you want to mail me at work (you don't), use arjan (at) linux.intel.com
Test the interaction between Linux and your BIOS via http://www.linuxfirmwarekit.org