diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 18277efe37a146d480e113a2d7614275c21682e9..c76d7e54f1f5057382c7ce19122e0da316fc2998 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3454,6 +3454,13 @@ Note that if CONFIG_MODULE_SIG_FORCE is set, that is always true, so this option does nothing. + module.sig_enforce_subsys + [KNL] When CONFIG_MODULE_SIG is set, this means that + modules the user set without (valid) signatures will + fail to load. Note that CONFIG_MODULE_SIG_FORCE is set, + that is always true, so this option does nothing. + Now we support gpu, block and net. + module_blacklist= [KNL] Do not load a comma-separated list of modules. Useful for debugging problem modules. diff --git a/kernel/module/internal.h b/kernel/module/internal.h index c8b7b4dcf7820dcfea57c5ea5003ac2094285855..bf1b643ef970427a177d30d841d0bddb25bff940 100644 --- a/kernel/module/internal.h +++ b/kernel/module/internal.h @@ -82,6 +82,8 @@ struct load_info { struct { unsigned int sym, str, mod, vers, info, pcpu; } index; + + unsigned long subsys; }; enum mod_license { @@ -330,11 +332,20 @@ int module_enforce_rwx_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, #ifdef CONFIG_MODULE_SIG int module_sig_check(struct load_info *info, int flags); +int force_subsys_sig_check(struct load_info *info); +void set_module_subsys(struct load_info *info, const char *name); #else /* !CONFIG_MODULE_SIG */ static inline int module_sig_check(struct load_info *info, int flags) { return 0; } + +static inline int force_subsys_sig_check(struct load_info *info) +{ + return 0; +} + +static inline void set_module_subsys(struct load_info *info, const char *name) { } #endif /* !CONFIG_MODULE_SIG */ #ifdef CONFIG_DEBUG_KMEMLEAK diff --git a/kernel/module/main.c b/kernel/module/main.c index 34d9e718c2c7ddba6261117da436cc3c40a2b856..03ace37763eb68ec19a204c09fc22e8cfc787762 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -1364,7 +1364,7 @@ static bool ignore_undef_symbol(Elf_Half emachine, const char *name) } /* Change all symbols so that st_value encodes the pointer directly. */ -static int simplify_symbols(struct module *mod, const struct load_info *info) +static int simplify_symbols(struct module *mod, struct load_info *info) { Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Sym *sym = (void *)symsec->sh_addr; @@ -1406,6 +1406,7 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) ksym = resolve_symbol_wait(mod, info, name); /* Ok if resolved. */ if (ksym && !IS_ERR(ksym)) { + set_module_subsys(info, name); sym[i].st_value = kernel_symbol_value(ksym); break; } @@ -2921,6 +2922,10 @@ static int load_module(struct load_info *info, const char __user *uargs, if (err < 0) goto free_modinfo; + err = force_subsys_sig_check(info); + if (err < 0) + goto free_modinfo; + err = apply_relocations(mod, info); if (err < 0) goto free_modinfo; diff --git a/kernel/module/signing.c b/kernel/module/signing.c index a2ff4242e623d5d4e87d2f3d139d8620fb937579..67919f8b20377c788857309db05476110e00c0f5 100644 --- a/kernel/module/signing.c +++ b/kernel/module/signing.c @@ -22,6 +22,62 @@ static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE); module_param(sig_enforce, bool_enable_only, 0644); +static char *sig_enforce_subsys = ""; +module_param(sig_enforce_subsys, charp, 0644); +MODULE_PARM_DESC(sig_enforce_subsys, "Enforce subsys modules signature check"); + +enum modules_subsys { + MODULE_SUBSYS_GPU, + MODULE_SUBSYS_BLOCK, + MODULE_SUBSYS_NET, +}; + +void set_module_subsys(struct load_info *info, const char *name) +{ + char *key_intf_blk = "device_add_disk"; + char *key_intf_scsi = "scsi_host_alloc"; + char *key_intf_net = "register_netdev"; + char *key_intf_gpu = "drm_"; + + if (info->subsys) + return; + + if (!strncmp(name, key_intf_gpu, strlen(key_intf_gpu))) + set_bit(MODULE_SUBSYS_GPU, &info->subsys); + + if (!strncmp(name, key_intf_blk, strlen(key_intf_blk)) || + !strncmp(name, key_intf_scsi, strlen(key_intf_scsi))) + set_bit(MODULE_SUBSYS_BLOCK, &info->subsys); + + /* register_netdev or register_netdevice */ + if (!strncmp(name, key_intf_net, strlen(key_intf_net))) + set_bit(MODULE_SUBSYS_NET, &info->subsys); +} + +int force_subsys_sig_check(struct load_info *info) +{ + if (info->sig_ok) + return 0; + + if (test_bit(MODULE_SUBSYS_GPU, &info->subsys) && + parse_option_str(sig_enforce_subsys, "gpu")) + goto err; + + if (test_bit(MODULE_SUBSYS_BLOCK, &info->subsys) && + parse_option_str(sig_enforce_subsys, "block")) + goto err; + + if (test_bit(MODULE_SUBSYS_NET, &info->subsys) && + parse_option_str(sig_enforce_subsys, "net")) + goto err; + + return 0; +err: + pr_notice("%s: Loading is rejected, because of wrong signature or key missing!\n", + info->name); + return -EKEYREJECTED; +} + /* * Export sig_enforce kernel cmdline parameter to allow other subsystems rely * on that instead of directly to CONFIG_MODULE_SIG_FORCE config.