diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 114 | 
1 files changed, 100 insertions, 14 deletions
diff --git a/kernel/module.c b/kernel/module.c index 9ee93421269c..ff2d7359a418 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -7,6 +7,7 @@  #include <linux/export.h>  #include <linux/extable.h>  #include <linux/moduleloader.h> +#include <linux/module_signature.h>  #include <linux/trace_events.h>  #include <linux/init.h>  #include <linux/kallsyms.h> @@ -544,12 +545,20 @@ static const char *kernel_symbol_name(const struct kernel_symbol *sym)  #endif  } -static int cmp_name(const void *va, const void *vb) +static const char *kernel_symbol_namespace(const struct kernel_symbol *sym)  { -	const char *a; -	const struct kernel_symbol *b; -	a = va; b = vb; -	return strcmp(a, kernel_symbol_name(b)); +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +	if (!sym->namespace_offset) +		return NULL; +	return offset_to_ptr(&sym->namespace_offset); +#else +	return sym->namespace; +#endif +} + +static int cmp_name(const void *name, const void *sym) +{ +	return strcmp(name, kernel_symbol_name(sym));  }  static bool find_exported_symbol_in_section(const struct symsearch *syms, @@ -1379,6 +1388,41 @@ static inline int same_magic(const char *amagic, const char *bmagic,  }  #endif /* CONFIG_MODVERSIONS */ +static char *get_modinfo(const struct load_info *info, const char *tag); +static char *get_next_modinfo(const struct load_info *info, const char *tag, +			      char *prev); + +static int verify_namespace_is_imported(const struct load_info *info, +					const struct kernel_symbol *sym, +					struct module *mod) +{ +	const char *namespace; +	char *imported_namespace; + +	namespace = kernel_symbol_namespace(sym); +	if (namespace) { +		imported_namespace = get_modinfo(info, "import_ns"); +		while (imported_namespace) { +			if (strcmp(namespace, imported_namespace) == 0) +				return 0; +			imported_namespace = get_next_modinfo( +				info, "import_ns", imported_namespace); +		} +#ifdef CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS +		pr_warn( +#else +		pr_err( +#endif +			"%s: module uses symbol (%s) from namespace %s, but does not import it.\n", +			mod->name, kernel_symbol_name(sym), namespace); +#ifndef CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS +		return -EINVAL; +#endif +	} +	return 0; +} + +  /* Resolve a symbol for this module.  I.e. if we find one, record usage. */  static const struct kernel_symbol *resolve_symbol(struct module *mod,  						  const struct load_info *info, @@ -1407,6 +1451,12 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,  		goto getname;  	} +	err = verify_namespace_is_imported(info, sym, mod); +	if (err) { +		sym = ERR_PTR(err); +		goto getname; +	} +  	err = ref_module(mod, owner);  	if (err) {  		sym = ERR_PTR(err); @@ -2481,7 +2531,8 @@ static char *next_string(char *string, unsigned long *secsize)  	return string;  } -static char *get_modinfo(struct load_info *info, const char *tag) +static char *get_next_modinfo(const struct load_info *info, const char *tag, +			      char *prev)  {  	char *p;  	unsigned int taglen = strlen(tag); @@ -2492,13 +2543,25 @@ static char *get_modinfo(struct load_info *info, const char *tag)  	 * get_modinfo() calls made before rewrite_section_headers()  	 * must use sh_offset, as sh_addr isn't set!  	 */ -	for (p = (char *)info->hdr + infosec->sh_offset; p; p = next_string(p, &size)) { +	char *modinfo = (char *)info->hdr + infosec->sh_offset; + +	if (prev) { +		size -= prev - modinfo; +		modinfo = next_string(prev, &size); +	} + +	for (p = modinfo; p; p = next_string(p, &size)) {  		if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')  			return p + taglen + 1;  	}  	return NULL;  } +static char *get_modinfo(const struct load_info *info, const char *tag) +{ +	return get_next_modinfo(info, tag, NULL); +} +  static void setup_modinfo(struct module *mod, struct load_info *info)  {  	struct module_attribute *attr; @@ -2776,8 +2839,9 @@ static inline void kmemleak_load_module(const struct module *mod,  #ifdef CONFIG_MODULE_SIG  static int module_sig_check(struct load_info *info, int flags)  { -	int err = -ENOKEY; +	int err = -ENODATA;  	const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; +	const char *reason;  	const void *mod = info->hdr;  	/* @@ -2792,16 +2856,38 @@ static int module_sig_check(struct load_info *info, int flags)  		err = mod_verify_sig(mod, info);  	} -	if (!err) { +	switch (err) { +	case 0:  		info->sig_ok = true;  		return 0; -	} -	/* Not having a signature is only an error if we're strict. */ -	if (err == -ENOKEY && !is_module_sig_enforced()) -		err = 0; +		/* We don't permit modules to be loaded into trusted kernels +		 * without a valid signature on them, but if we're not +		 * enforcing, certain errors are non-fatal. +		 */ +	case -ENODATA: +		reason = "Loading of unsigned module"; +		goto decide; +	case -ENOPKG: +		reason = "Loading of module with unsupported crypto"; +		goto decide; +	case -ENOKEY: +		reason = "Loading of module with unavailable key"; +	decide: +		if (is_module_sig_enforced()) { +			pr_notice("%s is rejected\n", reason); +			return -EKEYREJECTED; +		} -	return err; +		return security_locked_down(LOCKDOWN_MODULE_SIGNATURE); + +		/* All other errors are fatal, including nomem, unparseable +		 * signatures and signature check failures - even if signatures +		 * aren't required. +		 */ +	default: +		return err; +	}  }  #else /* !CONFIG_MODULE_SIG */  static int module_sig_check(struct load_info *info, int flags)  |