Skip to content

Commit

Permalink
module: signature checking hook
Browse files Browse the repository at this point in the history
We do a very simple search for a particular string appended to the module
(which is cache-hot and about to be SHA'd anyway).  There's both a config
option and a boot parameter which control whether we accept or fail with
unsigned modules and modules that are signed with an unknown key.

If module signing is enabled, the kernel will be tainted if a module is
loaded that is unsigned or has a signature for which we don't have the
key.

(Useful feedback and tweaks by David Howells <[email protected]>)

Signed-off-by: Rusty Russell <[email protected]>
Signed-off-by: David Howells <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Oct 10, 2012
1 parent c26fd69 commit 106a4ee
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Documentation/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
log everything. Information is printed at KERN_DEBUG
so loglevel=8 may also need to be specified.

module.sig_enforce
[KNL] When CONFIG_MODULE_SIG is set, this means that
modules without (valid) signatures will fail to load.
Note that if CONFIG_MODULE_SIG_ENFORCE is set, that
is always true, so this option does nothing.

mousedev.tap_time=
[MOUSE] Maximum time between finger touching and
leaving touchpad surface for touch to be considered
Expand Down
8 changes: 8 additions & 0 deletions include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include <linux/percpu.h>
#include <asm/module.h>

/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
#define MODULE_SIG_STRING "~Module signature appended~\n"

/* Not Yet Implemented */
#define MODULE_SUPPORTED_DEVICE(name)

Expand Down Expand Up @@ -260,6 +263,11 @@ struct module
const unsigned long *unused_gpl_crcs;
#endif

#ifdef CONFIG_MODULE_SIG
/* Signature was verified. */
bool sig_ok;
#endif

/* symbols that will be GPL-only in the near future. */
const struct kernel_symbol *gpl_future_syms;
const unsigned long *gpl_future_crcs;
Expand Down
14 changes: 14 additions & 0 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,20 @@ 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_SIG
bool "Module signature verification"
depends on MODULES
help
Check modules for valid signatures upon load: the signature
is simply appended to the module. For more information see
Documentation/module-signing.txt.

config MODULE_SIG_FORCE
bool "Require modules to be validly signed"
depends on MODULE_SIG
help
Reject unsigned modules or signed modules for which we don't have a
key. Without this, such modules will simply taint the kernel.
endif # MODULES

config INIT_ALL_POSSIBLE
Expand Down
1 change: 1 addition & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +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) += module_signing.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o
Expand Down
13 changes: 13 additions & 0 deletions kernel/module-internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* Module internals
*
* Copyright (C) 2012 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 Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/

extern int mod_verify_sig(const void *mod, unsigned long modlen,
const void *sig, unsigned long siglen);
93 changes: 92 additions & 1 deletion kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include <linux/jump_label.h>
#include <linux/pfn.h>
#include <linux/bsearch.h>
#include "module-internal.h"

#define CREATE_TRACE_POINTS
#include <trace/events/module.h>
Expand Down Expand Up @@ -102,6 +103,43 @@ static LIST_HEAD(modules);
struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */
#endif /* CONFIG_KGDB_KDB */

#ifdef CONFIG_MODULE_SIG
#ifdef CONFIG_MODULE_SIG_FORCE
static bool sig_enforce = true;
#else
static bool sig_enforce = false;

static int param_set_bool_enable_only(const char *val,
const struct kernel_param *kp)
{
int err;
bool test;
struct kernel_param dummy_kp = *kp;

dummy_kp.arg = &test;

err = param_set_bool(val, &dummy_kp);
if (err)
return err;

/* Don't let them unset it once it's set! */
if (!test && sig_enforce)
return -EROFS;

if (test)
sig_enforce = true;
return 0;
}

static const struct kernel_param_ops param_ops_bool_enable_only = {
.set = param_set_bool_enable_only,
.get = param_get_bool,
};
#define param_check_bool_enable_only param_check_bool

module_param(sig_enforce, bool_enable_only, 0644);
#endif /* !CONFIG_MODULE_SIG_FORCE */
#endif /* CONFIG_MODULE_SIG */

/* Block module loading/unloading? */
int modules_disabled = 0;
Expand Down Expand Up @@ -136,6 +174,7 @@ struct load_info {
unsigned long symoffs, stroffs;
struct _ddebug *debug;
unsigned int num_debug;
bool sig_ok;
struct {
unsigned int sym, str, mod, vers, info, pcpu;
} index;
Expand Down Expand Up @@ -2379,7 +2418,49 @@ static inline void kmemleak_load_module(const struct module *mod,
}
#endif

/* Sets info->hdr and info->len. */
#ifdef CONFIG_MODULE_SIG
static int module_sig_check(struct load_info *info,
const void *mod, unsigned long *len)
{
int err = -ENOKEY;
const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
const void *p = mod, *end = mod + *len;

/* Poor man's memmem. */
while ((p = memchr(p, MODULE_SIG_STRING[0], end - p))) {
if (p + markerlen > end)
break;

if (memcmp(p, MODULE_SIG_STRING, markerlen) == 0) {
const void *sig = p + markerlen;
/* Truncate module up to signature. */
*len = p - mod;
err = mod_verify_sig(mod, *len, sig, end - sig);
break;
}
p++;
}

if (!err) {
info->sig_ok = true;
return 0;
}

/* Not having a signature is only an error if we're strict. */
if (err == -ENOKEY && !sig_enforce)
err = 0;

return err;
}
#else /* !CONFIG_MODULE_SIG */
static int module_sig_check(struct load_info *info,
void *mod, unsigned long *len)
{
return 0;
}
#endif /* !CONFIG_MODULE_SIG */

/* Sets info->hdr, info->len and info->sig_ok. */
static int copy_and_check(struct load_info *info,
const void __user *umod, unsigned long len,
const char __user *uargs)
Expand All @@ -2399,6 +2480,10 @@ static int copy_and_check(struct load_info *info,
goto free_hdr;
}

err = module_sig_check(info, hdr, &len);
if (err)
goto free_hdr;

/* Sanity checks against insmoding binaries or wrong arch,
weird elf version */
if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0
Expand Down Expand Up @@ -2884,6 +2969,12 @@ static struct module *load_module(void __user *umod,
goto free_copy;
}

#ifdef CONFIG_MODULE_SIG
mod->sig_ok = info.sig_ok;
if (!mod->sig_ok)
add_taint_module(mod, TAINT_FORCED_MODULE);
#endif

/* Now module is in final location, initialize linked lists, etc. */
err = module_unload_init(mod);
if (err)
Expand Down
23 changes: 23 additions & 0 deletions kernel/module_signing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Module signature checker
*
* Copyright (C) 2012 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 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/err.h>
#include "module-internal.h"

/*
* Verify the signature on a module.
*/
int mod_verify_sig(const void *mod, unsigned long modlen,
const void *sig, unsigned long siglen)
{
return -ENOKEY;
}

0 comments on commit 106a4ee

Please sign in to comment.