|
@@ -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>
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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)
|