linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: rusty@rustcorp.com.au
Cc: dhowells@redhat.com, dmitry.kasatkin@intel.com,
	zohar@linux.vnet.ibm.com, jmorris@namei.org,
	keyrings@linux-nfs.org, linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 21/25] MODSIGN: Module signature verification
Date: Thu, 16 Aug 2012 02:38:02 +0100	[thread overview]
Message-ID: <20120816013802.872.20099.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20120816013405.872.42381.stgit@warthog.procyon.org.uk>

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

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

 (1) to prevent accidentally corrupted modules from causing damage;

 (2) to prevent maliciously modified modules from 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.

These patches have two main appeals: (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.

Note that 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.  To protect the kernel image
would be the responsibility of the boot loader or the system BIOS.

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

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.

This table indicates the behaviours in 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
	Signed, hash algorithm unavailable	ENOPKG		ENOPKG
	Signed, pubkey algorithm unavailable	ENOPKG		ENOPKG
	Signature without sig packet		ENOMSG		ENOMSG
	Corrupt signature			EBADMSG		EBADMSG
	Corrupt file				ELIBBAD		ELIBBAD


=======================
!!!IMPORTANT WARNING!!!
=======================

Signed modules generated by this kernel very likely CANNOT be used with
existing packaging and installation infrastructure.  For example, in Fedora's
environment, the module is potentially stripped at least twice:

 (1) by rpmbuild when the debuginfo is detached from the module, and

 (2) by the initrd image composer to reduce the module size.

Both of these will potentially result in the module signature being discarded
or rendered unverifiable, resulting in the module load just going ahead if the
signature magic is not found and enforcemodulesig=1 not being supplied or
-EKEYREJECTED being given or a panic being forced if FIPS mode is engaged.

To aid with (2), the module is completely stripped prior to signing as it
cannot be stripped after signing.  Both "strip -x -g" and "eu-strip" are
applied as the use of both of these results in a smaller binary.

That, however, means there is no debug information directly available for the
module.

The original unstripped binary for the foo.ko module can be found as
foo.ko.unsigned in the build tree.  It may be possible to use this as the debug
info source.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 include/linux/module.h |    3 +
 kernel/Makefile        |    2 -
 kernel/module-verify.c |  136 ++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/module-verify.h |    6 ++
 kernel/module.c        |   26 +++++++--
 5 files changed, 167 insertions(+), 6 deletions(-)
 create mode 100644 kernel/module-verify.c


diff --git a/include/linux/module.h b/include/linux/module.h
index fbcafe2..7391833 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -227,6 +227,9 @@ struct module
 	/* Unique handle for this module */
 	char name[MODULE_NAME_LEN];
 
+	/* Is this module GPG signed */
+	bool gpgsig_ok;
+
 	/* Sysfs stuff. */
 	struct module_kobject mkobj;
 	struct module_attribute *modinfo_attrs;
diff --git a/kernel/Makefile b/kernel/Makefile
index 84f6c04..895bef0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -55,7 +55,7 @@ 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_SIG) += modsign-pubkey.o
+obj-$(CONFIG_MODULE_SIG) += module-verify.o modsign-pubkey.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
 obj-$(CONFIG_KEXEC) += kexec.o
diff --git a/kernel/module-verify.c b/kernel/module-verify.c
new file mode 100644
index 0000000..6684e24
--- /dev/null
+++ b/kernel/module-verify.c
@@ -0,0 +1,136 @@
+/* Module signature verification
+ *
+ * The code in this file examines a signed kernel module and attempts to
+ * determine if the PGP signature attached to the end of the module matches the
+ * entire content of the module without the signature attached.
+ *
+ * Copyright (C) 2004, 2011, 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Method specified by Rusty Russell.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/modsign.h>
+#include <linux/moduleparam.h>
+#include <keys/crypto-type.h>
+#include "module-verify.h"
+
+#ifdef CONFIG_MODULE_SIG_FORCE
+#define modsign_signedonly true
+#else
+static bool modsign_signedonly;
+#endif
+
+static const char modsign_magic[] = "This Is A Crypto Signed Module";
+
+/*
+ * Verify a module's signature, if it has one
+ *
+ * Returns 0 if module is validly signed, 1 if there's no signature and a
+ * negative error code otherwise.
+ */
+static int module_verify_signature(const void *data, size_t size)
+{
+	struct crypto_sig_verify_context *mod_sig;
+	const char *cp, *sig;
+	char *end;
+	size_t magic_size, sig_size, mod_size;
+	int ret;
+
+	magic_size = sizeof(modsign_magic) - 1;
+	if (size <= 5 + magic_size)
+		return 1;
+
+	if (memcmp(data + size - magic_size, modsign_magic, magic_size) != 0)
+		return 1;
+	size -= 5 + magic_size;
+
+	cp = data + size;
+	sig_size = simple_strtoul(cp, &end, 10);
+	if (sig_size >= size || (*end != ' ' && *end != 'T'))
+		return -ELIBBAD;
+
+	mod_size = size - sig_size;
+	sig = data + mod_size;
+
+	/* Find the crypto key for the module signature
+	 * - !!! if this tries to load the required hash algorithm module,
+	 *       we will deadlock!!!
+	 */
+	mod_sig = verify_sig_begin(modsign_keyring, sig, sig_size);
+	if (IS_ERR(mod_sig)) {
+		pr_err("Couldn't initiate module signature verification: %ld\n",
+		       PTR_ERR(mod_sig));
+		return PTR_ERR(mod_sig);
+	}
+
+	/* Load the module contents into the digest */
+	ret = verify_sig_add_data(mod_sig, data, mod_size);
+	if (ret < 0) {
+		verify_sig_cancel(mod_sig);
+		return ret;
+	}
+
+	/* Do the actual signature verification */
+	ret = verify_sig_end(mod_sig, sig, sig_size);
+	pr_devel("verify-sig : %d\n", ret);
+	return ret;
+}
+
+/*
+ * Verify a module's integrity
+ */
+int module_verify(const void *data, size_t size, bool *_gpgsig_ok)
+{
+	int ret;
+
+	pr_devel("-->module_verify(,%zu,)\n", size);
+
+	ret = module_verify_signature(data, size);
+
+	pr_devel("module_verify_signature() = %d\n", ret);
+
+	switch (ret) {
+	case 0:			/* Good signature */
+		*_gpgsig_ok = true;
+		break;
+	case 1:			/* Unsigned module */
+		if (modsign_signedonly) {
+			pr_err("An attempt to load unsigned module was rejected\n");
+			return -EKEYREJECTED;
+		}
+		ret = 0;
+		break;
+	case -ELIBBAD:
+		pr_err("Module format error encountered\n");
+		break;
+	case -EBADMSG:
+		pr_err("Module signature error encountered\n");
+		break;
+	case -EKEYREJECTED:	/* Signature mismatch or number format error */
+		pr_err("Module signature verification failed\n");
+		break;
+	case -ENOKEY:		/* Signed, but we don't have the public key */
+		pr_err("Module signed with unknown public key\n");
+		break;
+	default:		/* Other error (probably ENOMEM) */
+		break;
+	}
+	return ret;
+}
+
+static int __init sign_setup(char *str)
+{
+#ifndef CONFIG_MODULE_SIG_FORCE
+	modsign_signedonly = true;
+#endif
+	return 0;
+}
+__setup("enforcemodulesig", sign_setup);
diff --git a/kernel/module-verify.h b/kernel/module-verify.h
index 2f6cc16..d59e7c9 100644
--- a/kernel/module-verify.h
+++ b/kernel/module-verify.h
@@ -11,4 +11,10 @@
 
 #ifdef CONFIG_MODULE_SIG
 extern struct key *modsign_keyring;
+extern int module_verify(const void *data, size_t size, bool *_gpgsig_ok);
+#else
+static inline int module_verify(const void *data, size_t size, bool *_gpgsig_ok)
+{
+	return 0;
+}
 #endif
diff --git a/kernel/module.c b/kernel/module.c
index 4edbd9c..30f0c0f 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -58,6 +58,7 @@
 #include <linux/jump_label.h>
 #include <linux/pfn.h>
 #include <linux/bsearch.h>
+#include "module-verify.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/module.h>
@@ -2402,7 +2403,8 @@ static inline void kmemleak_load_module(const struct module *mod,
 /* Sets info->hdr and info->len. */
 static int copy_and_check(struct load_info *info,
 			  const void __user *umod, unsigned long len,
-			  const char __user *uargs)
+			  const char __user *uargs,
+			  bool *_gpgsig_ok)
 {
 	int err;
 	Elf_Ehdr *hdr;
@@ -2435,6 +2437,12 @@ static int copy_and_check(struct load_info *info,
 		goto free_hdr;
 	}
 
+	/* Verify the module's contents */
+	*_gpgsig_ok = false;
+	err = module_verify(hdr, len, _gpgsig_ok);
+	if (err < 0)
+		goto free_hdr;
+
 	info->hdr = hdr;
 	info->len = len;
 	return 0;
@@ -2777,7 +2785,8 @@ int __weak module_frob_arch_sections(Elf_Ehdr *hdr,
 	return 0;
 }
 
-static struct module *layout_and_allocate(struct load_info *info)
+static struct module *layout_and_allocate(struct load_info *info,
+					  bool gpgsig_ok)
 {
 	/* Module within temporary copy. */
 	struct module *mod;
@@ -2787,6 +2796,7 @@ static struct module *layout_and_allocate(struct load_info *info)
 	mod = setup_load_info(info);
 	if (IS_ERR(mod))
 		return mod;
+	mod->gpgsig_ok = gpgsig_ok;
 
 	err = check_modinfo(mod, info);
 	if (err)
@@ -2870,17 +2880,18 @@ static struct module *load_module(void __user *umod,
 	struct load_info info = { NULL, };
 	struct module *mod;
 	long err;
+	bool gpgsig_ok;
 
 	pr_debug("load_module: umod=%p, len=%lu, uargs=%p\n",
 	       umod, len, uargs);
 
 	/* Copy in the blobs from userspace, check they are vaguely sane. */
-	err = copy_and_check(&info, umod, len, uargs);
+	err = copy_and_check(&info, umod, len, uargs, &gpgsig_ok);
 	if (err)
 		return ERR_PTR(err);
 
 	/* Figure out module layout, and allocate all the memory. */
-	mod = layout_and_allocate(&info);
+	mod = layout_and_allocate(&info, gpgsig_ok);
 	if (IS_ERR(mod)) {
 		err = PTR_ERR(mod);
 		goto free_copy;
@@ -3517,8 +3528,13 @@ void print_modules(void)
 	printk(KERN_DEFAULT "Modules linked in:");
 	/* Most callers should already have preempt disabled, but make sure */
 	preempt_disable();
-	list_for_each_entry_rcu(mod, &modules, list)
+	list_for_each_entry_rcu(mod, &modules, list) {
 		printk(" %s%s", mod->name, module_flags(mod, buf));
+#ifdef CONFIG_MODULE_SIG
+		if (!mod->gpgsig_ok)
+			printk("(U)");
+#endif
+	}
 	preempt_enable();
 	if (last_unloaded_module[0])
 		printk(" [last unloaded: %s]", last_unloaded_module);


  parent reply	other threads:[~2012-08-16  1:38 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-16  1:34 [PATCH 00/25] Crypto keys and module signing David Howells
2012-08-16  1:34 ` [PATCH 01/25] KEYS: Add payload preparsing opportunity prior to key instantiate or update David Howells
2012-08-16  1:34 ` [PATCH 02/25] MPILIB: Provide count_leading/trailing_zeros() based on arch functions David Howells
2012-09-10  7:13   ` Kasatkin, Dmitry
2012-09-13  5:14     ` James Morris
2012-09-13 14:09       ` Kasatkin, Dmitry
2012-08-16  1:34 ` [PATCH 03/25] KEYS: Create a key type that can be used for general cryptographic operations David Howells
2012-08-16  1:34 ` [PATCH 04/25] KEYS: Add signature verification facility David Howells
2012-08-16  1:35 ` [PATCH 05/25] KEYS: Asymmetric public-key algorithm crypto key subtype David Howells
2012-08-16  1:35 ` [PATCH 06/25] MPILIB: Reinstate mpi_cmp[_ui]() and export for RSA signature verification David Howells
2012-08-16  1:35 ` [PATCH 07/25] KEYS: RSA: Implement signature verification algorithm [PKCS#1 / RFC3447] David Howells
2012-08-16  1:35 ` [PATCH 08/25] KEYS: RSA: Fix signature verification for shorter signatures David Howells
2012-08-16  1:35 ` [PATCH 09/25] PGPLIB: PGP definitions (RFC 4880) David Howells
2012-08-16  1:36 ` [PATCH 10/25] PGPLIB: Basic packet parser David Howells
2012-08-16  1:36 ` [PATCH 11/25] PGPLIB: Signature parser David Howells
2012-08-16  1:36 ` [PATCH 12/25] KEYS: PGP data parser David Howells
2012-08-16  1:36 ` [PATCH 13/25] KEYS: PGP-based public key signature verification David Howells
2012-08-16  1:36 ` [PATCH 14/25] KEYS: PGP format signature parser David Howells
2012-08-16  1:36 ` [PATCH 15/25] KEYS: Provide PGP key description autogeneration David Howells
2012-08-16  1:37 ` [PATCH 16/25] KEYS: Provide a function to load keys from a PGP keyring blob David Howells
2012-08-16  1:37 ` [PATCH 17/25] MODSIGN: Provide gitignore and make clean rules for extra files David Howells
2012-08-16  1:37 ` [PATCH 18/25] MODSIGN: Provide Documentation and Kconfig options David Howells
2012-08-16  1:37 ` [PATCH 19/25] MODSIGN: Sign modules during the build process David Howells
2012-08-16  1:37 ` [PATCH 20/25] MODSIGN: Provide module signing public keys to the kernel David Howells
2012-08-31 14:33   ` Michal Marek
2012-08-16  1:38 ` David Howells [this message]
2012-08-16  1:38 ` [PATCH 22/25] MODSIGN: Automatically generate module signing keys if missing David Howells
2012-08-16  1:38 ` [PATCH 23/25] MODSIGN: Panic the kernel if FIPS is enabled upon module signing failure David Howells
2012-08-16  1:38 ` [PATCH 24/25] MODSIGN: Allow modules to be signed with an unknown key unless enforcing David Howells
2012-08-16  1:38 ` [PATCH 25/25] MODSIGN: Fix documentation of signed-nokey behavior when not enforcing David Howells
2012-08-21  5:04 ` [PATCH 00/25] Crypto keys and module signing Rusty Russell
2012-08-22 10:50 ` David Howells
2012-08-22 11:52   ` Mimi Zohar
2012-08-22 16:07   ` Kasatkin, Dmitry
2012-09-04  5:55 ` [RFC] module: signature infrastructure Rusty Russell
2012-09-04 12:07   ` Kasatkin, Dmitry
2012-09-04 12:21     ` Kasatkin, Dmitry
2012-09-04 13:40       ` Mimi Zohar
2012-09-05  0:29     ` Rusty Russell
2012-09-05 13:34       ` Mimi Zohar
2012-09-06  2:05         ` Rusty Russell
2012-09-04 14:25   ` Lucas De Marchi
2012-09-04 15:04     ` Kasatkin, Dmitry
2012-09-05  0:19     ` Rusty Russell
2012-09-05 23:41       ` Lucas De Marchi
2012-09-06  7:55         ` Rusty Russell
2012-09-04 22:51   ` David Howells
2012-09-04 23:17     ` Kasatkin, Dmitry

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20120816013802.872.20099.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=dmitry.kasatkin@intel.com \
    --cc=jmorris@namei.org \
    --cc=keyrings@linux-nfs.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    --cc=zohar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

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

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